diff options
Diffstat (limited to 'drivers/net/ethernet')
661 files changed, 33731 insertions, 9432 deletions
diff --git a/drivers/net/ethernet/3com/3c509.c b/drivers/net/ethernet/3com/3c509.c index 846fa3af4504..fb68339e1511 100644 --- a/drivers/net/ethernet/3com/3c509.c +++ b/drivers/net/ethernet/3com/3c509.c @@ -1135,7 +1135,7 @@ el3_netdev_set_ecmd(struct net_device *dev, static void el3_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { - strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); + strscpy(info->driver, DRV_NAME, sizeof(info->driver)); } static int el3_get_link_ksettings(struct net_device *dev, diff --git a/drivers/net/ethernet/3com/3c515.c b/drivers/net/ethernet/3com/3c515.c index 1d124b0f65e7..d2f4358cc550 100644 --- a/drivers/net/ethernet/3com/3c515.c +++ b/drivers/net/ethernet/3com/3c515.c @@ -1527,7 +1527,7 @@ static void set_rx_mode(struct net_device *dev) static void netdev_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { - strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); + strscpy(info->driver, DRV_NAME, sizeof(info->driver)); snprintf(info->bus_info, sizeof(info->bus_info), "ISA 0x%lx", dev->base_addr); } diff --git a/drivers/net/ethernet/3com/3c589_cs.c b/drivers/net/ethernet/3com/3c589_cs.c index 4673bc1604e7..82f94b1635bf 100644 --- a/drivers/net/ethernet/3com/3c589_cs.c +++ b/drivers/net/ethernet/3com/3c589_cs.c @@ -480,7 +480,7 @@ static void tc589_reset(struct net_device *dev) static void netdev_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { - strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); + strscpy(info->driver, DRV_NAME, sizeof(info->driver)); snprintf(info->bus_info, sizeof(info->bus_info), "PCMCIA 0x%lx", dev->base_addr); } diff --git a/drivers/net/ethernet/3com/3c59x.c b/drivers/net/ethernet/3com/3c59x.c index ccf07667aa5e..082388bb6169 100644 --- a/drivers/net/ethernet/3com/3c59x.c +++ b/drivers/net/ethernet/3com/3c59x.c @@ -2959,13 +2959,13 @@ static void vortex_get_drvinfo(struct net_device *dev, { struct vortex_private *vp = netdev_priv(dev); - strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); + strscpy(info->driver, DRV_NAME, sizeof(info->driver)); if (VORTEX_PCI(vp)) { - strlcpy(info->bus_info, pci_name(VORTEX_PCI(vp)), + strscpy(info->bus_info, pci_name(VORTEX_PCI(vp)), sizeof(info->bus_info)); } else { if (VORTEX_EISA(vp)) - strlcpy(info->bus_info, dev_name(vp->gendev), + strscpy(info->bus_info, dev_name(vp->gendev), sizeof(info->bus_info)); else snprintf(info->bus_info, sizeof(info->bus_info), diff --git a/drivers/net/ethernet/3com/typhoon.c b/drivers/net/ethernet/3com/typhoon.c index cad4f354cc76..aaaff3ba43ef 100644 --- a/drivers/net/ethernet/3com/typhoon.c +++ b/drivers/net/ethernet/3com/typhoon.c @@ -969,12 +969,12 @@ typhoon_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) smp_rmb(); if (tp->card_state == Sleeping) { - strlcpy(info->fw_version, "Sleep image", + strscpy(info->fw_version, "Sleep image", sizeof(info->fw_version)); } else { INIT_COMMAND_WITH_RESPONSE(&xp_cmd, TYPHOON_CMD_READ_VERSIONS); if (typhoon_issue_command(tp, 1, &xp_cmd, 3, xp_resp) < 0) { - strlcpy(info->fw_version, "Unknown runtime", + strscpy(info->fw_version, "Unknown runtime", sizeof(info->fw_version)); } else { u32 sleep_ver = le32_to_cpu(xp_resp[0].parm2); @@ -984,8 +984,8 @@ typhoon_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) } } - strlcpy(info->driver, KBUILD_MODNAME, sizeof(info->driver)); - strlcpy(info->bus_info, pci_name(pci_dev), sizeof(info->bus_info)); + strscpy(info->driver, KBUILD_MODNAME, sizeof(info->driver)); + strscpy(info->bus_info, pci_name(pci_dev), sizeof(info->bus_info)); } static int diff --git a/drivers/net/ethernet/8390/ax88796.c b/drivers/net/ethernet/8390/ax88796.c index 1f8acbba5b6b..af603256b724 100644 --- a/drivers/net/ethernet/8390/ax88796.c +++ b/drivers/net/ethernet/8390/ax88796.c @@ -579,9 +579,9 @@ static void ax_get_drvinfo(struct net_device *dev, { struct platform_device *pdev = to_platform_device(dev->dev.parent); - strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); - strlcpy(info->version, DRV_VERSION, sizeof(info->version)); - strlcpy(info->bus_info, pdev->name, sizeof(info->bus_info)); + strscpy(info->driver, DRV_NAME, sizeof(info->driver)); + strscpy(info->version, DRV_VERSION, sizeof(info->version)); + strscpy(info->bus_info, pdev->name, sizeof(info->bus_info)); } static u32 ax_get_msglevel(struct net_device *dev) diff --git a/drivers/net/ethernet/8390/etherh.c b/drivers/net/ethernet/8390/etherh.c index e7b879123bb1..05d39ecb97ff 100644 --- a/drivers/net/ethernet/8390/etherh.c +++ b/drivers/net/ethernet/8390/etherh.c @@ -555,9 +555,9 @@ static int __init etherm_addr(char *addr) static void etherh_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { - strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); - strlcpy(info->version, DRV_VERSION, sizeof(info->version)); - strlcpy(info->bus_info, dev_name(dev->dev.parent), + strscpy(info->driver, DRV_NAME, sizeof(info->driver)); + strscpy(info->version, DRV_VERSION, sizeof(info->version)); + strscpy(info->bus_info, dev_name(dev->dev.parent), sizeof(info->bus_info)); } diff --git a/drivers/net/ethernet/8390/mcf8390.c b/drivers/net/ethernet/8390/mcf8390.c index 21047ae1bc3d..8a7918d33419 100644 --- a/drivers/net/ethernet/8390/mcf8390.c +++ b/drivers/net/ethernet/8390/mcf8390.c @@ -450,8 +450,7 @@ static int mcf8390_remove(struct platform_device *pdev) unregister_netdev(dev); mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (mem) - release_mem_region(mem->start, resource_size(mem)); + release_mem_region(mem->start, resource_size(mem)); free_netdev(dev); return 0; } diff --git a/drivers/net/ethernet/Kconfig b/drivers/net/ethernet/Kconfig index 9a55c1d5a0a1..1917da784191 100644 --- a/drivers/net/ethernet/Kconfig +++ b/drivers/net/ethernet/Kconfig @@ -121,6 +121,7 @@ config LANTIQ_XRX200 Support for the PMAC of the Gigabit switch (GSWIP) inside the Lantiq / Intel VRX200 VDSL SoC +source "drivers/net/ethernet/adi/Kconfig" source "drivers/net/ethernet/litex/Kconfig" source "drivers/net/ethernet/marvell/Kconfig" source "drivers/net/ethernet/mediatek/Kconfig" diff --git a/drivers/net/ethernet/Makefile b/drivers/net/ethernet/Makefile index c06e75ed4231..0d872d4efcd1 100644 --- a/drivers/net/ethernet/Makefile +++ b/drivers/net/ethernet/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_NET_VENDOR_8390) += 8390/ obj-$(CONFIG_NET_VENDOR_ACTIONS) += actions/ obj-$(CONFIG_NET_VENDOR_ADAPTEC) += adaptec/ obj-$(CONFIG_GRETH) += aeroflex/ +obj-$(CONFIG_NET_VENDOR_ADI) += adi/ obj-$(CONFIG_NET_VENDOR_AGERE) += agere/ obj-$(CONFIG_NET_VENDOR_ALACRITECH) += alacritech/ obj-$(CONFIG_NET_VENDOR_ALLWINNER) += allwinner/ diff --git a/drivers/net/ethernet/actions/owl-emac.c b/drivers/net/ethernet/actions/owl-emac.c index 1cfdd01b4c2e..cd4d71b83c33 100644 --- a/drivers/net/ethernet/actions/owl-emac.c +++ b/drivers/net/ethernet/actions/owl-emac.c @@ -1576,7 +1576,7 @@ static int owl_emac_probe(struct platform_device *pdev) netdev->watchdog_timeo = OWL_EMAC_TX_TIMEOUT; netdev->netdev_ops = &owl_emac_netdev_ops; netdev->ethtool_ops = &owl_emac_ethtool_ops; - netif_napi_add(netdev, &priv->napi, owl_emac_poll, NAPI_POLL_WEIGHT); + netif_napi_add(netdev, &priv->napi, owl_emac_poll); ret = devm_register_netdev(dev, netdev); if (ret) { diff --git a/drivers/net/ethernet/adaptec/starfire.c b/drivers/net/ethernet/adaptec/starfire.c index 8f0a6b9c518e..857361c74f5d 100644 --- a/drivers/net/ethernet/adaptec/starfire.c +++ b/drivers/net/ethernet/adaptec/starfire.c @@ -1844,8 +1844,8 @@ static int check_if_running(struct net_device *dev) static void get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { struct netdev_private *np = netdev_priv(dev); - strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); - strlcpy(info->bus_info, pci_name(np->pci_dev), sizeof(info->bus_info)); + strscpy(info->driver, DRV_NAME, sizeof(info->driver)); + strscpy(info->bus_info, pci_name(np->pci_dev), sizeof(info->bus_info)); } static int get_link_ksettings(struct net_device *dev, diff --git a/drivers/net/ethernet/adi/Kconfig b/drivers/net/ethernet/adi/Kconfig new file mode 100644 index 000000000000..da3bdd302502 --- /dev/null +++ b/drivers/net/ethernet/adi/Kconfig @@ -0,0 +1,28 @@ +# SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause +# +# Analog Devices device configuration +# + +config NET_VENDOR_ADI + bool "Analog Devices devices" + default y + depends on SPI + help + If you have a network (Ethernet) card belonging to this class, say Y. + + Note that the answer to this question doesn't directly affect the + kernel: saying N will just cause the configurator to skip all + the questions about ADI devices. If you say Y, you will be asked + for your specific card in the following questions. + +if NET_VENDOR_ADI + +config ADIN1110 + tristate "Analog Devices ADIN1110 MAC-PHY" + depends on SPI && NET_SWITCHDEV + select CRC8 + help + Say yes here to build support for Analog Devices ADIN1110 + Low Power 10BASE-T1L Ethernet MAC-PHY. + +endif # NET_VENDOR_ADI diff --git a/drivers/net/ethernet/adi/Makefile b/drivers/net/ethernet/adi/Makefile new file mode 100644 index 000000000000..d0383d94303c --- /dev/null +++ b/drivers/net/ethernet/adi/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause +# +# Makefile for the Analog Devices network device drivers. +# + +obj-$(CONFIG_ADIN1110) += adin1110.o diff --git a/drivers/net/ethernet/adi/adin1110.c b/drivers/net/ethernet/adi/adin1110.c new file mode 100644 index 000000000000..aaee7c4248e6 --- /dev/null +++ b/drivers/net/ethernet/adi/adin1110.c @@ -0,0 +1,1697 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause +/* ADIN1110 Low Power 10BASE-T1L Ethernet MAC-PHY + * ADIN2111 2-Port Ethernet Switch with Integrated 10BASE-T1L PHY + * + * Copyright 2021 Analog Devices Inc. + */ + +#include <linux/bitfield.h> +#include <linux/bits.h> +#include <linux/cache.h> +#include <linux/crc8.h> +#include <linux/etherdevice.h> +#include <linux/ethtool.h> +#include <linux/if_bridge.h> +#include <linux/interrupt.h> +#include <linux/iopoll.h> +#include <linux/gpio.h> +#include <linux/kernel.h> +#include <linux/mii.h> +#include <linux/module.h> +#include <linux/netdevice.h> +#include <linux/regulator/consumer.h> +#include <linux/phy.h> +#include <linux/property.h> +#include <linux/spi/spi.h> + +#include <net/switchdev.h> + +#include <asm/unaligned.h> + +#define ADIN1110_PHY_ID 0x1 + +#define ADIN1110_RESET 0x03 +#define ADIN1110_SWRESET BIT(0) + +#define ADIN1110_CONFIG1 0x04 +#define ADIN1110_CONFIG1_SYNC BIT(15) + +#define ADIN1110_CONFIG2 0x06 +#define ADIN2111_P2_FWD_UNK2HOST BIT(12) +#define ADIN2111_PORT_CUT_THRU_EN BIT(11) +#define ADIN1110_CRC_APPEND BIT(5) +#define ADIN1110_FWD_UNK2HOST BIT(2) + +#define ADIN1110_STATUS0 0x08 + +#define ADIN1110_STATUS1 0x09 +#define ADIN2111_P2_RX_RDY BIT(17) +#define ADIN1110_SPI_ERR BIT(10) +#define ADIN1110_RX_RDY BIT(4) + +#define ADIN1110_IMASK1 0x0D +#define ADIN2111_RX_RDY_IRQ BIT(17) +#define ADIN1110_SPI_ERR_IRQ BIT(10) +#define ADIN1110_RX_RDY_IRQ BIT(4) +#define ADIN1110_TX_RDY_IRQ BIT(3) + +#define ADIN1110_MDIOACC 0x20 +#define ADIN1110_MDIO_TRDONE BIT(31) +#define ADIN1110_MDIO_ST GENMASK(29, 28) +#define ADIN1110_MDIO_OP GENMASK(27, 26) +#define ADIN1110_MDIO_PRTAD GENMASK(25, 21) +#define ADIN1110_MDIO_DEVAD GENMASK(20, 16) +#define ADIN1110_MDIO_DATA GENMASK(15, 0) + +#define ADIN1110_TX_FSIZE 0x30 +#define ADIN1110_TX 0x31 +#define ADIN1110_TX_SPACE 0x32 + +#define ADIN1110_MAC_ADDR_FILTER_UPR 0x50 +#define ADIN2111_MAC_ADDR_APPLY2PORT2 BIT(31) +#define ADIN1110_MAC_ADDR_APPLY2PORT BIT(30) +#define ADIN2111_MAC_ADDR_TO_OTHER_PORT BIT(17) +#define ADIN1110_MAC_ADDR_TO_HOST BIT(16) + +#define ADIN1110_MAC_ADDR_FILTER_LWR 0x51 + +#define ADIN1110_MAC_ADDR_MASK_UPR 0x70 +#define ADIN1110_MAC_ADDR_MASK_LWR 0x71 + +#define ADIN1110_RX_FSIZE 0x90 +#define ADIN1110_RX 0x91 + +#define ADIN2111_RX_P2_FSIZE 0xC0 +#define ADIN2111_RX_P2 0xC1 + +#define ADIN1110_CLEAR_STATUS0 0xFFF + +/* MDIO_OP codes */ +#define ADIN1110_MDIO_OP_WR 0x1 +#define ADIN1110_MDIO_OP_RD 0x3 + +#define ADIN1110_CD BIT(7) +#define ADIN1110_WRITE BIT(5) + +#define ADIN1110_MAX_BUFF 2048 +#define ADIN1110_MAX_FRAMES_READ 64 +#define ADIN1110_WR_HEADER_LEN 2 +#define ADIN1110_FRAME_HEADER_LEN 2 +#define ADIN1110_INTERNAL_SIZE_HEADER_LEN 2 +#define ADIN1110_RD_HEADER_LEN 3 +#define ADIN1110_REG_LEN 4 +#define ADIN1110_FEC_LEN 4 + +#define ADIN1110_PHY_ID_VAL 0x0283BC91 +#define ADIN2111_PHY_ID_VAL 0x0283BCA1 + +#define ADIN_MAC_MAX_PORTS 2 +#define ADIN_MAC_MAX_ADDR_SLOTS 16 + +#define ADIN_MAC_MULTICAST_ADDR_SLOT 0 +#define ADIN_MAC_BROADCAST_ADDR_SLOT 1 +#define ADIN_MAC_P1_ADDR_SLOT 2 +#define ADIN_MAC_P2_ADDR_SLOT 3 +#define ADIN_MAC_FDB_ADDR_SLOT 4 + +DECLARE_CRC8_TABLE(adin1110_crc_table); + +enum adin1110_chips_id { + ADIN1110_MAC = 0, + ADIN2111_MAC, +}; + +struct adin1110_cfg { + enum adin1110_chips_id id; + char name[MDIO_NAME_SIZE]; + u32 phy_ids[PHY_MAX_ADDR]; + u32 ports_nr; + u32 phy_id_val; +}; + +struct adin1110_port_priv { + struct adin1110_priv *priv; + struct net_device *netdev; + struct net_device *bridge; + struct phy_device *phydev; + struct work_struct tx_work; + u64 rx_packets; + u64 tx_packets; + u64 rx_bytes; + u64 tx_bytes; + struct work_struct rx_mode_work; + u32 flags; + struct sk_buff_head txq; + u32 nr; + u32 state; + struct adin1110_cfg *cfg; +}; + +struct adin1110_priv { + struct mutex lock; /* protect spi */ + spinlock_t state_lock; /* protect RX mode */ + struct mii_bus *mii_bus; + struct spi_device *spidev; + bool append_crc; + struct adin1110_cfg *cfg; + u32 tx_space; + u32 irq_mask; + bool forwarding; + int irq; + struct adin1110_port_priv *ports[ADIN_MAC_MAX_PORTS]; + char mii_bus_name[MII_BUS_ID_SIZE]; + u8 data[ADIN1110_MAX_BUFF] ____cacheline_aligned; +}; + +struct adin1110_switchdev_event_work { + struct work_struct work; + struct switchdev_notifier_fdb_info fdb_info; + struct adin1110_port_priv *port_priv; + unsigned long event; +}; + +static struct adin1110_cfg adin1110_cfgs[] = { + { + .id = ADIN1110_MAC, + .name = "adin1110", + .phy_ids = {1}, + .ports_nr = 1, + .phy_id_val = ADIN1110_PHY_ID_VAL, + }, + { + .id = ADIN2111_MAC, + .name = "adin2111", + .phy_ids = {1, 2}, + .ports_nr = 2, + .phy_id_val = ADIN2111_PHY_ID_VAL, + }, +}; + +static u8 adin1110_crc_data(u8 *data, u32 len) +{ + return crc8(adin1110_crc_table, data, len, 0); +} + +static int adin1110_read_reg(struct adin1110_priv *priv, u16 reg, u32 *val) +{ + u32 header_len = ADIN1110_RD_HEADER_LEN; + u32 read_len = ADIN1110_REG_LEN; + struct spi_transfer t[2] = {0}; + int ret; + + priv->data[0] = ADIN1110_CD | FIELD_GET(GENMASK(12, 8), reg); + priv->data[1] = FIELD_GET(GENMASK(7, 0), reg); + priv->data[2] = 0x00; + + if (priv->append_crc) { + priv->data[2] = adin1110_crc_data(&priv->data[0], 2); + priv->data[3] = 0x00; + header_len++; + } + + t[0].tx_buf = &priv->data[0]; + t[0].len = header_len; + + if (priv->append_crc) + read_len++; + + memset(&priv->data[header_len], 0, read_len); + t[1].rx_buf = &priv->data[header_len]; + t[1].len = read_len; + + ret = spi_sync_transfer(priv->spidev, t, 2); + if (ret) + return ret; + + if (priv->append_crc) { + u8 recv_crc; + u8 crc; + + crc = adin1110_crc_data(&priv->data[header_len], + ADIN1110_REG_LEN); + recv_crc = priv->data[header_len + ADIN1110_REG_LEN]; + + if (crc != recv_crc) { + dev_err_ratelimited(&priv->spidev->dev, "CRC error."); + return -EBADMSG; + } + } + + *val = get_unaligned_be32(&priv->data[header_len]); + + return ret; +} + +static int adin1110_write_reg(struct adin1110_priv *priv, u16 reg, u32 val) +{ + u32 header_len = ADIN1110_WR_HEADER_LEN; + u32 write_len = ADIN1110_REG_LEN; + + priv->data[0] = ADIN1110_CD | ADIN1110_WRITE | FIELD_GET(GENMASK(12, 8), reg); + priv->data[1] = FIELD_GET(GENMASK(7, 0), reg); + + if (priv->append_crc) { + priv->data[2] = adin1110_crc_data(&priv->data[0], header_len); + header_len++; + } + + put_unaligned_be32(val, &priv->data[header_len]); + if (priv->append_crc) { + priv->data[header_len + write_len] = adin1110_crc_data(&priv->data[header_len], + write_len); + write_len++; + } + + return spi_write(priv->spidev, &priv->data[0], header_len + write_len); +} + +static int adin1110_set_bits(struct adin1110_priv *priv, u16 reg, + unsigned long mask, unsigned long val) +{ + u32 write_val; + int ret; + + ret = adin1110_read_reg(priv, reg, &write_val); + if (ret < 0) + return ret; + + set_mask_bits(&write_val, mask, val); + + return adin1110_write_reg(priv, reg, write_val); +} + +static int adin1110_round_len(int len) +{ + /* can read/write only mutiples of 4 bytes of payload */ + len = ALIGN(len, 4); + + /* NOTE: ADIN1110_WR_HEADER_LEN should be used for write ops. */ + if (len + ADIN1110_RD_HEADER_LEN > ADIN1110_MAX_BUFF) + return -EINVAL; + + return len; +} + +static int adin1110_read_fifo(struct adin1110_port_priv *port_priv) +{ + struct adin1110_priv *priv = port_priv->priv; + u32 header_len = ADIN1110_RD_HEADER_LEN; + struct spi_transfer t[2] = {0}; + u32 frame_size_no_fcs; + struct sk_buff *rxb; + u32 frame_size; + int round_len; + u16 reg; + int ret; + + if (!port_priv->nr) { + reg = ADIN1110_RX; + ret = adin1110_read_reg(priv, ADIN1110_RX_FSIZE, &frame_size); + } else { + reg = ADIN2111_RX_P2; + ret = adin1110_read_reg(priv, ADIN2111_RX_P2_FSIZE, + &frame_size); + } + + if (ret < 0) + return ret; + + /* The read frame size includes the extra 2 bytes + * from the ADIN1110 frame header. + */ + if (frame_size < ADIN1110_FRAME_HEADER_LEN + ADIN1110_FEC_LEN) + return ret; + + round_len = adin1110_round_len(frame_size); + if (round_len < 0) + return ret; + + frame_size_no_fcs = frame_size - ADIN1110_FRAME_HEADER_LEN - ADIN1110_FEC_LEN; + + rxb = netdev_alloc_skb(port_priv->netdev, round_len); + if (!rxb) + return -ENOMEM; + + memset(priv->data, 0, round_len + ADIN1110_RD_HEADER_LEN); + + priv->data[0] = ADIN1110_CD | FIELD_GET(GENMASK(12, 8), reg); + priv->data[1] = FIELD_GET(GENMASK(7, 0), reg); + + if (priv->append_crc) { + priv->data[2] = adin1110_crc_data(&priv->data[0], 2); + header_len++; + } + + skb_put(rxb, frame_size_no_fcs + ADIN1110_FRAME_HEADER_LEN); + + t[0].tx_buf = &priv->data[0]; + t[0].len = header_len; + + t[1].rx_buf = &rxb->data[0]; + t[1].len = round_len; + + ret = spi_sync_transfer(priv->spidev, t, 2); + if (ret) { + kfree_skb(rxb); + return ret; + } + + skb_pull(rxb, ADIN1110_FRAME_HEADER_LEN); + rxb->protocol = eth_type_trans(rxb, port_priv->netdev); + + if ((port_priv->flags & IFF_ALLMULTI && rxb->pkt_type == PACKET_MULTICAST) || + (port_priv->flags & IFF_BROADCAST && rxb->pkt_type == PACKET_BROADCAST)) + rxb->offload_fwd_mark = 1; + + netif_rx(rxb); + + port_priv->rx_bytes += frame_size - ADIN1110_FRAME_HEADER_LEN; + port_priv->rx_packets++; + + return 0; +} + +static int adin1110_write_fifo(struct adin1110_port_priv *port_priv, + struct sk_buff *txb) +{ + struct adin1110_priv *priv = port_priv->priv; + u32 header_len = ADIN1110_WR_HEADER_LEN; + __be16 frame_header; + int padding = 0; + int padded_len; + int round_len; + int ret; + + /* Pad frame to 64 byte length, + * MAC nor PHY will otherwise add the + * required padding. + * The FEC will be added by the MAC internally. + */ + if (txb->len + ADIN1110_FEC_LEN < 64) + padding = 64 - (txb->len + ADIN1110_FEC_LEN); + + padded_len = txb->len + padding + ADIN1110_FRAME_HEADER_LEN; + + round_len = adin1110_round_len(padded_len); + if (round_len < 0) + return round_len; + + ret = adin1110_write_reg(priv, ADIN1110_TX_FSIZE, padded_len); + if (ret < 0) + return ret; + + memset(priv->data, 0, round_len + ADIN1110_WR_HEADER_LEN); + + priv->data[0] = ADIN1110_CD | ADIN1110_WRITE; + priv->data[0] |= FIELD_GET(GENMASK(12, 8), ADIN1110_TX); + priv->data[1] = FIELD_GET(GENMASK(7, 0), ADIN1110_TX); + if (priv->append_crc) { + priv->data[2] = adin1110_crc_data(&priv->data[0], 2); + header_len++; + } + + /* mention the port on which to send the frame in the frame header */ + frame_header = cpu_to_be16(port_priv->nr); + memcpy(&priv->data[header_len], &frame_header, + ADIN1110_FRAME_HEADER_LEN); + + memcpy(&priv->data[header_len + ADIN1110_FRAME_HEADER_LEN], + txb->data, txb->len); + + ret = spi_write(priv->spidev, &priv->data[0], round_len + header_len); + if (ret < 0) + return ret; + + port_priv->tx_bytes += txb->len; + port_priv->tx_packets++; + + return 0; +} + +static int adin1110_read_mdio_acc(struct adin1110_priv *priv) +{ + u32 val; + int ret; + + mutex_lock(&priv->lock); + ret = adin1110_read_reg(priv, ADIN1110_MDIOACC, &val); + mutex_unlock(&priv->lock); + if (ret < 0) + return 0; + + return val; +} + +static int adin1110_mdio_read(struct mii_bus *bus, int phy_id, int reg) +{ + struct adin1110_priv *priv = bus->priv; + u32 val = 0; + int ret; + + if (mdio_phy_id_is_c45(phy_id)) + return -EOPNOTSUPP; + + val |= FIELD_PREP(ADIN1110_MDIO_OP, ADIN1110_MDIO_OP_RD); + val |= FIELD_PREP(ADIN1110_MDIO_ST, 0x1); + val |= FIELD_PREP(ADIN1110_MDIO_PRTAD, phy_id); + val |= FIELD_PREP(ADIN1110_MDIO_DEVAD, reg); + + /* write the clause 22 read command to the chip */ + mutex_lock(&priv->lock); + ret = adin1110_write_reg(priv, ADIN1110_MDIOACC, val); + mutex_unlock(&priv->lock); + if (ret < 0) + return ret; + + /* ADIN1110_MDIO_TRDONE BIT of the ADIN1110_MDIOACC + * register is set when the read is done. + * After the transaction is done, ADIN1110_MDIO_DATA + * bitfield of ADIN1110_MDIOACC register will contain + * the requested register value. + */ + ret = readx_poll_timeout(adin1110_read_mdio_acc, priv, val, + (val & ADIN1110_MDIO_TRDONE), 10000, 30000); + if (ret < 0) + return ret; + + return (val & ADIN1110_MDIO_DATA); +} + +static int adin1110_mdio_write(struct mii_bus *bus, int phy_id, + int reg, u16 reg_val) +{ + struct adin1110_priv *priv = bus->priv; + u32 val = 0; + int ret; + + if (mdio_phy_id_is_c45(phy_id)) + return -EOPNOTSUPP; + + val |= FIELD_PREP(ADIN1110_MDIO_OP, ADIN1110_MDIO_OP_WR); + val |= FIELD_PREP(ADIN1110_MDIO_ST, 0x1); + val |= FIELD_PREP(ADIN1110_MDIO_PRTAD, phy_id); + val |= FIELD_PREP(ADIN1110_MDIO_DEVAD, reg); + val |= FIELD_PREP(ADIN1110_MDIO_DATA, reg_val); + + /* write the clause 22 write command to the chip */ + mutex_lock(&priv->lock); + ret = adin1110_write_reg(priv, ADIN1110_MDIOACC, val); + mutex_unlock(&priv->lock); + if (ret < 0) + return ret; + + return readx_poll_timeout(adin1110_read_mdio_acc, priv, val, + (val & ADIN1110_MDIO_TRDONE), 10000, 30000); +} + +/* ADIN1110 MAC-PHY contains an ADIN1100 PHY. + * ADIN2111 MAC-PHY contains two ADIN1100 PHYs. + * By registering a new MDIO bus we allow the PAL to discover + * the encapsulated PHY and probe the ADIN1100 driver. + */ +static int adin1110_register_mdiobus(struct adin1110_priv *priv, + struct device *dev) +{ + struct mii_bus *mii_bus; + int ret; + + mii_bus = devm_mdiobus_alloc(dev); + if (!mii_bus) + return -ENOMEM; + + snprintf(priv->mii_bus_name, MII_BUS_ID_SIZE, "%s-%u", + priv->cfg->name, priv->spidev->chip_select); + + mii_bus->name = priv->mii_bus_name; + mii_bus->read = adin1110_mdio_read; + mii_bus->write = adin1110_mdio_write; + mii_bus->priv = priv; + mii_bus->parent = dev; + mii_bus->phy_mask = ~((u32)GENMASK(2, 0)); + mii_bus->probe_capabilities = MDIOBUS_C22; + snprintf(mii_bus->id, MII_BUS_ID_SIZE, "%s", dev_name(dev)); + + ret = devm_mdiobus_register(dev, mii_bus); + if (ret) + return ret; + + priv->mii_bus = mii_bus; + + return 0; +} + +static bool adin1110_port_rx_ready(struct adin1110_port_priv *port_priv, + u32 status) +{ + if (!netif_oper_up(port_priv->netdev)) + return false; + + if (!port_priv->nr) + return !!(status & ADIN1110_RX_RDY); + else + return !!(status & ADIN2111_P2_RX_RDY); +} + +static void adin1110_read_frames(struct adin1110_port_priv *port_priv, + unsigned int budget) +{ + struct adin1110_priv *priv = port_priv->priv; + u32 status1; + int ret; + + while (budget) { + ret = adin1110_read_reg(priv, ADIN1110_STATUS1, &status1); + if (ret < 0) + return; + + if (!adin1110_port_rx_ready(port_priv, status1)) + break; + + ret = adin1110_read_fifo(port_priv); + if (ret < 0) + return; + + budget--; + } +} + +static void adin1110_wake_queues(struct adin1110_priv *priv) +{ + int i; + + for (i = 0; i < priv->cfg->ports_nr; i++) + netif_wake_queue(priv->ports[i]->netdev); +} + +static irqreturn_t adin1110_irq(int irq, void *p) +{ + struct adin1110_priv *priv = p; + u32 status1; + u32 val; + int ret; + int i; + + mutex_lock(&priv->lock); + + ret = adin1110_read_reg(priv, ADIN1110_STATUS1, &status1); + if (ret < 0) + goto out; + + if (priv->append_crc && (status1 & ADIN1110_SPI_ERR)) + dev_warn_ratelimited(&priv->spidev->dev, + "SPI CRC error on write.\n"); + + ret = adin1110_read_reg(priv, ADIN1110_TX_SPACE, &val); + if (ret < 0) + goto out; + + /* TX FIFO space is expressed in half-words */ + priv->tx_space = 2 * val; + + for (i = 0; i < priv->cfg->ports_nr; i++) { + if (adin1110_port_rx_ready(priv->ports[i], status1)) + adin1110_read_frames(priv->ports[i], + ADIN1110_MAX_FRAMES_READ); + } + + /* clear IRQ sources */ + adin1110_write_reg(priv, ADIN1110_STATUS0, ADIN1110_CLEAR_STATUS0); + adin1110_write_reg(priv, ADIN1110_STATUS1, priv->irq_mask); + +out: + mutex_unlock(&priv->lock); + + if (priv->tx_space > 0 && ret >= 0) + adin1110_wake_queues(priv); + + return IRQ_HANDLED; +} + +/* ADIN1110 can filter up to 16 MAC addresses, mac_nr here is the slot used */ +static int adin1110_write_mac_address(struct adin1110_port_priv *port_priv, + int mac_nr, const u8 *addr, + u8 *mask, u32 port_rules) +{ + struct adin1110_priv *priv = port_priv->priv; + u32 offset = mac_nr * 2; + u32 port_rules_mask; + int ret; + u32 val; + + if (!port_priv->nr) + port_rules_mask = ADIN1110_MAC_ADDR_APPLY2PORT; + else + port_rules_mask = ADIN2111_MAC_ADDR_APPLY2PORT2; + + if (port_rules & port_rules_mask) + port_rules_mask |= ADIN1110_MAC_ADDR_TO_HOST | ADIN2111_MAC_ADDR_TO_OTHER_PORT; + + port_rules_mask |= GENMASK(15, 0); + val = port_rules | get_unaligned_be16(&addr[0]); + ret = adin1110_set_bits(priv, ADIN1110_MAC_ADDR_FILTER_UPR + offset, + port_rules_mask, val); + if (ret < 0) + return ret; + + val = get_unaligned_be32(&addr[2]); + ret = adin1110_write_reg(priv, + ADIN1110_MAC_ADDR_FILTER_LWR + offset, val); + if (ret < 0) + return ret; + + /* Only the first two MAC address slots support masking. */ + if (mac_nr < ADIN_MAC_P1_ADDR_SLOT) { + val = get_unaligned_be16(&mask[0]); + ret = adin1110_write_reg(priv, + ADIN1110_MAC_ADDR_MASK_UPR + offset, + val); + if (ret < 0) + return ret; + + val = get_unaligned_be32(&mask[2]); + return adin1110_write_reg(priv, + ADIN1110_MAC_ADDR_MASK_LWR + offset, + val); + } + + return 0; +} + +static int adin1110_clear_mac_address(struct adin1110_priv *priv, int mac_nr) +{ + u32 offset = mac_nr * 2; + int ret; + + ret = adin1110_write_reg(priv, ADIN1110_MAC_ADDR_FILTER_UPR + offset, 0); + if (ret < 0) + return ret; + + ret = adin1110_write_reg(priv, ADIN1110_MAC_ADDR_FILTER_LWR + offset, 0); + if (ret < 0) + return ret; + + /* only the first two MAC address slots are maskable */ + if (mac_nr <= 1) { + ret = adin1110_write_reg(priv, ADIN1110_MAC_ADDR_MASK_UPR + offset, 0); + if (ret < 0) + return ret; + + ret = adin1110_write_reg(priv, ADIN1110_MAC_ADDR_MASK_LWR + offset, 0); + } + + return ret; +} + +static u32 adin1110_port_rules(struct adin1110_port_priv *port_priv, + bool fw_to_host, + bool fw_to_other_port) +{ + u32 port_rules = 0; + + if (!port_priv->nr) + port_rules |= ADIN1110_MAC_ADDR_APPLY2PORT; + else + port_rules |= ADIN2111_MAC_ADDR_APPLY2PORT2; + + if (fw_to_host) + port_rules |= ADIN1110_MAC_ADDR_TO_HOST; + + if (fw_to_other_port && port_priv->priv->forwarding) + port_rules |= ADIN2111_MAC_ADDR_TO_OTHER_PORT; + + return port_rules; +} + +static int adin1110_multicast_filter(struct adin1110_port_priv *port_priv, + int mac_nr, bool accept_multicast) +{ + u8 mask[ETH_ALEN] = {0}; + u8 mac[ETH_ALEN] = {0}; + u32 port_rules = 0; + + mask[0] = BIT(0); + mac[0] = BIT(0); + + if (accept_multicast && port_priv->state == BR_STATE_FORWARDING) + port_rules = adin1110_port_rules(port_priv, true, true); + + return adin1110_write_mac_address(port_priv, mac_nr, mac, + mask, port_rules); +} + +static int adin1110_broadcasts_filter(struct adin1110_port_priv *port_priv, + int mac_nr, bool accept_broadcast) +{ + u32 port_rules = 0; + u8 mask[ETH_ALEN]; + + memset(mask, 0xFF, ETH_ALEN); + + if (accept_broadcast && port_priv->state == BR_STATE_FORWARDING) + port_rules = adin1110_port_rules(port_priv, true, true); + + return adin1110_write_mac_address(port_priv, mac_nr, mask, + mask, port_rules); +} + +static int adin1110_set_mac_address(struct net_device *netdev, + const unsigned char *dev_addr) +{ + struct adin1110_port_priv *port_priv = netdev_priv(netdev); + u8 mask[ETH_ALEN]; + u32 port_rules; + u32 mac_slot; + + if (!is_valid_ether_addr(dev_addr)) + return -EADDRNOTAVAIL; + + eth_hw_addr_set(netdev, dev_addr); + memset(mask, 0xFF, ETH_ALEN); + + mac_slot = (!port_priv->nr) ? ADIN_MAC_P1_ADDR_SLOT : ADIN_MAC_P2_ADDR_SLOT; + port_rules = adin1110_port_rules(port_priv, true, false); + + return adin1110_write_mac_address(port_priv, mac_slot, netdev->dev_addr, + mask, port_rules); +} + +static int adin1110_ndo_set_mac_address(struct net_device *netdev, void *addr) +{ + struct sockaddr *sa = addr; + int ret; + + ret = eth_prepare_mac_addr_change(netdev, addr); + if (ret < 0) + return ret; + + return adin1110_set_mac_address(netdev, sa->sa_data); +} + +static int adin1110_ioctl(struct net_device *netdev, struct ifreq *rq, int cmd) +{ + if (!netif_running(netdev)) + return -EINVAL; + + return phy_do_ioctl(netdev, rq, cmd); +} + +static int adin1110_set_promisc_mode(struct adin1110_port_priv *port_priv, + bool promisc) +{ + struct adin1110_priv *priv = port_priv->priv; + u32 mask; + + if (port_priv->state != BR_STATE_FORWARDING) + promisc = false; + + if (!port_priv->nr) + mask = ADIN1110_FWD_UNK2HOST; + else + mask = ADIN2111_P2_FWD_UNK2HOST; + + return adin1110_set_bits(priv, ADIN1110_CONFIG2, + mask, promisc ? mask : 0); +} + +static int adin1110_setup_rx_mode(struct adin1110_port_priv *port_priv) +{ + int ret; + + ret = adin1110_set_promisc_mode(port_priv, + !!(port_priv->flags & IFF_PROMISC)); + if (ret < 0) + return ret; + + ret = adin1110_multicast_filter(port_priv, ADIN_MAC_MULTICAST_ADDR_SLOT, + !!(port_priv->flags & IFF_ALLMULTI)); + if (ret < 0) + return ret; + + ret = adin1110_broadcasts_filter(port_priv, + ADIN_MAC_BROADCAST_ADDR_SLOT, + !!(port_priv->flags & IFF_BROADCAST)); + if (ret < 0) + return ret; + + return adin1110_set_bits(port_priv->priv, ADIN1110_CONFIG1, + ADIN1110_CONFIG1_SYNC, ADIN1110_CONFIG1_SYNC); +} + +static bool adin1110_can_offload_forwarding(struct adin1110_priv *priv) +{ + int i; + + if (priv->cfg->id != ADIN2111_MAC) + return false; + + /* Can't enable forwarding if ports do not belong to the same bridge */ + if (priv->ports[0]->bridge != priv->ports[1]->bridge || !priv->ports[0]->bridge) + return false; + + /* Can't enable forwarding if there is a port + * that has been blocked by STP. + */ + for (i = 0; i < priv->cfg->ports_nr; i++) { + if (priv->ports[i]->state != BR_STATE_FORWARDING) + return false; + } + + return true; +} + +static void adin1110_rx_mode_work(struct work_struct *work) +{ + struct adin1110_port_priv *port_priv; + struct adin1110_priv *priv; + + port_priv = container_of(work, struct adin1110_port_priv, rx_mode_work); + priv = port_priv->priv; + + mutex_lock(&priv->lock); + adin1110_setup_rx_mode(port_priv); + mutex_unlock(&priv->lock); +} + +static void adin1110_set_rx_mode(struct net_device *dev) +{ + struct adin1110_port_priv *port_priv = netdev_priv(dev); + struct adin1110_priv *priv = port_priv->priv; + + spin_lock(&priv->state_lock); + + port_priv->flags = dev->flags; + schedule_work(&port_priv->rx_mode_work); + + spin_unlock(&priv->state_lock); +} + +static int adin1110_net_open(struct net_device *net_dev) +{ + struct adin1110_port_priv *port_priv = netdev_priv(net_dev); + struct adin1110_priv *priv = port_priv->priv; + u32 val; + int ret; + + mutex_lock(&priv->lock); + + /* Configure MAC to compute and append the FCS itself. */ + ret = adin1110_write_reg(priv, ADIN1110_CONFIG2, ADIN1110_CRC_APPEND); + if (ret < 0) + goto out; + + val = ADIN1110_TX_RDY_IRQ | ADIN1110_RX_RDY_IRQ | ADIN1110_SPI_ERR_IRQ; + if (priv->cfg->id == ADIN2111_MAC) + val |= ADIN2111_RX_RDY_IRQ; + + priv->irq_mask = val; + ret = adin1110_write_reg(priv, ADIN1110_IMASK1, ~val); + if (ret < 0) { + netdev_err(net_dev, "Failed to enable chip IRQs: %d\n", ret); + goto out; + } + + ret = adin1110_read_reg(priv, ADIN1110_TX_SPACE, &val); + if (ret < 0) { + netdev_err(net_dev, "Failed to read TX FIFO space: %d\n", ret); + goto out; + } + + priv->tx_space = 2 * val; + + port_priv->state = BR_STATE_FORWARDING; + ret = adin1110_set_mac_address(net_dev, net_dev->dev_addr); + if (ret < 0) { + netdev_err(net_dev, "Could not set MAC address: %pM, %d\n", + net_dev->dev_addr, ret); + goto out; + } + + ret = adin1110_set_bits(priv, ADIN1110_CONFIG1, ADIN1110_CONFIG1_SYNC, + ADIN1110_CONFIG1_SYNC); + +out: + mutex_unlock(&priv->lock); + + if (ret < 0) + return ret; + + phy_start(port_priv->phydev); + + netif_start_queue(net_dev); + + return 0; +} + +static int adin1110_net_stop(struct net_device *net_dev) +{ + struct adin1110_port_priv *port_priv = netdev_priv(net_dev); + struct adin1110_priv *priv = port_priv->priv; + u32 mask; + int ret; + + mask = !port_priv->nr ? ADIN2111_RX_RDY_IRQ : ADIN1110_RX_RDY_IRQ; + + /* Disable RX RDY IRQs */ + mutex_lock(&priv->lock); + ret = adin1110_set_bits(priv, ADIN1110_IMASK1, mask, mask); + mutex_unlock(&priv->lock); + if (ret < 0) + return ret; + + netif_stop_queue(port_priv->netdev); + flush_work(&port_priv->tx_work); + phy_stop(port_priv->phydev); + + return 0; +} + +static void adin1110_tx_work(struct work_struct *work) +{ + struct adin1110_port_priv *port_priv; + struct adin1110_priv *priv; + struct sk_buff *txb; + int ret; + + port_priv = container_of(work, struct adin1110_port_priv, tx_work); + priv = port_priv->priv; + + mutex_lock(&priv->lock); + + while ((txb = skb_dequeue(&port_priv->txq))) { + ret = adin1110_write_fifo(port_priv, txb); + if (ret < 0) + dev_err_ratelimited(&priv->spidev->dev, + "Frame write error: %d\n", ret); + + dev_kfree_skb(txb); + } + + mutex_unlock(&priv->lock); +} + +static netdev_tx_t adin1110_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct adin1110_port_priv *port_priv = netdev_priv(dev); + struct adin1110_priv *priv = port_priv->priv; + netdev_tx_t netdev_ret = NETDEV_TX_OK; + u32 tx_space_needed; + + tx_space_needed = skb->len + ADIN1110_FRAME_HEADER_LEN + ADIN1110_INTERNAL_SIZE_HEADER_LEN; + if (tx_space_needed > priv->tx_space) { + netif_stop_queue(dev); + netdev_ret = NETDEV_TX_BUSY; + } else { + priv->tx_space -= tx_space_needed; + skb_queue_tail(&port_priv->txq, skb); + } + + schedule_work(&port_priv->tx_work); + + return netdev_ret; +} + +static void adin1110_ndo_get_stats64(struct net_device *dev, + struct rtnl_link_stats64 *storage) +{ + struct adin1110_port_priv *port_priv = netdev_priv(dev); + + storage->rx_packets = port_priv->rx_packets; + storage->tx_packets = port_priv->tx_packets; + + storage->rx_bytes = port_priv->rx_bytes; + storage->tx_bytes = port_priv->tx_bytes; +} + +static int adin1110_port_get_port_parent_id(struct net_device *dev, + struct netdev_phys_item_id *ppid) +{ + struct adin1110_port_priv *port_priv = netdev_priv(dev); + struct adin1110_priv *priv = port_priv->priv; + + ppid->id_len = strnlen(priv->mii_bus_name, MAX_PHYS_ITEM_ID_LEN); + memcpy(ppid->id, priv->mii_bus_name, ppid->id_len); + + return 0; +} + +static int adin1110_ndo_get_phys_port_name(struct net_device *dev, + char *name, size_t len) +{ + struct adin1110_port_priv *port_priv = netdev_priv(dev); + int err; + + err = snprintf(name, len, "p%d", port_priv->nr); + if (err >= len) + return -EINVAL; + + return 0; +} + +static const struct net_device_ops adin1110_netdev_ops = { + .ndo_open = adin1110_net_open, + .ndo_stop = adin1110_net_stop, + .ndo_eth_ioctl = adin1110_ioctl, + .ndo_start_xmit = adin1110_start_xmit, + .ndo_set_mac_address = adin1110_ndo_set_mac_address, + .ndo_set_rx_mode = adin1110_set_rx_mode, + .ndo_validate_addr = eth_validate_addr, + .ndo_get_stats64 = adin1110_ndo_get_stats64, + .ndo_get_port_parent_id = adin1110_port_get_port_parent_id, + .ndo_get_phys_port_name = adin1110_ndo_get_phys_port_name, +}; + +static void adin1110_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *di) +{ + strscpy(di->driver, "ADIN1110", sizeof(di->driver)); + strscpy(di->bus_info, dev_name(dev->dev.parent), sizeof(di->bus_info)); +} + +static const struct ethtool_ops adin1110_ethtool_ops = { + .get_drvinfo = adin1110_get_drvinfo, + .get_link = ethtool_op_get_link, + .get_link_ksettings = phy_ethtool_get_link_ksettings, + .set_link_ksettings = phy_ethtool_set_link_ksettings, +}; + +static void adin1110_adjust_link(struct net_device *dev) +{ + struct phy_device *phydev = dev->phydev; + + if (!phydev->link) + phy_print_status(phydev); +} + +/* PHY ID is stored in the MAC registers too, + * check spi connection by reading it. + */ +static int adin1110_check_spi(struct adin1110_priv *priv) +{ + int ret; + u32 val; + + ret = adin1110_read_reg(priv, ADIN1110_PHY_ID, &val); + if (ret < 0) + return ret; + + if (val != priv->cfg->phy_id_val) { + dev_err(&priv->spidev->dev, "PHY ID expected: %x, read: %x\n", + priv->cfg->phy_id_val, val); + return -EIO; + } + + return 0; +} + +static int adin1110_hw_forwarding(struct adin1110_priv *priv, bool enable) +{ + int ret; + int i; + + priv->forwarding = enable; + + if (!priv->forwarding) { + for (i = ADIN_MAC_FDB_ADDR_SLOT; i < ADIN_MAC_MAX_ADDR_SLOTS; i++) { + ret = adin1110_clear_mac_address(priv, i); + if (ret < 0) + return ret; + } + } + + /* Forwarding is optimised when MAC runs in Cut Through mode. */ + ret = adin1110_set_bits(priv, ADIN1110_CONFIG2, + ADIN2111_PORT_CUT_THRU_EN, + priv->forwarding ? ADIN2111_PORT_CUT_THRU_EN : 0); + if (ret < 0) + return ret; + + for (i = 0; i < priv->cfg->ports_nr; i++) { + ret = adin1110_setup_rx_mode(priv->ports[i]); + if (ret < 0) + return ret; + } + + return ret; +} + +static int adin1110_port_bridge_join(struct adin1110_port_priv *port_priv, + struct net_device *bridge) +{ + struct adin1110_priv *priv = port_priv->priv; + int ret; + + port_priv->bridge = bridge; + + if (adin1110_can_offload_forwarding(priv)) { + mutex_lock(&priv->lock); + ret = adin1110_hw_forwarding(priv, true); + mutex_unlock(&priv->lock); + + if (ret < 0) + return ret; + } + + return adin1110_set_mac_address(port_priv->netdev, bridge->dev_addr); +} + +static int adin1110_port_bridge_leave(struct adin1110_port_priv *port_priv, + struct net_device *bridge) +{ + struct adin1110_priv *priv = port_priv->priv; + int ret; + + port_priv->bridge = NULL; + + mutex_lock(&priv->lock); + ret = adin1110_hw_forwarding(priv, false); + mutex_unlock(&priv->lock); + + return ret; +} + +static int adin1110_netdevice_event(struct notifier_block *unused, + unsigned long event, void *ptr) +{ + struct net_device *dev = netdev_notifier_info_to_dev(ptr); + struct adin1110_port_priv *port_priv = netdev_priv(dev); + struct netdev_notifier_changeupper_info *info = ptr; + int ret = 0; + + switch (event) { + case NETDEV_CHANGEUPPER: + if (netif_is_bridge_master(info->upper_dev)) { + if (info->linking) + ret = adin1110_port_bridge_join(port_priv, info->upper_dev); + else + ret = adin1110_port_bridge_leave(port_priv, info->upper_dev); + } + break; + default: + break; + } + + return notifier_from_errno(ret); +} + +static struct notifier_block adin1110_netdevice_nb = { + .notifier_call = adin1110_netdevice_event, +}; + +static void adin1110_disconnect_phy(void *data) +{ + phy_disconnect(data); +} + +static bool adin1110_port_dev_check(const struct net_device *dev) +{ + return dev->netdev_ops == &adin1110_netdev_ops; +} + +static int adin1110_port_set_forwarding_state(struct adin1110_port_priv *port_priv) +{ + struct adin1110_priv *priv = port_priv->priv; + int ret; + + port_priv->state = BR_STATE_FORWARDING; + + mutex_lock(&priv->lock); + ret = adin1110_set_mac_address(port_priv->netdev, + port_priv->netdev->dev_addr); + if (ret < 0) + goto out; + + if (adin1110_can_offload_forwarding(priv)) + ret = adin1110_hw_forwarding(priv, true); + else + ret = adin1110_setup_rx_mode(port_priv); +out: + mutex_unlock(&priv->lock); + + return ret; +} + +static int adin1110_port_set_blocking_state(struct adin1110_port_priv *port_priv) +{ + u8 mac[ETH_ALEN] = {0x01, 0x80, 0xC2, 0x00, 0x00, 0x00}; + struct adin1110_priv *priv = port_priv->priv; + u8 mask[ETH_ALEN]; + u32 port_rules; + int mac_slot; + int ret; + + port_priv->state = BR_STATE_BLOCKING; + + mutex_lock(&priv->lock); + + mac_slot = (!port_priv->nr) ? ADIN_MAC_P1_ADDR_SLOT : ADIN_MAC_P2_ADDR_SLOT; + ret = adin1110_clear_mac_address(priv, mac_slot); + if (ret < 0) + goto out; + + ret = adin1110_hw_forwarding(priv, false); + if (ret < 0) + goto out; + + /* Allow only BPDUs to be passed to the CPU */ + memset(mask, 0xFF, ETH_ALEN); + port_rules = adin1110_port_rules(port_priv, true, false); + ret = adin1110_write_mac_address(port_priv, mac_slot, mac, + mask, port_rules); +out: + mutex_unlock(&priv->lock); + + return ret; +} + +/* ADIN1110/2111 does not have any native STP support. + * Listen for bridge core state changes and + * allow all frames to pass or only the BPDUs. + */ +static int adin1110_port_attr_stp_state_set(struct adin1110_port_priv *port_priv, + u8 state) +{ + switch (state) { + case BR_STATE_FORWARDING: + return adin1110_port_set_forwarding_state(port_priv); + case BR_STATE_LEARNING: + case BR_STATE_LISTENING: + case BR_STATE_DISABLED: + case BR_STATE_BLOCKING: + return adin1110_port_set_blocking_state(port_priv); + default: + return -EINVAL; + } +} + +static int adin1110_port_attr_set(struct net_device *dev, const void *ctx, + const struct switchdev_attr *attr, + struct netlink_ext_ack *extack) +{ + struct adin1110_port_priv *port_priv = netdev_priv(dev); + + switch (attr->id) { + case SWITCHDEV_ATTR_ID_PORT_STP_STATE: + return adin1110_port_attr_stp_state_set(port_priv, + attr->u.stp_state); + default: + return -EOPNOTSUPP; + } +} + +static int adin1110_switchdev_blocking_event(struct notifier_block *unused, + unsigned long event, + void *ptr) +{ + struct net_device *netdev = switchdev_notifier_info_to_dev(ptr); + int ret; + + if (event == SWITCHDEV_PORT_ATTR_SET) { + ret = switchdev_handle_port_attr_set(netdev, ptr, + adin1110_port_dev_check, + adin1110_port_attr_set); + + return notifier_from_errno(ret); + } + + return NOTIFY_DONE; +} + +static struct notifier_block adin1110_switchdev_blocking_notifier = { + .notifier_call = adin1110_switchdev_blocking_event, +}; + +static void adin1110_fdb_offload_notify(struct net_device *netdev, + struct switchdev_notifier_fdb_info *rcv) +{ + struct switchdev_notifier_fdb_info info = {}; + + info.addr = rcv->addr; + info.vid = rcv->vid; + info.offloaded = true; + call_switchdev_notifiers(SWITCHDEV_FDB_OFFLOADED, + netdev, &info.info, NULL); +} + +static int adin1110_fdb_add(struct adin1110_port_priv *port_priv, + struct switchdev_notifier_fdb_info *fdb) +{ + struct adin1110_priv *priv = port_priv->priv; + struct adin1110_port_priv *other_port; + u8 mask[ETH_ALEN]; + u32 port_rules; + int mac_nr; + u32 val; + int ret; + + netdev_dbg(port_priv->netdev, + "DEBUG: %s: MACID = %pM vid = %u flags = %u %u -- port %d\n", + __func__, fdb->addr, fdb->vid, fdb->added_by_user, + fdb->offloaded, port_priv->nr); + + if (!priv->forwarding) + return 0; + + if (fdb->is_local) + return -EINVAL; + + /* Find free FDB slot on device. */ + for (mac_nr = ADIN_MAC_FDB_ADDR_SLOT; mac_nr < ADIN_MAC_MAX_ADDR_SLOTS; mac_nr++) { + ret = adin1110_read_reg(priv, ADIN1110_MAC_ADDR_FILTER_UPR + (mac_nr * 2), &val); + if (ret < 0) + return ret; + if (!val) + break; + } + + if (mac_nr == ADIN_MAC_MAX_ADDR_SLOTS) + return -ENOMEM; + + other_port = priv->ports[!port_priv->nr]; + port_rules = adin1110_port_rules(port_priv, false, true); + memset(mask, 0xFF, ETH_ALEN); + + return adin1110_write_mac_address(other_port, mac_nr, (u8 *)fdb->addr, + mask, port_rules); +} + +static int adin1110_read_mac(struct adin1110_priv *priv, int mac_nr, u8 *addr) +{ + u32 val; + int ret; + + ret = adin1110_read_reg(priv, ADIN1110_MAC_ADDR_FILTER_UPR + (mac_nr * 2), &val); + if (ret < 0) + return ret; + + put_unaligned_be16(val, addr); + + ret = adin1110_read_reg(priv, ADIN1110_MAC_ADDR_FILTER_LWR + (mac_nr * 2), &val); + if (ret < 0) + return ret; + + put_unaligned_be32(val, addr + 2); + + return 0; +} + +static int adin1110_fdb_del(struct adin1110_port_priv *port_priv, + struct switchdev_notifier_fdb_info *fdb) +{ + struct adin1110_priv *priv = port_priv->priv; + u8 addr[ETH_ALEN]; + int mac_nr; + int ret; + + netdev_dbg(port_priv->netdev, + "DEBUG: %s: MACID = %pM vid = %u flags = %u %u -- port %d\n", + __func__, fdb->addr, fdb->vid, fdb->added_by_user, + fdb->offloaded, port_priv->nr); + + if (fdb->is_local) + return -EINVAL; + + for (mac_nr = ADIN_MAC_FDB_ADDR_SLOT; mac_nr < ADIN_MAC_MAX_ADDR_SLOTS; mac_nr++) { + ret = adin1110_read_mac(priv, mac_nr, addr); + if (ret < 0) + return ret; + + if (ether_addr_equal(addr, fdb->addr)) { + ret = adin1110_clear_mac_address(priv, mac_nr); + if (ret < 0) + return ret; + } + } + + return 0; +} + +static void adin1110_switchdev_event_work(struct work_struct *work) +{ + struct adin1110_switchdev_event_work *switchdev_work; + struct adin1110_port_priv *port_priv; + int ret; + + switchdev_work = container_of(work, struct adin1110_switchdev_event_work, work); + port_priv = switchdev_work->port_priv; + + mutex_lock(&port_priv->priv->lock); + + switch (switchdev_work->event) { + case SWITCHDEV_FDB_ADD_TO_DEVICE: + ret = adin1110_fdb_add(port_priv, &switchdev_work->fdb_info); + if (!ret) + adin1110_fdb_offload_notify(port_priv->netdev, + &switchdev_work->fdb_info); + break; + case SWITCHDEV_FDB_DEL_TO_DEVICE: + adin1110_fdb_del(port_priv, &switchdev_work->fdb_info); + break; + default: + break; + } + + mutex_unlock(&port_priv->priv->lock); + + kfree(switchdev_work->fdb_info.addr); + kfree(switchdev_work); + dev_put(port_priv->netdev); +} + +/* called under rcu_read_lock() */ +static int adin1110_switchdev_event(struct notifier_block *unused, + unsigned long event, void *ptr) +{ + struct net_device *netdev = switchdev_notifier_info_to_dev(ptr); + struct adin1110_port_priv *port_priv = netdev_priv(netdev); + struct adin1110_switchdev_event_work *switchdev_work; + struct switchdev_notifier_fdb_info *fdb_info = ptr; + + if (!adin1110_port_dev_check(netdev)) + return NOTIFY_DONE; + + switchdev_work = kzalloc(sizeof(*switchdev_work), GFP_ATOMIC); + if (WARN_ON(!switchdev_work)) + return NOTIFY_BAD; + + INIT_WORK(&switchdev_work->work, adin1110_switchdev_event_work); + switchdev_work->port_priv = port_priv; + switchdev_work->event = event; + + switch (event) { + case SWITCHDEV_FDB_ADD_TO_DEVICE: + case SWITCHDEV_FDB_DEL_TO_DEVICE: + memcpy(&switchdev_work->fdb_info, ptr, + sizeof(switchdev_work->fdb_info)); + switchdev_work->fdb_info.addr = kzalloc(ETH_ALEN, GFP_ATOMIC); + + if (!switchdev_work->fdb_info.addr) + goto err_addr_alloc; + + ether_addr_copy((u8 *)switchdev_work->fdb_info.addr, + fdb_info->addr); + dev_hold(netdev); + break; + default: + kfree(switchdev_work); + return NOTIFY_DONE; + } + + queue_work(system_long_wq, &switchdev_work->work); + + return NOTIFY_DONE; + +err_addr_alloc: + kfree(switchdev_work); + return NOTIFY_BAD; +} + +static struct notifier_block adin1110_switchdev_notifier = { + .notifier_call = adin1110_switchdev_event, +}; + +static void adin1110_unregister_notifiers(void *data) +{ + unregister_switchdev_blocking_notifier(&adin1110_switchdev_blocking_notifier); + unregister_switchdev_notifier(&adin1110_switchdev_notifier); + unregister_netdevice_notifier(&adin1110_netdevice_nb); +} + +static int adin1110_setup_notifiers(struct adin1110_priv *priv) +{ + struct device *dev = &priv->spidev->dev; + int ret; + + ret = register_netdevice_notifier(&adin1110_netdevice_nb); + if (ret < 0) + return ret; + + ret = register_switchdev_notifier(&adin1110_switchdev_notifier); + if (ret < 0) + goto err_netdev; + + ret = register_switchdev_blocking_notifier(&adin1110_switchdev_blocking_notifier); + if (ret < 0) + goto err_sdev; + + return devm_add_action_or_reset(dev, adin1110_unregister_notifiers, NULL); + +err_sdev: + unregister_switchdev_notifier(&adin1110_switchdev_notifier); + +err_netdev: + unregister_netdevice_notifier(&adin1110_netdevice_nb); + return ret; +} + +static int adin1110_probe_netdevs(struct adin1110_priv *priv) +{ + struct device *dev = &priv->spidev->dev; + struct adin1110_port_priv *port_priv; + struct net_device *netdev; + int ret; + int i; + + for (i = 0; i < priv->cfg->ports_nr; i++) { + netdev = devm_alloc_etherdev(dev, sizeof(*port_priv)); + if (!netdev) + return -ENOMEM; + + port_priv = netdev_priv(netdev); + port_priv->netdev = netdev; + port_priv->priv = priv; + port_priv->cfg = priv->cfg; + port_priv->nr = i; + priv->ports[i] = port_priv; + SET_NETDEV_DEV(netdev, dev); + + ret = device_get_ethdev_address(dev, netdev); + if (ret < 0) + return ret; + + netdev->irq = priv->spidev->irq; + INIT_WORK(&port_priv->tx_work, adin1110_tx_work); + INIT_WORK(&port_priv->rx_mode_work, adin1110_rx_mode_work); + skb_queue_head_init(&port_priv->txq); + + netif_carrier_off(netdev); + + netdev->if_port = IF_PORT_10BASET; + netdev->netdev_ops = &adin1110_netdev_ops; + netdev->ethtool_ops = &adin1110_ethtool_ops; + netdev->priv_flags |= IFF_UNICAST_FLT; + netdev->features |= NETIF_F_NETNS_LOCAL; + + port_priv->phydev = get_phy_device(priv->mii_bus, i + 1, false); + if (IS_ERR(port_priv->phydev)) { + netdev_err(netdev, "Could not find PHY with device address: %d.\n", i); + return PTR_ERR(port_priv->phydev); + } + + port_priv->phydev = phy_connect(netdev, + phydev_name(port_priv->phydev), + adin1110_adjust_link, + PHY_INTERFACE_MODE_INTERNAL); + if (IS_ERR(port_priv->phydev)) { + netdev_err(netdev, "Could not connect PHY with device address: %d.\n", i); + return PTR_ERR(port_priv->phydev); + } + + ret = devm_add_action_or_reset(dev, adin1110_disconnect_phy, + port_priv->phydev); + if (ret < 0) + return ret; + } + + /* ADIN1110 INT_N pin will be used to signal the host */ + ret = devm_request_threaded_irq(dev, priv->spidev->irq, NULL, + adin1110_irq, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, + dev_name(dev), priv); + if (ret < 0) + return ret; + + ret = adin1110_setup_notifiers(priv); + if (ret < 0) + return ret; + + for (i = 0; i < priv->cfg->ports_nr; i++) { + ret = devm_register_netdev(dev, priv->ports[i]->netdev); + if (ret < 0) { + dev_err(dev, "Failed to register network device.\n"); + return ret; + } + } + + return 0; +} + +static int adin1110_probe(struct spi_device *spi) +{ + const struct spi_device_id *dev_id = spi_get_device_id(spi); + struct device *dev = &spi->dev; + struct adin1110_priv *priv; + int ret; + + priv = devm_kzalloc(dev, sizeof(struct adin1110_priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->spidev = spi; + priv->cfg = &adin1110_cfgs[dev_id->driver_data]; + spi->bits_per_word = 8; + spi->mode = SPI_MODE_0; + + mutex_init(&priv->lock); + spin_lock_init(&priv->state_lock); + + /* use of CRC on control and data transactions is pin dependent */ + priv->append_crc = device_property_read_bool(dev, "adi,spi-crc"); + if (priv->append_crc) + crc8_populate_msb(adin1110_crc_table, 0x7); + + ret = adin1110_check_spi(priv); + if (ret < 0) { + dev_err(dev, "Probe SPI Read check failed: %d\n", ret); + return ret; + } + + ret = adin1110_write_reg(priv, ADIN1110_RESET, ADIN1110_SWRESET); + if (ret < 0) + return ret; + + ret = adin1110_register_mdiobus(priv, dev); + if (ret < 0) { + dev_err(dev, "Could not register MDIO bus %d\n", ret); + return ret; + } + + return adin1110_probe_netdevs(priv); +} + +static const struct of_device_id adin1110_match_table[] = { + { .compatible = "adi,adin1110" }, + { .compatible = "adi,adin2111" }, + { } +}; +MODULE_DEVICE_TABLE(of, adin1110_match_table); + +static const struct spi_device_id adin1110_spi_id[] = { + { .name = "adin1110", .driver_data = ADIN1110_MAC }, + { .name = "adin2111", .driver_data = ADIN2111_MAC }, + { } +}; +MODULE_DEVICE_TABLE(spi, adin1110_spi_id); + +static struct spi_driver adin1110_driver = { + .driver = { + .name = "adin1110", + .of_match_table = adin1110_match_table, + }, + .probe = adin1110_probe, + .id_table = adin1110_spi_id, +}; +module_spi_driver(adin1110_driver); + +MODULE_DESCRIPTION("ADIN1110 Network driver"); +MODULE_AUTHOR("Alexandru Tachici <alexandru.tachici@analog.com>"); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/net/ethernet/aeroflex/greth.c b/drivers/net/ethernet/aeroflex/greth.c index 447dc64a17e5..e104fb02817d 100644 --- a/drivers/net/ethernet/aeroflex/greth.c +++ b/drivers/net/ethernet/aeroflex/greth.c @@ -1112,9 +1112,9 @@ static void greth_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *in { struct greth_private *greth = netdev_priv(dev); - strlcpy(info->driver, dev_driver_string(greth->dev), + strscpy(info->driver, dev_driver_string(greth->dev), sizeof(info->driver)); - strlcpy(info->bus_info, greth->dev->bus->name, sizeof(info->bus_info)); + strscpy(info->bus_info, greth->dev->bus->name, sizeof(info->bus_info)); } static void greth_get_regs(struct net_device *dev, struct ethtool_regs *regs, void *p) @@ -1507,7 +1507,7 @@ static int greth_of_probe(struct platform_device *ofdev) } /* setup NAPI */ - netif_napi_add(dev, &greth->napi, greth_poll, 64); + netif_napi_add(dev, &greth->napi, greth_poll); return 0; diff --git a/drivers/net/ethernet/agere/et131x.c b/drivers/net/ethernet/agere/et131x.c index d19d1579c415..5fab589b3ddf 100644 --- a/drivers/net/ethernet/agere/et131x.c +++ b/drivers/net/ethernet/agere/et131x.c @@ -2952,8 +2952,8 @@ static void et131x_get_drvinfo(struct net_device *netdev, { struct et131x_adapter *adapter = netdev_priv(netdev); - strlcpy(info->driver, DRIVER_NAME, sizeof(info->driver)); - strlcpy(info->bus_info, pci_name(adapter->pdev), + strscpy(info->driver, DRIVER_NAME, sizeof(info->driver)); + strscpy(info->bus_info, pci_name(adapter->pdev), sizeof(info->bus_info)); } @@ -3969,7 +3969,7 @@ static int et131x_pci_setup(struct pci_dev *pdev, et131x_init_send(adapter); - netif_napi_add(netdev, &adapter->napi, et131x_poll, 64); + netif_napi_add(netdev, &adapter->napi, et131x_poll); eth_hw_addr_set(netdev, adapter->addr); diff --git a/drivers/net/ethernet/alacritech/slicoss.c b/drivers/net/ethernet/alacritech/slicoss.c index ce353b0c02a3..a30d0f172986 100644 --- a/drivers/net/ethernet/alacritech/slicoss.c +++ b/drivers/net/ethernet/alacritech/slicoss.c @@ -1531,8 +1531,8 @@ static void slic_get_drvinfo(struct net_device *dev, { struct slic_device *sdev = netdev_priv(dev); - strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); - strlcpy(info->bus_info, pci_name(sdev->pdev), sizeof(info->bus_info)); + strscpy(info->driver, DRV_NAME, sizeof(info->driver)); + strscpy(info->bus_info, pci_name(sdev->pdev), sizeof(info->bus_info)); } static const struct ethtool_ops slic_ethtool_ops = { @@ -1803,7 +1803,7 @@ static int slic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) goto unmap; } - netif_napi_add(dev, &sdev->napi, slic_poll, NAPI_POLL_WEIGHT); + netif_napi_add(dev, &sdev->napi, slic_poll); netif_carrier_off(dev); err = register_netdev(dev); diff --git a/drivers/net/ethernet/allwinner/sun4i-emac.c b/drivers/net/ethernet/allwinner/sun4i-emac.c index 621ce742ad21..a94c62956eed 100644 --- a/drivers/net/ethernet/allwinner/sun4i-emac.c +++ b/drivers/net/ethernet/allwinner/sun4i-emac.c @@ -331,8 +331,8 @@ prepare_err: static void emac_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { - strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); - strlcpy(info->bus_info, dev_name(&dev->dev), sizeof(info->bus_info)); + strscpy(info->driver, DRV_NAME, sizeof(info->driver)); + strscpy(info->bus_info, dev_name(&dev->dev), sizeof(info->bus_info)); } static u32 emac_get_msglevel(struct net_device *dev) diff --git a/drivers/net/ethernet/alteon/acenic.c b/drivers/net/ethernet/alteon/acenic.c index 22fe98555b24..d7762da8b2c0 100644 --- a/drivers/net/ethernet/alteon/acenic.c +++ b/drivers/net/ethernet/alteon/acenic.c @@ -2691,12 +2691,12 @@ static void ace_get_drvinfo(struct net_device *dev, { struct ace_private *ap = netdev_priv(dev); - strlcpy(info->driver, "acenic", sizeof(info->driver)); + strscpy(info->driver, "acenic", sizeof(info->driver)); snprintf(info->fw_version, sizeof(info->version), "%i.%i.%i", ap->firmware_major, ap->firmware_minor, ap->firmware_fix); if (ap->pdev) - strlcpy(info->bus_info, pci_name(ap->pdev), + strscpy(info->bus_info, pci_name(ap->pdev), sizeof(info->bus_info)); } diff --git a/drivers/net/ethernet/altera/Kconfig b/drivers/net/ethernet/altera/Kconfig index 914e56b91467..dd7fd41ccde5 100644 --- a/drivers/net/ethernet/altera/Kconfig +++ b/drivers/net/ethernet/altera/Kconfig @@ -3,6 +3,8 @@ config ALTERA_TSE tristate "Altera Triple-Speed Ethernet MAC support" depends on HAS_DMA select PHYLIB + select PHYLINK + select PCS_ALTERA_TSE help This driver supports the Altera Triple-Speed (TSE) Ethernet MAC. diff --git a/drivers/net/ethernet/altera/altera_tse.h b/drivers/net/ethernet/altera/altera_tse.h index f17acfb579a0..db5eed06e92d 100644 --- a/drivers/net/ethernet/altera/altera_tse.h +++ b/drivers/net/ethernet/altera/altera_tse.h @@ -27,6 +27,7 @@ #include <linux/list.h> #include <linux/netdevice.h> #include <linux/phy.h> +#include <linux/phylink.h> #define ALTERA_TSE_SW_RESET_WATCHDOG_CNTR 10000 #define ALTERA_TSE_MAC_FIFO_WIDTH 4 /* TX/RX FIFO width in @@ -109,17 +110,6 @@ #define MAC_CMDCFG_DISABLE_READ_TIMEOUT_GET(v) GET_BIT_VALUE(v, 27) #define MAC_CMDCFG_CNT_RESET_GET(v) GET_BIT_VALUE(v, 31) -/* SGMII PCS register addresses - */ -#define SGMII_PCS_SCRATCH 0x10 -#define SGMII_PCS_REV 0x11 -#define SGMII_PCS_LINK_TIMER_0 0x12 -#define SGMII_PCS_LINK_TIMER_1 0x13 -#define SGMII_PCS_IF_MODE 0x14 -#define SGMII_PCS_DIS_READ_TO 0x15 -#define SGMII_PCS_READ_TO 0x16 -#define SGMII_PCS_SW_RESET_TIMEOUT 100 /* usecs */ - /* MDIO registers within MAC register Space */ struct altera_tse_mdio { @@ -423,6 +413,9 @@ struct altera_tse_private { void __iomem *tx_dma_csr; void __iomem *tx_dma_desc; + /* SGMII PCS address space */ + void __iomem *pcs_base; + /* Rx buffers queue */ struct tse_buffer *rx_ring; u32 rx_cons; @@ -480,6 +473,10 @@ struct altera_tse_private { u32 msg_enable; struct altera_dmaops *dmaops; + + struct phylink *phylink; + struct phylink_config phylink_config; + struct phylink_pcs *pcs; }; /* Function prototypes diff --git a/drivers/net/ethernet/altera/altera_tse_ethtool.c b/drivers/net/ethernet/altera/altera_tse_ethtool.c index 4299f1301149..81313c85833e 100644 --- a/drivers/net/ethernet/altera/altera_tse_ethtool.c +++ b/drivers/net/ethernet/altera/altera_tse_ethtool.c @@ -199,9 +199,9 @@ static int tse_reglen(struct net_device *dev) static void tse_get_regs(struct net_device *dev, struct ethtool_regs *regs, void *regbuf) { - int i; struct altera_tse_private *priv = netdev_priv(dev); u32 *buf = regbuf; + int i; /* Set version to a known value, so ethtool knows * how to do any special formatting of this data. @@ -221,6 +221,22 @@ static void tse_get_regs(struct net_device *dev, struct ethtool_regs *regs, buf[i] = csrrd32(priv->mac_dev, i * 4); } +static int tse_ethtool_set_link_ksettings(struct net_device *dev, + const struct ethtool_link_ksettings *cmd) +{ + struct altera_tse_private *priv = netdev_priv(dev); + + return phylink_ethtool_ksettings_set(priv->phylink, cmd); +} + +static int tse_ethtool_get_link_ksettings(struct net_device *dev, + struct ethtool_link_ksettings *cmd) +{ + struct altera_tse_private *priv = netdev_priv(dev); + + return phylink_ethtool_ksettings_get(priv->phylink, cmd); +} + static const struct ethtool_ops tse_ethtool_ops = { .get_drvinfo = tse_get_drvinfo, .get_regs_len = tse_reglen, @@ -231,8 +247,9 @@ static const struct ethtool_ops tse_ethtool_ops = { .get_ethtool_stats = tse_fill_stats, .get_msglevel = tse_get_msglevel, .set_msglevel = tse_set_msglevel, - .get_link_ksettings = phy_ethtool_get_link_ksettings, - .set_link_ksettings = phy_ethtool_set_link_ksettings, + .get_link_ksettings = tse_ethtool_get_link_ksettings, + .set_link_ksettings = tse_ethtool_set_link_ksettings, + .get_ts_info = ethtool_op_get_ts_info, }; void altera_tse_set_ethtool_ops(struct net_device *netdev) diff --git a/drivers/net/ethernet/altera/altera_tse_main.c b/drivers/net/ethernet/altera/altera_tse_main.c index 8c5828582c21..7633b227b2ca 100644 --- a/drivers/net/ethernet/altera/altera_tse_main.c +++ b/drivers/net/ethernet/altera/altera_tse_main.c @@ -32,6 +32,7 @@ #include <linux/of_mdio.h> #include <linux/of_net.h> #include <linux/of_platform.h> +#include <linux/pcs-altera-tse.h> #include <linux/phy.h> #include <linux/platform_device.h> #include <linux/skbuff.h> @@ -86,27 +87,6 @@ static inline u32 tse_tx_avail(struct altera_tse_private *priv) return priv->tx_cons + priv->tx_ring_size - priv->tx_prod - 1; } -/* PCS Register read/write functions - */ -static u16 sgmii_pcs_read(struct altera_tse_private *priv, int regnum) -{ - return csrrd32(priv->mac_dev, - tse_csroffs(mdio_phy0) + regnum * 4) & 0xffff; -} - -static void sgmii_pcs_write(struct altera_tse_private *priv, int regnum, - u16 value) -{ - csrwr32(value, priv->mac_dev, tse_csroffs(mdio_phy0) + regnum * 4); -} - -/* Check PCS scratch memory */ -static int sgmii_pcs_scratch_test(struct altera_tse_private *priv, u16 value) -{ - sgmii_pcs_write(priv, SGMII_PCS_SCRATCH, value); - return (sgmii_pcs_read(priv, SGMII_PCS_SCRATCH) == value); -} - /* MDIO specific functions */ static int altera_tse_mdio_read(struct mii_bus *bus, int mii_id, int regnum) @@ -141,10 +121,10 @@ static int altera_tse_mdio_write(struct mii_bus *bus, int mii_id, int regnum, static int altera_tse_mdio_create(struct net_device *dev, unsigned int id) { struct altera_tse_private *priv = netdev_priv(dev); - int ret; struct device_node *mdio_node = NULL; - struct mii_bus *mdio = NULL; struct device_node *child_node = NULL; + struct mii_bus *mdio = NULL; + int ret; for_each_child_of_node(priv->device->of_node, child_node) { if (of_device_is_compatible(child_node, "altr,tse-mdio")) { @@ -236,8 +216,8 @@ static int tse_init_rx_buffer(struct altera_tse_private *priv, static void tse_free_rx_buffer(struct altera_tse_private *priv, struct tse_buffer *rxbuffer) { - struct sk_buff *skb = rxbuffer->skb; dma_addr_t dma_addr = rxbuffer->dma_addr; + struct sk_buff *skb = rxbuffer->skb; if (skb != NULL) { if (dma_addr) @@ -358,6 +338,7 @@ static inline void tse_rx_vlan(struct net_device *dev, struct sk_buff *skb) { struct ethhdr *eth_hdr; u16 vid; + if ((dev->features & NETIF_F_HW_VLAN_CTAG_RX) && !__vlan_get_tag(skb, &vid)) { eth_hdr = (struct ethhdr *)skb->data; @@ -371,10 +352,10 @@ static inline void tse_rx_vlan(struct net_device *dev, struct sk_buff *skb) */ static int tse_rx(struct altera_tse_private *priv, int limit) { - unsigned int count = 0; + unsigned int entry = priv->rx_cons % priv->rx_ring_size; unsigned int next_entry; + unsigned int count = 0; struct sk_buff *skb; - unsigned int entry = priv->rx_cons % priv->rx_ring_size; u32 rxstatus; u16 pktlength; u16 pktstatus; @@ -448,10 +429,10 @@ static int tse_rx(struct altera_tse_private *priv, int limit) static int tse_tx_complete(struct altera_tse_private *priv) { unsigned int txsize = priv->tx_ring_size; - u32 ready; - unsigned int entry; struct tse_buffer *tx_buff; + unsigned int entry; int txcomplete = 0; + u32 ready; spin_lock(&priv->tx_lock); @@ -497,8 +478,8 @@ static int tse_poll(struct napi_struct *napi, int budget) { struct altera_tse_private *priv = container_of(napi, struct altera_tse_private, napi); - int rxcomplete = 0; unsigned long int flags; + int rxcomplete = 0; tse_tx_complete(priv); @@ -561,13 +542,13 @@ static irqreturn_t altera_isr(int irq, void *dev_id) static netdev_tx_t tse_start_xmit(struct sk_buff *skb, struct net_device *dev) { struct altera_tse_private *priv = netdev_priv(dev); + unsigned int nopaged_len = skb_headlen(skb); unsigned int txsize = priv->tx_ring_size; - unsigned int entry; - struct tse_buffer *buffer = NULL; int nfrags = skb_shinfo(skb)->nr_frags; - unsigned int nopaged_len = skb_headlen(skb); + struct tse_buffer *buffer = NULL; netdev_tx_t ret = NETDEV_TX_OK; dma_addr_t dma_addr; + unsigned int entry; spin_lock_bh(&priv->tx_lock); @@ -619,117 +600,6 @@ out: return ret; } -/* Called every time the controller might need to be made - * aware of new link state. The PHY code conveys this - * information through variables in the phydev structure, and this - * function converts those variables into the appropriate - * register values, and can bring down the device if needed. - */ -static void altera_tse_adjust_link(struct net_device *dev) -{ - struct altera_tse_private *priv = netdev_priv(dev); - struct phy_device *phydev = dev->phydev; - int new_state = 0; - - /* only change config if there is a link */ - spin_lock(&priv->mac_cfg_lock); - if (phydev->link) { - /* Read old config */ - u32 cfg_reg = ioread32(&priv->mac_dev->command_config); - - /* Check duplex */ - if (phydev->duplex != priv->oldduplex) { - new_state = 1; - if (!(phydev->duplex)) - cfg_reg |= MAC_CMDCFG_HD_ENA; - else - cfg_reg &= ~MAC_CMDCFG_HD_ENA; - - netdev_dbg(priv->dev, "%s: Link duplex = 0x%x\n", - dev->name, phydev->duplex); - - priv->oldduplex = phydev->duplex; - } - - /* Check speed */ - if (phydev->speed != priv->oldspeed) { - new_state = 1; - switch (phydev->speed) { - case 1000: - cfg_reg |= MAC_CMDCFG_ETH_SPEED; - cfg_reg &= ~MAC_CMDCFG_ENA_10; - break; - case 100: - cfg_reg &= ~MAC_CMDCFG_ETH_SPEED; - cfg_reg &= ~MAC_CMDCFG_ENA_10; - break; - case 10: - cfg_reg &= ~MAC_CMDCFG_ETH_SPEED; - cfg_reg |= MAC_CMDCFG_ENA_10; - break; - default: - if (netif_msg_link(priv)) - netdev_warn(dev, "Speed (%d) is not 10/100/1000!\n", - phydev->speed); - break; - } - priv->oldspeed = phydev->speed; - } - iowrite32(cfg_reg, &priv->mac_dev->command_config); - - if (!priv->oldlink) { - new_state = 1; - priv->oldlink = 1; - } - } else if (priv->oldlink) { - new_state = 1; - priv->oldlink = 0; - priv->oldspeed = 0; - priv->oldduplex = -1; - } - - if (new_state && netif_msg_link(priv)) - phy_print_status(phydev); - - spin_unlock(&priv->mac_cfg_lock); -} -static struct phy_device *connect_local_phy(struct net_device *dev) -{ - struct altera_tse_private *priv = netdev_priv(dev); - struct phy_device *phydev = NULL; - char phy_id_fmt[MII_BUS_ID_SIZE + 3]; - - if (priv->phy_addr != POLL_PHY) { - snprintf(phy_id_fmt, MII_BUS_ID_SIZE + 3, PHY_ID_FMT, - priv->mdio->id, priv->phy_addr); - - netdev_dbg(dev, "trying to attach to %s\n", phy_id_fmt); - - phydev = phy_connect(dev, phy_id_fmt, &altera_tse_adjust_link, - priv->phy_iface); - if (IS_ERR(phydev)) { - netdev_err(dev, "Could not attach to PHY\n"); - phydev = NULL; - } - - } else { - int ret; - phydev = phy_find_first(priv->mdio); - if (phydev == NULL) { - netdev_err(dev, "No PHY found\n"); - return phydev; - } - - ret = phy_connect_direct(dev, phydev, &altera_tse_adjust_link, - priv->phy_iface); - if (ret != 0) { - netdev_err(dev, "Could not attach to PHY\n"); - phydev = NULL; - } - } - return phydev; -} - static int altera_tse_phy_get_addr_mdio_create(struct net_device *dev) { struct altera_tse_private *priv = netdev_priv(dev); @@ -768,91 +638,6 @@ static int altera_tse_phy_get_addr_mdio_create(struct net_device *dev) return 0; } -/* Initialize driver's PHY state, and attach to the PHY - */ -static int init_phy(struct net_device *dev) -{ - struct altera_tse_private *priv = netdev_priv(dev); - struct phy_device *phydev; - struct device_node *phynode; - bool fixed_link = false; - int rc = 0; - - /* Avoid init phy in case of no phy present */ - if (!priv->phy_iface) - return 0; - - priv->oldlink = 0; - priv->oldspeed = 0; - priv->oldduplex = -1; - - phynode = of_parse_phandle(priv->device->of_node, "phy-handle", 0); - - if (!phynode) { - /* check if a fixed-link is defined in device-tree */ - if (of_phy_is_fixed_link(priv->device->of_node)) { - rc = of_phy_register_fixed_link(priv->device->of_node); - if (rc < 0) { - netdev_err(dev, "cannot register fixed PHY\n"); - return rc; - } - - /* In the case of a fixed PHY, the DT node associated - * to the PHY is the Ethernet MAC DT node. - */ - phynode = of_node_get(priv->device->of_node); - fixed_link = true; - - netdev_dbg(dev, "fixed-link detected\n"); - phydev = of_phy_connect(dev, phynode, - &altera_tse_adjust_link, - 0, priv->phy_iface); - } else { - netdev_dbg(dev, "no phy-handle found\n"); - if (!priv->mdio) { - netdev_err(dev, "No phy-handle nor local mdio specified\n"); - return -ENODEV; - } - phydev = connect_local_phy(dev); - } - } else { - netdev_dbg(dev, "phy-handle found\n"); - phydev = of_phy_connect(dev, phynode, - &altera_tse_adjust_link, 0, priv->phy_iface); - } - of_node_put(phynode); - - if (!phydev) { - netdev_err(dev, "Could not find the PHY\n"); - if (fixed_link) - of_phy_deregister_fixed_link(priv->device->of_node); - return -ENODEV; - } - - /* Stop Advertising 1000BASE Capability if interface is not GMII - */ - if ((priv->phy_iface == PHY_INTERFACE_MODE_MII) || - (priv->phy_iface == PHY_INTERFACE_MODE_RMII)) - phy_set_max_speed(phydev, SPEED_100); - - /* Broken HW is sometimes missing the pull-up resistor on the - * MDIO line, which results in reads to non-existent devices returning - * 0 rather than 0xffff. Catch this here and treat 0 as a non-existent - * device as well. If a fixed-link is used the phy_id is always 0. - * Note: phydev->phy_id is the result of reading the UID PHY registers. - */ - if ((phydev->phy_id == 0) && !fixed_link) { - netdev_err(dev, "Bad PHY UID 0x%08x\n", phydev->phy_id); - phy_disconnect(phydev); - return -ENODEV; - } - - netdev_dbg(dev, "attached to PHY %d UID 0x%08x Link = %d\n", - phydev->mdio.addr, phydev->phy_id, phydev->link); - - return 0; -} - static void tse_update_mac_addr(struct altera_tse_private *priv, const u8 *addr) { u32 msb; @@ -1012,8 +797,8 @@ static int tse_change_mtu(struct net_device *dev, int new_mtu) static void altera_tse_set_mcfilter(struct net_device *dev) { struct altera_tse_private *priv = netdev_priv(dev); - int i; struct netdev_hw_addr *ha; + int i; /* clear the hash filter */ for (i = 0; i < 64; i++) @@ -1087,74 +872,14 @@ static void tse_set_rx_mode(struct net_device *dev) spin_unlock(&priv->mac_cfg_lock); } -/* Initialise (if necessary) the SGMII PCS component - */ -static int init_sgmii_pcs(struct net_device *dev) -{ - struct altera_tse_private *priv = netdev_priv(dev); - int n; - unsigned int tmp_reg = 0; - - if (priv->phy_iface != PHY_INTERFACE_MODE_SGMII) - return 0; /* Nothing to do, not in SGMII mode */ - - /* The TSE SGMII PCS block looks a little like a PHY, it is - * mapped into the zeroth MDIO space of the MAC and it has - * ID registers like a PHY would. Sadly this is often - * configured to zeroes, so don't be surprised if it does - * show 0x00000000. - */ - - if (sgmii_pcs_scratch_test(priv, 0x0000) && - sgmii_pcs_scratch_test(priv, 0xffff) && - sgmii_pcs_scratch_test(priv, 0xa5a5) && - sgmii_pcs_scratch_test(priv, 0x5a5a)) { - netdev_info(dev, "PCS PHY ID: 0x%04x%04x\n", - sgmii_pcs_read(priv, MII_PHYSID1), - sgmii_pcs_read(priv, MII_PHYSID2)); - } else { - netdev_err(dev, "SGMII PCS Scratch memory test failed.\n"); - return -ENOMEM; - } - - /* Starting on page 5-29 of the MegaCore Function User Guide - * Set SGMII Link timer to 1.6ms - */ - sgmii_pcs_write(priv, SGMII_PCS_LINK_TIMER_0, 0x0D40); - sgmii_pcs_write(priv, SGMII_PCS_LINK_TIMER_1, 0x03); - - /* Enable SGMII Interface and Enable SGMII Auto Negotiation */ - sgmii_pcs_write(priv, SGMII_PCS_IF_MODE, 0x3); - - /* Enable Autonegotiation */ - tmp_reg = sgmii_pcs_read(priv, MII_BMCR); - tmp_reg |= (BMCR_SPEED1000 | BMCR_FULLDPLX | BMCR_ANENABLE); - sgmii_pcs_write(priv, MII_BMCR, tmp_reg); - - /* Reset PCS block */ - tmp_reg |= BMCR_RESET; - sgmii_pcs_write(priv, MII_BMCR, tmp_reg); - for (n = 0; n < SGMII_PCS_SW_RESET_TIMEOUT; n++) { - if (!(sgmii_pcs_read(priv, MII_BMCR) & BMCR_RESET)) { - netdev_info(dev, "SGMII PCS block initialised OK\n"); - return 0; - } - udelay(1); - } - - /* We failed to reset the block, return a timeout */ - netdev_err(dev, "SGMII PCS block reset failed.\n"); - return -ETIMEDOUT; -} - /* Open and initialize the interface */ static int tse_open(struct net_device *dev) { struct altera_tse_private *priv = netdev_priv(dev); + unsigned long flags; int ret = 0; int i; - unsigned long int flags; /* Reset and configure TSE MAC and probe associated PHY */ ret = priv->dmaops->init_dma(priv); @@ -1171,14 +896,6 @@ static int tse_open(struct net_device *dev) netdev_warn(dev, "TSE revision %x\n", priv->revision); spin_lock(&priv->mac_cfg_lock); - /* no-op if MAC not operating in SGMII mode*/ - ret = init_sgmii_pcs(dev); - if (ret) { - netdev_err(dev, - "Cannot init the SGMII PCS (error: %d)\n", ret); - spin_unlock(&priv->mac_cfg_lock); - goto phy_error; - } ret = reset_mac(priv); /* Note that reset_mac will fail if the clocks are gated by the PHY @@ -1236,8 +953,12 @@ static int tse_open(struct net_device *dev) spin_unlock_irqrestore(&priv->rxdma_irq_lock, flags); - if (dev->phydev) - phy_start(dev->phydev); + ret = phylink_of_phy_connect(priv->phylink, priv->device->of_node, 0); + if (ret) { + netdev_err(dev, "could not connect phylink (%d)\n", ret); + goto tx_request_irq_error; + } + phylink_start(priv->phylink); napi_enable(&priv->napi); netif_start_queue(dev); @@ -1265,13 +986,10 @@ phy_error: static int tse_shutdown(struct net_device *dev) { struct altera_tse_private *priv = netdev_priv(dev); - int ret; unsigned long int flags; + int ret; - /* Stop the PHY */ - if (dev->phydev) - phy_stop(dev->phydev); - + phylink_stop(priv->phylink); netif_stop_queue(dev); napi_disable(&priv->napi); @@ -1317,11 +1035,79 @@ static struct net_device_ops altera_tse_netdev_ops = { .ndo_validate_addr = eth_validate_addr, }; +static void alt_tse_mac_an_restart(struct phylink_config *config) +{ +} + +static void alt_tse_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 altera_tse_private *priv = netdev_priv(ndev); + + spin_lock(&priv->mac_cfg_lock); + reset_mac(priv); + tse_set_mac(priv, true); + spin_unlock(&priv->mac_cfg_lock); +} + +static void alt_tse_mac_link_down(struct phylink_config *config, + unsigned int mode, phy_interface_t interface) +{ +} + +static void alt_tse_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 net_device *ndev = to_net_dev(config->dev); + struct altera_tse_private *priv = netdev_priv(ndev); + u32 ctrl; + + ctrl = csrrd32(priv->mac_dev, tse_csroffs(command_config)); + ctrl &= ~(MAC_CMDCFG_ENA_10 | MAC_CMDCFG_ETH_SPEED | MAC_CMDCFG_HD_ENA); + + if (duplex == DUPLEX_HALF) + ctrl |= MAC_CMDCFG_HD_ENA; + + if (speed == SPEED_1000) + ctrl |= MAC_CMDCFG_ETH_SPEED; + else if (speed == SPEED_10) + ctrl |= MAC_CMDCFG_ENA_10; + + spin_lock(&priv->mac_cfg_lock); + csrwr32(ctrl, priv->mac_dev, tse_csroffs(command_config)); + spin_unlock(&priv->mac_cfg_lock); +} + +static struct phylink_pcs *alt_tse_select_pcs(struct phylink_config *config, + phy_interface_t interface) +{ + struct net_device *ndev = to_net_dev(config->dev); + struct altera_tse_private *priv = netdev_priv(ndev); + + if (interface == PHY_INTERFACE_MODE_SGMII || + interface == PHY_INTERFACE_MODE_1000BASEX) + return priv->pcs; + else + return NULL; +} + +static const struct phylink_mac_ops alt_tse_phylink_ops = { + .validate = phylink_generic_validate, + .mac_an_restart = alt_tse_mac_an_restart, + .mac_config = alt_tse_mac_config, + .mac_link_down = alt_tse_mac_link_down, + .mac_link_up = alt_tse_mac_link_up, + .mac_select_pcs = alt_tse_select_pcs, +}; + static int request_and_map(struct platform_device *pdev, const char *name, struct resource **res, void __iomem **ptr) { - struct resource *region; struct device *device = &pdev->dev; + struct resource *region; *res = platform_get_resource_byname(pdev, IORESOURCE_MEM, name); if (*res == NULL) { @@ -1350,13 +1136,15 @@ static int request_and_map(struct platform_device *pdev, const char *name, */ static int altera_tse_probe(struct platform_device *pdev) { - struct net_device *ndev; - int ret = -ENODEV; + const struct of_device_id *of_id = NULL; + struct altera_tse_private *priv; struct resource *control_port; struct resource *dma_res; - struct altera_tse_private *priv; + struct resource *pcs_res; + struct net_device *ndev; void __iomem *descmap; - const struct of_device_id *of_id = NULL; + int pcs_reg_width = 2; + int ret = -ENODEV; ndev = alloc_etherdev(sizeof(struct altera_tse_private)); if (!ndev) { @@ -1467,6 +1255,17 @@ static int altera_tse_probe(struct platform_device *pdev) if (ret) goto err_free_netdev; + /* SGMII PCS address space. The location can vary depending on how the + * IP is integrated. We can have a resource dedicated to it at a specific + * address space, but if it's not the case, we fallback to the mdiophy0 + * from the MAC's address space + */ + ret = request_and_map(pdev, "pcs", &pcs_res, + &priv->pcs_base); + if (ret) { + priv->pcs_base = priv->mac_dev + tse_csroffs(mdio_phy0); + pcs_reg_width = 4; + } /* Rx IRQ */ priv->rx_irq = platform_get_irq_byname(pdev, "rx_irq"); @@ -1566,7 +1365,7 @@ static int altera_tse_probe(struct platform_device *pdev) ndev->features |= NETIF_F_HW_VLAN_CTAG_RX; /* setup NAPI interface */ - netif_napi_add(ndev, &priv->napi, tse_poll, NAPI_POLL_WEIGHT); + netif_napi_add(ndev, &priv->napi, tse_poll); spin_lock_init(&priv->mac_cfg_lock); spin_lock_init(&priv->tx_lock); @@ -1590,11 +1389,32 @@ static int altera_tse_probe(struct platform_device *pdev) (unsigned long) control_port->start, priv->rx_irq, priv->tx_irq); - ret = init_phy(ndev); - if (ret != 0) { - netdev_err(ndev, "Cannot attach to PHY (error: %d)\n", ret); + priv->pcs = alt_tse_pcs_create(ndev, priv->pcs_base, pcs_reg_width); + + priv->phylink_config.dev = &ndev->dev; + priv->phylink_config.type = PHYLINK_NETDEV; + priv->phylink_config.mac_capabilities = MAC_SYM_PAUSE | MAC_10 | + MAC_100 | MAC_1000FD; + + phy_interface_set_rgmii(priv->phylink_config.supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_MII, + priv->phylink_config.supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_GMII, + priv->phylink_config.supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_SGMII, + priv->phylink_config.supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_1000BASEX, + priv->phylink_config.supported_interfaces); + + priv->phylink = phylink_create(&priv->phylink_config, + of_fwnode_handle(priv->device->of_node), + priv->phy_iface, &alt_tse_phylink_ops); + if (IS_ERR(priv->phylink)) { + dev_err(&pdev->dev, "failed to create phylink\n"); + ret = PTR_ERR(priv->phylink); goto err_init_phy; } + return 0; err_init_phy: @@ -1614,16 +1434,10 @@ static int altera_tse_remove(struct platform_device *pdev) struct net_device *ndev = platform_get_drvdata(pdev); struct altera_tse_private *priv = netdev_priv(ndev); - if (ndev->phydev) { - phy_disconnect(ndev->phydev); - - if (of_phy_is_fixed_link(priv->device->of_node)) - of_phy_deregister_fixed_link(priv->device->of_node); - } - platform_set_drvdata(pdev, NULL); altera_tse_mdio_destroy(ndev); unregister_netdev(ndev); + phylink_destroy(priv->phylink); free_netdev(ndev); return 0; diff --git a/drivers/net/ethernet/amazon/ena/ena_ethtool.c b/drivers/net/ethernet/amazon/ena/ena_ethtool.c index 39242c5a1729..98d6386b7f39 100644 --- a/drivers/net/ethernet/amazon/ena/ena_ethtool.c +++ b/drivers/net/ethernet/amazon/ena/ena_ethtool.c @@ -462,8 +462,8 @@ static void ena_get_drvinfo(struct net_device *dev, { struct ena_adapter *adapter = netdev_priv(dev); - strlcpy(info->driver, DRV_MODULE_NAME, sizeof(info->driver)); - strlcpy(info->bus_info, pci_name(adapter->pdev), + strscpy(info->driver, DRV_MODULE_NAME, sizeof(info->driver)); + strscpy(info->bus_info, pci_name(adapter->pdev), sizeof(info->bus_info)); } diff --git a/drivers/net/ethernet/amazon/ena/ena_netdev.c b/drivers/net/ethernet/amazon/ena/ena_netdev.c index 6a356a6cee15..d350eeec8bad 100644 --- a/drivers/net/ethernet/amazon/ena/ena_netdev.c +++ b/drivers/net/ethernet/amazon/ena/ena_netdev.c @@ -2265,10 +2265,8 @@ static void ena_init_napi_in_range(struct ena_adapter *adapter, for (i = first_index; i < first_index + count; i++) { struct ena_napi *napi = &adapter->ena_napi[i]; - netif_napi_add(adapter->netdev, - &napi->napi, - ENA_IS_XDP_INDEX(adapter, i) ? ena_xdp_io_poll : ena_io_poll, - NAPI_POLL_WEIGHT); + netif_napi_add(adapter->netdev, &napi->napi, + ENA_IS_XDP_INDEX(adapter, i) ? ena_xdp_io_poll : ena_io_poll); if (!ENA_IS_XDP_INDEX(adapter, i)) { napi->rx_ring = &adapter->rx_ring[i]; @@ -3166,7 +3164,7 @@ static void ena_config_host_info(struct ena_com_dev *ena_dev, struct pci_dev *pd host_info->bdf = (pdev->bus->number << 8) | pdev->devfn; host_info->os_type = ENA_ADMIN_OS_LINUX; host_info->kernel_ver = LINUX_VERSION_CODE; - strlcpy(host_info->kernel_ver_str, utsname()->version, + strscpy(host_info->kernel_ver_str, utsname()->version, sizeof(host_info->kernel_ver_str) - 1); host_info->os_dist = 0; strncpy(host_info->os_dist_str, utsname()->release, diff --git a/drivers/net/ethernet/amd/a2065.c b/drivers/net/ethernet/amd/a2065.c index 3a351d3396bf..68983b717145 100644 --- a/drivers/net/ethernet/amd/a2065.c +++ b/drivers/net/ethernet/amd/a2065.c @@ -695,7 +695,7 @@ static int a2065_init_one(struct zorro_dev *z, } dev = alloc_etherdev(sizeof(struct lance_private)); - if (dev == NULL) { + if (!dev) { release_mem_region(base_addr, sizeof(struct lance_regs)); release_mem_region(mem_start, A2065_RAM_SIZE); return -ENOMEM; diff --git a/drivers/net/ethernet/amd/amd8111e.c b/drivers/net/ethernet/amd/amd8111e.c index 5d1baa01360f..ea6cfc2095e1 100644 --- a/drivers/net/ethernet/amd/amd8111e.c +++ b/drivers/net/ethernet/amd/amd8111e.c @@ -43,7 +43,7 @@ Revision History: 3.0.4 12/09/2003 1. Added set_mac_address routine for bonding driver support. 2. Tested the driver for bonding support - 3. Bug fix: Fixed mismach in actual receive buffer lenth and lenth + 3. Bug fix: Fixed mismach in actual receive buffer length and length indicated to the h/w. 4. Modified amd8111e_rx() routine to receive all the received packets in the first interrupt. @@ -185,24 +185,23 @@ static void amd8111e_set_ext_phy(struct net_device *dev) advert = amd8111e_mdio_read(dev, lp->ext_phy_addr, MII_ADVERTISE); tmp = advert & ~(ADVERTISE_ALL | ADVERTISE_100BASE4); switch (lp->ext_phy_option) { - - default: - case SPEED_AUTONEG: /* advertise all values */ - tmp |= (ADVERTISE_10HALF | ADVERTISE_10FULL | - ADVERTISE_100HALF | ADVERTISE_100FULL); - break; - case SPEED10_HALF: - tmp |= ADVERTISE_10HALF; - break; - case SPEED10_FULL: - tmp |= ADVERTISE_10FULL; - break; - case SPEED100_HALF: - tmp |= ADVERTISE_100HALF; - break; - case SPEED100_FULL: - tmp |= ADVERTISE_100FULL; - break; + default: + case SPEED_AUTONEG: /* advertise all values */ + tmp |= (ADVERTISE_10HALF | ADVERTISE_10FULL | + ADVERTISE_100HALF | ADVERTISE_100FULL); + break; + case SPEED10_HALF: + tmp |= ADVERTISE_10HALF; + break; + case SPEED10_FULL: + tmp |= ADVERTISE_10FULL; + break; + case SPEED100_HALF: + tmp |= ADVERTISE_100HALF; + break; + case SPEED100_FULL: + tmp |= ADVERTISE_100FULL; + break; } if(advert != tmp) @@ -237,7 +236,7 @@ static int amd8111e_free_skbs(struct net_device *dev) /* Freeing previously allocated receive buffers */ for (i = 0; i < NUM_RX_BUFFERS; i++) { rx_skbuff = lp->rx_skbuff[i]; - if (rx_skbuff != NULL) { + if (rx_skbuff) { dma_unmap_single(&lp->pci_dev->dev, lp->rx_dma_addr[i], lp->rx_buff_len - 2, DMA_FROM_DEVICE); @@ -1084,7 +1083,7 @@ static irqreturn_t amd8111e_interrupt(int irq, void *dev_id) unsigned int intr0, intren0; unsigned int handled = 1; - if (unlikely(dev == NULL)) + if (unlikely(!dev)) return IRQ_NONE; spin_lock(&lp->lock); @@ -1109,7 +1108,7 @@ static irqreturn_t amd8111e_interrupt(int irq, void *dev_id) /* Check if Receive Interrupt has occurred. */ if (intr0 & RINT0) { if (napi_schedule_prep(&lp->napi)) { - /* Disable receive interupts */ + /* Disable receive interrupts */ writel(RINTEN0, mmio + INTEN0); /* Schedule a polling routine */ __napi_schedule(&lp->napi); @@ -1364,10 +1363,10 @@ static void amd8111e_get_drvinfo(struct net_device *dev, { struct amd8111e_priv *lp = netdev_priv(dev); struct pci_dev *pci_dev = lp->pci_dev; - strlcpy(info->driver, MODULE_NAME, sizeof(info->driver)); + strscpy(info->driver, MODULE_NAME, sizeof(info->driver)); snprintf(info->fw_version, sizeof(info->fw_version), "%u", chip_version); - strlcpy(info->bus_info, pci_name(pci_dev), sizeof(info->bus_info)); + strscpy(info->bus_info, pci_name(pci_dev), sizeof(info->bus_info)); } static int amd8111e_get_regs_len(struct net_device *dev) @@ -1554,7 +1553,7 @@ static int amd8111e_enable_magicpkt(struct amd8111e_priv *lp) static int amd8111e_enable_link_change(struct amd8111e_priv *lp) { - /* Adapter is already stoped/suspended/interrupt-disabled */ + /* Adapter is already stopped/suspended/interrupt-disabled */ writel(VAL0 | LCMODE_SW, lp->mmio + CMD7); /* To eliminate PCI posting bug */ diff --git a/drivers/net/ethernet/amd/amd8111e.h b/drivers/net/ethernet/amd/amd8111e.h index 37da79da5f5e..9d570adb295b 100644 --- a/drivers/net/ethernet/amd/amd8111e.h +++ b/drivers/net/ethernet/amd/amd8111e.h @@ -600,7 +600,7 @@ typedef enum { #define CSTATE 1 #define SSTATE 2 -/* Assume contoller gets data 10 times the maximum processing time */ +/* Assume controller gets data 10 times the maximum processing time */ #define REPEAT_CNT 10 /* amd8111e descriptor flag definitions */ diff --git a/drivers/net/ethernet/amd/ariadne.c b/drivers/net/ethernet/amd/ariadne.c index 4ea7b9f3c424..38153e633231 100644 --- a/drivers/net/ethernet/amd/ariadne.c +++ b/drivers/net/ethernet/amd/ariadne.c @@ -193,7 +193,7 @@ static int ariadne_rx(struct net_device *dev) struct sk_buff *skb; skb = netdev_alloc_skb(dev, pkt_len + 2); - if (skb == NULL) { + if (!skb) { for (i = 0; i < RX_RING_SIZE; i++) if (lowb(priv->rx_ring[(entry + i) % RX_RING_SIZE]->RMD1) & RF_OWN) break; @@ -731,7 +731,7 @@ static int ariadne_init_one(struct zorro_dev *z, } dev = alloc_etherdev(sizeof(struct ariadne_private)); - if (dev == NULL) { + if (!dev) { release_mem_region(base_addr, sizeof(struct Am79C960)); release_mem_region(mem_start, ARIADNE_RAM_SIZE); return -ENOMEM; diff --git a/drivers/net/ethernet/amd/atarilance.c b/drivers/net/ethernet/amd/atarilance.c index 27869164c6e6..3222c48ce6ae 100644 --- a/drivers/net/ethernet/amd/atarilance.c +++ b/drivers/net/ethernet/amd/atarilance.c @@ -581,15 +581,15 @@ static unsigned long __init lance_probe1( struct net_device *dev, /* Get the ethernet address */ switch( lp->cardtype ) { - case OLD_RIEBL: + case OLD_RIEBL: /* No ethernet address! (Set some default address) */ eth_hw_addr_set(dev, OldRieblDefHwaddr); break; - case NEW_RIEBL: + case NEW_RIEBL: lp->memcpy_f(addr, RIEBL_HWADDR_ADDR, ETH_ALEN); eth_hw_addr_set(dev, addr); break; - case PAM_CARD: + case PAM_CARD: i = IO->eeprom; for( i = 0; i < 6; ++i ) addr[i] = @@ -854,7 +854,7 @@ static irqreturn_t lance_interrupt( int irq, void *dev_id ) int csr0, boguscnt = 10; int handled = 0; - if (dev == NULL) { + if (!dev) { DPRINTK( 1, ( "lance_interrupt(): interrupt for unknown device.\n" )); return IRQ_NONE; } @@ -995,7 +995,7 @@ static int lance_rx( struct net_device *dev ) } else { skb = netdev_alloc_skb(dev, pkt_len + 2); - if (skb == NULL) { + if (!skb) { for( i = 0; i < RX_RING_SIZE; i++ ) if (MEM->rx_head[(entry+i) & RX_RING_MOD_MASK].flag & RMD1_OWN_CHIP) diff --git a/drivers/net/ethernet/amd/au1000_eth.c b/drivers/net/ethernet/amd/au1000_eth.c index d5f2c6989221..c5cec4e79489 100644 --- a/drivers/net/ethernet/amd/au1000_eth.c +++ b/drivers/net/ethernet/amd/au1000_eth.c @@ -650,7 +650,7 @@ au1000_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { struct au1000_private *aup = netdev_priv(dev); - strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); + strscpy(info->driver, DRV_NAME, sizeof(info->driver)); snprintf(info->bus_info, sizeof(info->bus_info), "%s %d", DRV_NAME, aup->mac_id); } @@ -786,7 +786,7 @@ static int au1000_rx(struct net_device *dev) frmlen = (status & RX_FRAME_LEN_MASK); frmlen -= 4; /* Remove FCS */ skb = netdev_alloc_skb(dev, frmlen + 2); - if (skb == NULL) { + if (!skb) { dev->stats.rx_dropped++; continue; } @@ -1199,7 +1199,7 @@ static int au1000_probe(struct platform_device *pdev) } aup->mii_bus = mdiobus_alloc(); - if (aup->mii_bus == NULL) { + if (!aup->mii_bus) { dev_err(&pdev->dev, "failed to allocate mdiobus structure\n"); err = -ENOMEM; goto err_mdiobus_alloc; @@ -1284,7 +1284,7 @@ static int au1000_probe(struct platform_device *pdev) return 0; err_out: - if (aup->mii_bus != NULL) + if (aup->mii_bus) mdiobus_unregister(aup->mii_bus); /* here we should have a valid dev plus aup-> register addresses diff --git a/drivers/net/ethernet/amd/lance.c b/drivers/net/ethernet/amd/lance.c index 462016666752..fb8686214a32 100644 --- a/drivers/net/ethernet/amd/lance.c +++ b/drivers/net/ethernet/amd/lance.c @@ -880,7 +880,7 @@ lance_init_ring(struct net_device *dev, gfp_t gfp) rx_buff = skb->data; else rx_buff = kmalloc(PKT_BUF_SZ, GFP_DMA | gfp); - if (rx_buff == NULL) + if (!rx_buff) lp->rx_ring[i].base = 0; else lp->rx_ring[i].base = (u32)isa_virt_to_bus(rx_buff) | 0x80000000; @@ -1186,7 +1186,7 @@ lance_rx(struct net_device *dev) else { skb = dev_alloc_skb(pkt_len+2); - if (skb == NULL) + if (!skb) { printk("%s: Memory squeeze, deferring packet.\n", dev->name); for (i=0; i < RX_RING_SIZE; i++) diff --git a/drivers/net/ethernet/amd/nmclan_cs.c b/drivers/net/ethernet/amd/nmclan_cs.c index 30ee5329bd7c..823a329a921f 100644 --- a/drivers/net/ethernet/amd/nmclan_cs.c +++ b/drivers/net/ethernet/amd/nmclan_cs.c @@ -485,10 +485,10 @@ static int mace_read(mace_private *lp, unsigned int ioaddr, int reg) unsigned long flags; switch (reg >> 4) { - case 0: /* register 0-15 */ + case 0: /* register 0-15 */ data = inb(ioaddr + AM2150_MACE_BASE + reg); break; - case 1: /* register 16-31 */ + case 1: /* register 16-31 */ spin_lock_irqsave(&lp->bank_lock, flags); MACEBANK(1); data = inb(ioaddr + AM2150_MACE_BASE + (reg & 0x0F)); @@ -512,10 +512,10 @@ static void mace_write(mace_private *lp, unsigned int ioaddr, int reg, unsigned long flags; switch (reg >> 4) { - case 0: /* register 0-15 */ + case 0: /* register 0-15 */ outb(data & 0xFF, ioaddr + AM2150_MACE_BASE + reg); break; - case 1: /* register 16-31 */ + case 1: /* register 16-31 */ spin_lock_irqsave(&lp->bank_lock, flags); MACEBANK(1); outb(data & 0xFF, ioaddr + AM2150_MACE_BASE + (reg & 0x0F)); @@ -567,13 +567,13 @@ static int mace_init(mace_private *lp, unsigned int ioaddr, * Or just set ASEL in PHYCC below! */ switch (if_port) { - case 1: + case 1: mace_write(lp, ioaddr, MACE_PLSCC, 0x02); break; - case 2: + case 2: mace_write(lp, ioaddr, MACE_PLSCC, 0x00); break; - default: + default: mace_write(lp, ioaddr, MACE_PHYCC, /* ASEL */ 4); /* ASEL Auto Select. When set, the PORTSEL[1-0] bits are overridden, and the MACE device will automatically select the operating media @@ -815,7 +815,7 @@ static int mace_close(struct net_device *dev) static void netdev_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { - strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); + strscpy(info->driver, DRV_NAME, sizeof(info->driver)); snprintf(info->bus_info, sizeof(info->bus_info), "PCMCIA 0x%lx", dev->base_addr); } @@ -918,7 +918,7 @@ static irqreturn_t mace_interrupt(int irq, void *dev_id) int status; int IntrCnt = MACE_MAX_IR_ITERATIONS; - if (dev == NULL) { + if (!dev) { pr_debug("mace_interrupt(): irq 0x%X for unknown device.\n", irq); return IRQ_NONE; @@ -1102,7 +1102,7 @@ static int mace_rx(struct net_device *dev, unsigned char RxCnt) skb = netdev_alloc_skb(dev, pkt_len + 2); - if (skb != NULL) { + if (skb) { skb_reserve(skb, 2); insw(ioaddr + AM2150_RCV, skb_put(skb, pkt_len), pkt_len>>1); if (pkt_len & 1) diff --git a/drivers/net/ethernet/amd/pcnet32.c b/drivers/net/ethernet/amd/pcnet32.c index b5ff47283cfe..72db9f9e7bee 100644 --- a/drivers/net/ethernet/amd/pcnet32.c +++ b/drivers/net/ethernet/amd/pcnet32.c @@ -488,7 +488,7 @@ static void pcnet32_realloc_tx_ring(struct net_device *dev, dma_alloc_coherent(&lp->pci_dev->dev, sizeof(struct pcnet32_tx_head) * entries, &new_ring_dma_addr, GFP_ATOMIC); - if (new_tx_ring == NULL) + if (!new_tx_ring) return; new_dma_addr_list = kcalloc(entries, sizeof(dma_addr_t), GFP_ATOMIC); @@ -547,7 +547,7 @@ static void pcnet32_realloc_rx_ring(struct net_device *dev, dma_alloc_coherent(&lp->pci_dev->dev, sizeof(struct pcnet32_rx_head) * entries, &new_ring_dma_addr, GFP_ATOMIC); - if (new_rx_ring == NULL) + if (!new_rx_ring) return; new_dma_addr_list = kcalloc(entries, sizeof(dma_addr_t), GFP_ATOMIC); @@ -797,9 +797,9 @@ static void pcnet32_get_drvinfo(struct net_device *dev, { struct pcnet32_private *lp = netdev_priv(dev); - strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); + strscpy(info->driver, DRV_NAME, sizeof(info->driver)); if (lp->pci_dev) - strlcpy(info->bus_info, pci_name(lp->pci_dev), + strscpy(info->bus_info, pci_name(lp->pci_dev), sizeof(info->bus_info)); else snprintf(info->bus_info, sizeof(info->bus_info), @@ -1249,7 +1249,7 @@ static void pcnet32_rx_entry(struct net_device *dev, } else skb = netdev_alloc_skb(dev, pkt_len + NET_IP_ALIGN); - if (skb == NULL) { + if (!skb) { dev->stats.rx_dropped++; return; } @@ -2018,7 +2018,7 @@ static int pcnet32_alloc_ring(struct net_device *dev, const char *name) lp->tx_ring = dma_alloc_coherent(&lp->pci_dev->dev, sizeof(struct pcnet32_tx_head) * lp->tx_ring_size, &lp->tx_ring_dma_addr, GFP_KERNEL); - if (lp->tx_ring == NULL) { + if (!lp->tx_ring) { netif_err(lp, drv, dev, "Coherent memory allocation failed\n"); return -ENOMEM; } @@ -2026,7 +2026,7 @@ static int pcnet32_alloc_ring(struct net_device *dev, const char *name) lp->rx_ring = dma_alloc_coherent(&lp->pci_dev->dev, sizeof(struct pcnet32_rx_head) * lp->rx_ring_size, &lp->rx_ring_dma_addr, GFP_KERNEL); - if (lp->rx_ring == NULL) { + if (!lp->rx_ring) { netif_err(lp, drv, dev, "Coherent memory allocation failed\n"); return -ENOMEM; } @@ -2365,7 +2365,7 @@ static int pcnet32_init_ring(struct net_device *dev) for (i = 0; i < lp->rx_ring_size; i++) { struct sk_buff *rx_skbuff = lp->rx_skbuff[i]; - if (rx_skbuff == NULL) { + if (!rx_skbuff) { lp->rx_skbuff[i] = netdev_alloc_skb(dev, PKT_BUF_SKB); rx_skbuff = lp->rx_skbuff[i]; if (!rx_skbuff) { diff --git a/drivers/net/ethernet/amd/sun3lance.c b/drivers/net/ethernet/amd/sun3lance.c index 007bd7787291..246f34c43765 100644 --- a/drivers/net/ethernet/amd/sun3lance.c +++ b/drivers/net/ethernet/amd/sun3lance.c @@ -341,7 +341,7 @@ static int __init lance_probe( struct net_device *dev) /* XXX - leak? */ MEM = dvma_malloc_align(sizeof(struct lance_memory), 0x10000); - if (MEM == NULL) { + if (!MEM) { #ifdef CONFIG_SUN3 iounmap((void __iomem *)ioaddr); #endif @@ -796,7 +796,7 @@ static int lance_rx( struct net_device *dev ) } else { skb = netdev_alloc_skb(dev, pkt_len + 2); - if (skb == NULL) { + if (!skb) { dev->stats.rx_dropped++; head->msg_length = 0; head->flag |= RMD1_OWN_CHIP; diff --git a/drivers/net/ethernet/amd/sunlance.c b/drivers/net/ethernet/amd/sunlance.c index 22d609563af8..68ca1225eedc 100644 --- a/drivers/net/ethernet/amd/sunlance.c +++ b/drivers/net/ethernet/amd/sunlance.c @@ -530,7 +530,7 @@ static void lance_rx_dvma(struct net_device *dev) len = (rd->mblength & 0xfff) - 4; skb = netdev_alloc_skb(dev, len + 2); - if (skb == NULL) { + if (!skb) { dev->stats.rx_dropped++; rd->mblength = 0; rd->rmd1_bits = LE_R1_OWN; @@ -700,7 +700,7 @@ static void lance_rx_pio(struct net_device *dev) len = (sbus_readw(&rd->mblength) & 0xfff) - 4; skb = netdev_alloc_skb(dev, len + 2); - if (skb == NULL) { + if (!skb) { dev->stats.rx_dropped++; sbus_writew(0, &rd->mblength); sbus_writeb(LE_R1_OWN, &rd->rmd1_bits); @@ -1276,7 +1276,7 @@ static void lance_free_hwresources(struct lance_private *lp) /* Ethtool support... */ static void sparc_lance_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { - strlcpy(info->driver, "sunlance", sizeof(info->driver)); + strscpy(info->driver, "sunlance", sizeof(info->driver)); } static const struct ethtool_ops sparc_lance_ethtool_ops = { diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c index f342bb853189..7b666106feee 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c @@ -952,14 +952,14 @@ static void xgbe_napi_enable(struct xgbe_prv_data *pdata, unsigned int add) channel = pdata->channel[i]; if (add) netif_napi_add(pdata->netdev, &channel->napi, - xgbe_one_poll, NAPI_POLL_WEIGHT); + xgbe_one_poll); napi_enable(&channel->napi); } } else { if (add) netif_napi_add(pdata->netdev, &pdata->napi, - xgbe_all_poll, NAPI_POLL_WEIGHT); + xgbe_all_poll); napi_enable(&pdata->napi); } diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c b/drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c index 6ceb1cdf6eba..6e83ff59172a 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c @@ -402,8 +402,8 @@ static void xgbe_get_drvinfo(struct net_device *netdev, struct xgbe_prv_data *pdata = netdev_priv(netdev); struct xgbe_hw_features *hw_feat = &pdata->hw_feat; - strlcpy(drvinfo->driver, XGBE_DRV_NAME, sizeof(drvinfo->driver)); - strlcpy(drvinfo->bus_info, dev_name(pdata->dev), + strscpy(drvinfo->driver, XGBE_DRV_NAME, sizeof(drvinfo->driver)); + strscpy(drvinfo->bus_info, dev_name(pdata->dev), sizeof(drvinfo->bus_info)); snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version), "%d.%d.%d", XGMAC_GET_BITS(hw_feat->version, MAC_VR, USERVER), diff --git a/drivers/net/ethernet/apm/xgene-v2/main.c b/drivers/net/ethernet/apm/xgene-v2/main.c index d022b6db9e06..379d19d18dbe 100644 --- a/drivers/net/ethernet/apm/xgene-v2/main.c +++ b/drivers/net/ethernet/apm/xgene-v2/main.c @@ -672,7 +672,7 @@ static int xge_probe(struct platform_device *pdev) if (ret) goto err; - netif_napi_add(ndev, &pdata->napi, xge_napi, NAPI_POLL_WEIGHT); + netif_napi_add(ndev, &pdata->napi, xge_napi); ret = register_netdev(ndev); if (ret) { diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_main.c b/drivers/net/ethernet/apm/xgene/xgene_enet_main.c index 53dc8d5fede8..d6cfea65a714 100644 --- a/drivers/net/ethernet/apm/xgene/xgene_enet_main.c +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_main.c @@ -1977,14 +1977,12 @@ static void xgene_enet_napi_add(struct xgene_enet_pdata *pdata) for (i = 0; i < pdata->rxq_cnt; i++) { napi = &pdata->rx_ring[i]->napi; - netif_napi_add(pdata->ndev, napi, xgene_enet_napi, - NAPI_POLL_WEIGHT); + netif_napi_add(pdata->ndev, napi, xgene_enet_napi); } for (i = 0; i < pdata->cq_cnt; i++) { napi = &pdata->tx_ring[i]->cp_ring->napi; - netif_napi_add(pdata->ndev, napi, xgene_enet_napi, - NAPI_POLL_WEIGHT); + netif_napi_add(pdata->ndev, napi, xgene_enet_napi); } } diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_ethtool.c b/drivers/net/ethernet/aquantia/atlantic/aq_ethtool.c index 1daecd483b8d..a08f221e30d4 100644 --- a/drivers/net/ethernet/aquantia/atlantic/aq_ethtool.c +++ b/drivers/net/ethernet/aquantia/atlantic/aq_ethtool.c @@ -238,7 +238,7 @@ static void aq_ethtool_get_drvinfo(struct net_device *ndev, "%u.%u.%u", firmware_version >> 24, (firmware_version >> 16) & 0xFFU, firmware_version & 0xFFFFU); - strlcpy(drvinfo->bus_info, pdev ? pci_name(pdev) : "", + strscpy(drvinfo->bus_info, pdev ? pci_name(pdev) : "", sizeof(drvinfo->bus_info)); drvinfo->n_stats = aq_ethtool_n_stats(ndev); drvinfo->testinfo_len = 0; diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_macsec.c b/drivers/net/ethernet/aquantia/atlantic/aq_macsec.c index 02058fe79f52..3d0e16791e1c 100644 --- a/drivers/net/ethernet/aquantia/atlantic/aq_macsec.c +++ b/drivers/net/ethernet/aquantia/atlantic/aq_macsec.c @@ -292,9 +292,6 @@ static int aq_mdo_dev_open(struct macsec_context *ctx) struct aq_nic_s *nic = netdev_priv(ctx->netdev); int ret = 0; - if (ctx->prepare) - return 0; - if (netif_carrier_ok(nic->ndev)) ret = aq_apply_secy_cfg(nic, ctx->secy); @@ -306,9 +303,6 @@ static int aq_mdo_dev_stop(struct macsec_context *ctx) struct aq_nic_s *nic = netdev_priv(ctx->netdev); int i; - if (ctx->prepare) - return 0; - for (i = 0; i < AQ_MACSEC_MAX_SC; i++) { if (nic->macsec_cfg->txsc_idx_busy & BIT(i)) aq_clear_secy(nic, nic->macsec_cfg->aq_txsc[i].sw_secy, @@ -466,9 +460,6 @@ static int aq_mdo_add_secy(struct macsec_context *ctx) if (txsc_idx == AQ_MACSEC_MAX_SC) return -ENOSPC; - if (ctx->prepare) - return 0; - cfg->sc_sa = sc_sa; cfg->aq_txsc[txsc_idx].hw_sc_idx = aq_to_hw_sc_idx(txsc_idx, sc_sa); cfg->aq_txsc[txsc_idx].sw_secy = secy; @@ -492,9 +483,6 @@ static int aq_mdo_upd_secy(struct macsec_context *ctx) if (txsc_idx < 0) return -ENOENT; - if (ctx->prepare) - return 0; - if (netif_carrier_ok(nic->ndev) && netif_running(secy->netdev)) ret = aq_set_txsc(nic, txsc_idx); @@ -543,9 +531,6 @@ static int aq_mdo_del_secy(struct macsec_context *ctx) struct aq_nic_s *nic = netdev_priv(ctx->netdev); int ret = 0; - if (ctx->prepare) - return 0; - if (!nic->macsec_cfg) return 0; @@ -601,9 +586,6 @@ static int aq_mdo_add_txsa(struct macsec_context *ctx) if (txsc_idx < 0) return -EINVAL; - if (ctx->prepare) - return 0; - aq_txsc = &cfg->aq_txsc[txsc_idx]; set_bit(ctx->sa.assoc_num, &aq_txsc->tx_sa_idx_busy); @@ -631,9 +613,6 @@ static int aq_mdo_upd_txsa(struct macsec_context *ctx) if (txsc_idx < 0) return -EINVAL; - if (ctx->prepare) - return 0; - aq_txsc = &cfg->aq_txsc[txsc_idx]; if (netif_carrier_ok(nic->ndev) && netif_running(secy->netdev)) ret = aq_update_txsa(nic, aq_txsc->hw_sc_idx, secy, @@ -681,9 +660,6 @@ static int aq_mdo_del_txsa(struct macsec_context *ctx) if (txsc_idx < 0) return -EINVAL; - if (ctx->prepare) - return 0; - ret = aq_clear_txsa(nic, &cfg->aq_txsc[txsc_idx], ctx->sa.assoc_num, AQ_CLEAR_ALL); @@ -780,9 +756,6 @@ static int aq_mdo_add_rxsc(struct macsec_context *ctx) if (rxsc_idx >= rxsc_idx_max) return -ENOSPC; - if (ctx->prepare) - return 0; - cfg->aq_rxsc[rxsc_idx].hw_sc_idx = aq_to_hw_sc_idx(rxsc_idx, cfg->sc_sa); cfg->aq_rxsc[rxsc_idx].sw_secy = ctx->secy; @@ -809,9 +782,6 @@ static int aq_mdo_upd_rxsc(struct macsec_context *ctx) if (rxsc_idx < 0) return -ENOENT; - if (ctx->prepare) - return 0; - if (netif_carrier_ok(nic->ndev) && netif_running(ctx->secy->netdev)) ret = aq_set_rxsc(nic, rxsc_idx); @@ -876,9 +846,6 @@ static int aq_mdo_del_rxsc(struct macsec_context *ctx) if (rxsc_idx < 0) return -ENOENT; - if (ctx->prepare) - return 0; - if (netif_carrier_ok(nic->ndev)) clear_type = AQ_CLEAR_ALL; @@ -948,9 +915,6 @@ static int aq_mdo_add_rxsa(struct macsec_context *ctx) if (rxsc_idx < 0) return -EINVAL; - if (ctx->prepare) - return 0; - aq_rxsc = &nic->macsec_cfg->aq_rxsc[rxsc_idx]; set_bit(ctx->sa.assoc_num, &aq_rxsc->rx_sa_idx_busy); @@ -978,9 +942,6 @@ static int aq_mdo_upd_rxsa(struct macsec_context *ctx) if (rxsc_idx < 0) return -EINVAL; - if (ctx->prepare) - return 0; - if (netif_carrier_ok(nic->ndev) && netif_running(secy->netdev)) ret = aq_update_rxsa(nic, cfg->aq_rxsc[rxsc_idx].hw_sc_idx, secy, ctx->sa.rx_sa, NULL, @@ -1029,9 +990,6 @@ static int aq_mdo_del_rxsa(struct macsec_context *ctx) if (rxsc_idx < 0) return -EINVAL; - if (ctx->prepare) - return 0; - ret = aq_clear_rxsa(nic, &cfg->aq_rxsc[rxsc_idx], ctx->sa.assoc_num, AQ_CLEAR_ALL); @@ -1044,9 +1002,6 @@ static int aq_mdo_get_dev_stats(struct macsec_context *ctx) struct aq_macsec_common_stats *stats = &nic->macsec_cfg->stats; struct aq_hw_s *hw = nic->aq_hw; - if (ctx->prepare) - return 0; - aq_get_macsec_common_stats(hw, stats); ctx->stats.dev_stats->OutPktsUntagged = stats->out.untagged_pkts; @@ -1073,9 +1028,6 @@ static int aq_mdo_get_tx_sc_stats(struct macsec_context *ctx) if (txsc_idx < 0) return -ENOENT; - if (ctx->prepare) - return 0; - aq_txsc = &nic->macsec_cfg->aq_txsc[txsc_idx]; stats = &aq_txsc->stats; aq_get_txsc_stats(hw, aq_txsc->hw_sc_idx, stats); @@ -1106,9 +1058,6 @@ static int aq_mdo_get_tx_sa_stats(struct macsec_context *ctx) if (txsc_idx < 0) return -EINVAL; - if (ctx->prepare) - return 0; - aq_txsc = &cfg->aq_txsc[txsc_idx]; sa_idx = aq_txsc->hw_sc_idx | ctx->sa.assoc_num; stats = &aq_txsc->tx_sa_stats[ctx->sa.assoc_num]; @@ -1147,9 +1096,6 @@ static int aq_mdo_get_rx_sc_stats(struct macsec_context *ctx) if (rxsc_idx < 0) return -ENOENT; - if (ctx->prepare) - return 0; - aq_rxsc = &cfg->aq_rxsc[rxsc_idx]; for (i = 0; i < MACSEC_NUM_AN; i++) { if (!test_bit(i, &aq_rxsc->rx_sa_idx_busy)) @@ -1196,9 +1142,6 @@ static int aq_mdo_get_rx_sa_stats(struct macsec_context *ctx) if (rxsc_idx < 0) return -EINVAL; - if (ctx->prepare) - return 0; - aq_rxsc = &cfg->aq_rxsc[rxsc_idx]; stats = &aq_rxsc->rx_sa_stats[ctx->sa.assoc_num]; sa_idx = aq_rxsc->hw_sc_idx | ctx->sa.assoc_num; diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_ptp.c b/drivers/net/ethernet/aquantia/atlantic/aq_ptp.c index 275324c9e51e..80b44043e6c5 100644 --- a/drivers/net/ethernet/aquantia/atlantic/aq_ptp.c +++ b/drivers/net/ethernet/aquantia/atlantic/aq_ptp.c @@ -1217,8 +1217,7 @@ int aq_ptp_init(struct aq_nic_s *aq_nic, unsigned int idx_vec) atomic_set(&aq_ptp->offset_egress, 0); atomic_set(&aq_ptp->offset_ingress, 0); - netif_napi_add(aq_nic_get_ndev(aq_nic), &aq_ptp->napi, - aq_ptp_poll, NAPI_POLL_WEIGHT); + netif_napi_add(aq_nic_get_ndev(aq_nic), &aq_ptp->napi, aq_ptp_poll); aq_ptp->idx_vector = idx_vec; diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_vec.c b/drivers/net/ethernet/aquantia/atlantic/aq_vec.c index f0fdf20f01c1..f5db1c44e9b9 100644 --- a/drivers/net/ethernet/aquantia/atlantic/aq_vec.c +++ b/drivers/net/ethernet/aquantia/atlantic/aq_vec.c @@ -119,8 +119,7 @@ struct aq_vec_s *aq_vec_alloc(struct aq_nic_s *aq_nic, unsigned int idx, self->tx_rings = 0; self->rx_rings = 0; - netif_napi_add(aq_nic_get_ndev(aq_nic), &self->napi, - aq_vec_poll, NAPI_POLL_WEIGHT); + netif_napi_add(aq_nic_get_ndev(aq_nic), &self->napi, aq_vec_poll); err_exit: return self; diff --git a/drivers/net/ethernet/arc/emac_main.c b/drivers/net/ethernet/arc/emac_main.c index 288e2961823e..ba0646b3b122 100644 --- a/drivers/net/ethernet/arc/emac_main.c +++ b/drivers/net/ethernet/arc/emac_main.c @@ -91,7 +91,7 @@ static void arc_emac_get_drvinfo(struct net_device *ndev, { struct arc_emac_priv *priv = netdev_priv(ndev); - strlcpy(info->driver, priv->drv_name, sizeof(info->driver)); + strscpy(info->driver, priv->drv_name, sizeof(info->driver)); } static const struct ethtool_ops arc_emac_ethtool_ops = { diff --git a/drivers/net/ethernet/asix/ax88796c_main.c b/drivers/net/ethernet/asix/ax88796c_main.c index 6ba5b024a7be..8b7cdf015a16 100644 --- a/drivers/net/ethernet/asix/ax88796c_main.c +++ b/drivers/net/ethernet/asix/ax88796c_main.c @@ -293,7 +293,7 @@ ax88796c_tx_fixup(struct net_device *ndev, struct sk_buff_head *q) skb_put(skb, padlen); /* EOP header */ - memcpy(skb_put(skb, TX_EOP_SIZE), &info.eop, TX_EOP_SIZE); + skb_put_data(skb, &info.eop, TX_EOP_SIZE); skb_unlink(skb, q); @@ -381,7 +381,7 @@ static int ax88796c_hard_xmit(struct ax88796c_device *ax_local) return 1; } -static int +static netdev_tx_t ax88796c_start_xmit(struct sk_buff *skb, struct net_device *ndev) { struct ax88796c_device *ax_local = to_ax88796c_device(ndev); diff --git a/drivers/net/ethernet/atheros/ag71xx.c b/drivers/net/ethernet/atheros/ag71xx.c index e461f4764066..cc932b3cf873 100644 --- a/drivers/net/ethernet/atheros/ag71xx.c +++ b/drivers/net/ethernet/atheros/ag71xx.c @@ -451,8 +451,8 @@ static void ag71xx_get_drvinfo(struct net_device *ndev, { struct ag71xx *ag = netdev_priv(ndev); - strlcpy(info->driver, "ag71xx", sizeof(info->driver)); - strlcpy(info->bus_info, of_node_full_name(ag->pdev->dev.of_node), + strscpy(info->driver, "ag71xx", sizeof(info->driver)); + strscpy(info->bus_info, of_node_full_name(ag->pdev->dev.of_node), sizeof(info->bus_info)); } diff --git a/drivers/net/ethernet/atheros/alx/main.c b/drivers/net/ethernet/atheros/alx/main.c index a89b93cb4e26..d30d11872719 100644 --- a/drivers/net/ethernet/atheros/alx/main.c +++ b/drivers/net/ethernet/atheros/alx/main.c @@ -752,7 +752,7 @@ static int alx_alloc_napis(struct alx_priv *alx) goto err_out; np->alx = alx; - netif_napi_add(alx->dev, &np->napi, alx_poll, 64); + netif_napi_add(alx->dev, &np->napi, alx_poll); alx->qnapi[i] = np; } @@ -1912,11 +1912,14 @@ static int alx_suspend(struct device *dev) if (!netif_running(alx->dev)) return 0; + + rtnl_lock(); netif_device_detach(alx->dev); mutex_lock(&alx->mtx); __alx_stop(alx); mutex_unlock(&alx->mtx); + rtnl_unlock(); return 0; } @@ -1927,6 +1930,7 @@ static int alx_resume(struct device *dev) struct alx_hw *hw = &alx->hw; int err; + rtnl_lock(); mutex_lock(&alx->mtx); alx_reset_phy(hw); @@ -1943,6 +1947,7 @@ static int alx_resume(struct device *dev) unlock: mutex_unlock(&alx->mtx); + rtnl_unlock(); return err; } diff --git a/drivers/net/ethernet/atheros/atl1c/atl1c_ethtool.c b/drivers/net/ethernet/atheros/atl1c/atl1c_ethtool.c index e2eb7b8c63a0..0bce122c68f1 100644 --- a/drivers/net/ethernet/atheros/atl1c/atl1c_ethtool.c +++ b/drivers/net/ethernet/atheros/atl1c/atl1c_ethtool.c @@ -220,8 +220,8 @@ static void atl1c_get_drvinfo(struct net_device *netdev, { struct atl1c_adapter *adapter = netdev_priv(netdev); - strlcpy(drvinfo->driver, atl1c_driver_name, sizeof(drvinfo->driver)); - strlcpy(drvinfo->bus_info, pci_name(adapter->pdev), + strscpy(drvinfo->driver, atl1c_driver_name, sizeof(drvinfo->driver)); + strscpy(drvinfo->bus_info, pci_name(adapter->pdev), sizeof(drvinfo->bus_info)); } diff --git a/drivers/net/ethernet/atheros/atl1c/atl1c_main.c b/drivers/net/ethernet/atheros/atl1c/atl1c_main.c index be4b1f8eef29..40c781695d58 100644 --- a/drivers/net/ethernet/atheros/atl1c/atl1c_main.c +++ b/drivers/net/ethernet/atheros/atl1c/atl1c_main.c @@ -2732,7 +2732,7 @@ static int atl1c_probe(struct pci_dev *pdev, const struct pci_device_id *ent) dev_set_threaded(netdev, true); for (i = 0; i < adapter->rx_queue_count; ++i) netif_napi_add(netdev, &adapter->rrd_ring[i].napi, - atl1c_clean_rx, 64); + atl1c_clean_rx); for (i = 0; i < adapter->tx_queue_count; ++i) netif_napi_add_tx(netdev, &adapter->tpd_ring[i].napi, atl1c_clean_tx); diff --git a/drivers/net/ethernet/atheros/atl1e/atl1e_ethtool.c b/drivers/net/ethernet/atheros/atl1e/atl1e_ethtool.c index 0cbde352d1ba..68f1832a198d 100644 --- a/drivers/net/ethernet/atheros/atl1e/atl1e_ethtool.c +++ b/drivers/net/ethernet/atheros/atl1e/atl1e_ethtool.c @@ -306,9 +306,9 @@ static void atl1e_get_drvinfo(struct net_device *netdev, { struct atl1e_adapter *adapter = netdev_priv(netdev); - strlcpy(drvinfo->driver, atl1e_driver_name, sizeof(drvinfo->driver)); - strlcpy(drvinfo->fw_version, "L1e", sizeof(drvinfo->fw_version)); - strlcpy(drvinfo->bus_info, pci_name(adapter->pdev), + strscpy(drvinfo->driver, atl1e_driver_name, sizeof(drvinfo->driver)); + strscpy(drvinfo->fw_version, "L1e", sizeof(drvinfo->fw_version)); + strscpy(drvinfo->bus_info, pci_name(adapter->pdev), sizeof(drvinfo->bus_info)); } diff --git a/drivers/net/ethernet/atheros/atl1e/atl1e_main.c b/drivers/net/ethernet/atheros/atl1e/atl1e_main.c index 57a51fb7746c..5db0f3495a32 100644 --- a/drivers/net/ethernet/atheros/atl1e/atl1e_main.c +++ b/drivers/net/ethernet/atheros/atl1e/atl1e_main.c @@ -2354,7 +2354,7 @@ static int atl1e_probe(struct pci_dev *pdev, const struct pci_device_id *ent) adapter->mii.phy_id_mask = 0x1f; adapter->mii.reg_num_mask = MDIO_REG_ADDR_MASK; - netif_napi_add(netdev, &adapter->napi, atl1e_clean, 64); + netif_napi_add(netdev, &adapter->napi, atl1e_clean); timer_setup(&adapter->phy_config_timer, atl1e_phy_config, 0); diff --git a/drivers/net/ethernet/atheros/atlx/atl1.c b/drivers/net/ethernet/atheros/atlx/atl1.c index ff1fe09abf9f..c8444bcdf527 100644 --- a/drivers/net/ethernet/atheros/atlx/atl1.c +++ b/drivers/net/ethernet/atheros/atlx/atl1.c @@ -2977,7 +2977,7 @@ static int atl1_probe(struct pci_dev *pdev, const struct pci_device_id *ent) netdev->netdev_ops = &atl1_netdev_ops; netdev->watchdog_timeo = 5 * HZ; - netif_napi_add(netdev, &adapter->napi, atl1_rings_clean, 64); + netif_napi_add(netdev, &adapter->napi, atl1_rings_clean); netdev->ethtool_ops = &atl1_ethtool_ops; adapter->bd_number = cards_found; @@ -3340,8 +3340,8 @@ static void atl1_get_drvinfo(struct net_device *netdev, { struct atl1_adapter *adapter = netdev_priv(netdev); - strlcpy(drvinfo->driver, ATLX_DRIVER_NAME, sizeof(drvinfo->driver)); - strlcpy(drvinfo->bus_info, pci_name(adapter->pdev), + strscpy(drvinfo->driver, ATLX_DRIVER_NAME, sizeof(drvinfo->driver)); + strscpy(drvinfo->bus_info, pci_name(adapter->pdev), sizeof(drvinfo->bus_info)); } diff --git a/drivers/net/ethernet/atheros/atlx/atl2.c b/drivers/net/ethernet/atheros/atlx/atl2.c index bbc4d7b08a49..1b487c071cb6 100644 --- a/drivers/net/ethernet/atheros/atlx/atl2.c +++ b/drivers/net/ethernet/atheros/atlx/atl2.c @@ -1980,9 +1980,9 @@ static void atl2_get_drvinfo(struct net_device *netdev, { struct atl2_adapter *adapter = netdev_priv(netdev); - strlcpy(drvinfo->driver, atl2_driver_name, sizeof(drvinfo->driver)); - strlcpy(drvinfo->fw_version, "L2", sizeof(drvinfo->fw_version)); - strlcpy(drvinfo->bus_info, pci_name(adapter->pdev), + strscpy(drvinfo->driver, atl2_driver_name, sizeof(drvinfo->driver)); + strscpy(drvinfo->fw_version, "L2", sizeof(drvinfo->fw_version)); + strscpy(drvinfo->bus_info, pci_name(adapter->pdev), sizeof(drvinfo->bus_info)); } diff --git a/drivers/net/ethernet/broadcom/b44.c b/drivers/net/ethernet/broadcom/b44.c index e5857e88c207..7f876721596c 100644 --- a/drivers/net/ethernet/broadcom/b44.c +++ b/drivers/net/ethernet/broadcom/b44.c @@ -1790,13 +1790,13 @@ static void b44_get_drvinfo (struct net_device *dev, struct ethtool_drvinfo *inf struct b44 *bp = netdev_priv(dev); struct ssb_bus *bus = bp->sdev->bus; - strlcpy(info->driver, DRV_MODULE_NAME, sizeof(info->driver)); + strscpy(info->driver, DRV_MODULE_NAME, sizeof(info->driver)); switch (bus->bustype) { case SSB_BUSTYPE_PCI: - strlcpy(info->bus_info, pci_name(bus->host_pci), sizeof(info->bus_info)); + strscpy(info->bus_info, pci_name(bus->host_pci), sizeof(info->bus_info)); break; case SSB_BUSTYPE_SSB: - strlcpy(info->bus_info, "SSB", sizeof(info->bus_info)); + strscpy(info->bus_info, "SSB", sizeof(info->bus_info)); break; case SSB_BUSTYPE_PCMCIA: case SSB_BUSTYPE_SDIO: @@ -2375,7 +2375,7 @@ static int b44_init_one(struct ssb_device *sdev, bp->tx_pending = B44_DEF_TX_RING_PENDING; dev->netdev_ops = &b44_netdev_ops; - netif_napi_add(dev, &bp->napi, b44_poll, 64); + netif_napi_add(dev, &bp->napi, b44_poll); dev->watchdog_timeo = B44_TX_TIMEOUT; dev->min_mtu = B44_MIN_MTU; dev->max_mtu = B44_MAX_MTU; diff --git a/drivers/net/ethernet/broadcom/bcm4908_enet.c b/drivers/net/ethernet/broadcom/bcm4908_enet.c index c131d8118489..93ccf549e2ed 100644 --- a/drivers/net/ethernet/broadcom/bcm4908_enet.c +++ b/drivers/net/ethernet/broadcom/bcm4908_enet.c @@ -507,7 +507,7 @@ static int bcm4908_enet_stop(struct net_device *netdev) return 0; } -static int bcm4908_enet_start_xmit(struct sk_buff *skb, struct net_device *netdev) +static netdev_tx_t bcm4908_enet_start_xmit(struct sk_buff *skb, struct net_device *netdev) { struct bcm4908_enet *enet = netdev_priv(netdev); struct bcm4908_enet_dma_ring *ring = &enet->tx_ring; @@ -716,6 +716,8 @@ static int bcm4908_enet_probe(struct platform_device *pdev) SET_NETDEV_DEV(netdev, &pdev->dev); err = of_get_ethdev_address(dev->of_node, netdev); + if (err == -EPROBE_DEFER) + goto err_dma_free; if (err) eth_hw_addr_random(netdev); netdev->netdev_ops = &bcm4908_enet_netdev_ops; @@ -723,17 +725,20 @@ static int bcm4908_enet_probe(struct platform_device *pdev) netdev->mtu = ETH_DATA_LEN; netdev->max_mtu = ENET_MTU_MAX; netif_napi_add_tx(netdev, &enet->tx_ring.napi, bcm4908_enet_poll_tx); - netif_napi_add(netdev, &enet->rx_ring.napi, bcm4908_enet_poll_rx, NAPI_POLL_WEIGHT); + netif_napi_add(netdev, &enet->rx_ring.napi, bcm4908_enet_poll_rx); err = register_netdev(netdev); - if (err) { - bcm4908_enet_dma_free(enet); - return err; - } + if (err) + goto err_dma_free; platform_set_drvdata(pdev, enet); return 0; + +err_dma_free: + bcm4908_enet_dma_free(enet); + + return err; } static int bcm4908_enet_remove(struct platform_device *pdev) diff --git a/drivers/net/ethernet/broadcom/bcm63xx_enet.c b/drivers/net/ethernet/broadcom/bcm63xx_enet.c index 1c6aea12db72..d91fdb0c2649 100644 --- a/drivers/net/ethernet/broadcom/bcm63xx_enet.c +++ b/drivers/net/ethernet/broadcom/bcm63xx_enet.c @@ -1321,8 +1321,8 @@ static const u32 unused_mib_regs[] = { static void bcm_enet_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *drvinfo) { - strlcpy(drvinfo->driver, bcm_enet_driver_name, sizeof(drvinfo->driver)); - strlcpy(drvinfo->bus_info, "bcm63xx", sizeof(drvinfo->bus_info)); + strscpy(drvinfo->driver, bcm_enet_driver_name, sizeof(drvinfo->driver)); + strscpy(drvinfo->bus_info, "bcm63xx", sizeof(drvinfo->bus_info)); } static int bcm_enet_get_sset_count(struct net_device *netdev, diff --git a/drivers/net/ethernet/broadcom/bcmsysport.c b/drivers/net/ethernet/broadcom/bcmsysport.c index 47fc8e6963d5..867f14c30e09 100644 --- a/drivers/net/ethernet/broadcom/bcmsysport.c +++ b/drivers/net/ethernet/broadcom/bcmsysport.c @@ -308,8 +308,8 @@ static const struct bcm_sysport_stats bcm_sysport_gstrings_stats[] = { static void bcm_sysport_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { - strlcpy(info->driver, KBUILD_MODNAME, sizeof(info->driver)); - strlcpy(info->bus_info, "platform", sizeof(info->bus_info)); + strscpy(info->driver, KBUILD_MODNAME, sizeof(info->driver)); + strscpy(info->bus_info, "platform", sizeof(info->bus_info)); } static u32 bcm_sysport_get_msglvl(struct net_device *dev) @@ -2564,7 +2564,7 @@ static int bcm_sysport_probe(struct platform_device *pdev) dev_set_drvdata(&pdev->dev, dev); dev->ethtool_ops = &bcm_sysport_ethtool_ops; dev->netdev_ops = &bcm_sysport_netdev_ops; - netif_napi_add(dev, &priv->napi, bcm_sysport_poll, 64); + netif_napi_add(dev, &priv->napi, bcm_sysport_poll); dev->features |= NETIF_F_RXCSUM | NETIF_F_HIGHDMA | NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | diff --git a/drivers/net/ethernet/broadcom/bgmac.c b/drivers/net/ethernet/broadcom/bgmac.c index 93580484a3f4..5fb3af5670ec 100644 --- a/drivers/net/ethernet/broadcom/bgmac.c +++ b/drivers/net/ethernet/broadcom/bgmac.c @@ -1367,7 +1367,7 @@ static void bgmac_get_strings(struct net_device *dev, u32 stringset, return; for (i = 0; i < BGMAC_STATS_LEN; i++) - strlcpy(data + i * ETH_GSTRING_LEN, + strscpy(data + i * ETH_GSTRING_LEN, bgmac_get_strings_stats[i].name, ETH_GSTRING_LEN); } @@ -1395,8 +1395,8 @@ static void bgmac_get_ethtool_stats(struct net_device *dev, static void bgmac_get_drvinfo(struct net_device *net_dev, struct ethtool_drvinfo *info) { - strlcpy(info->driver, KBUILD_MODNAME, sizeof(info->driver)); - strlcpy(info->bus_info, "AXI", sizeof(info->bus_info)); + strscpy(info->driver, KBUILD_MODNAME, sizeof(info->driver)); + strscpy(info->bus_info, "AXI", sizeof(info->bus_info)); } static const struct ethtool_ops bgmac_ethtool_ops = { @@ -1527,7 +1527,7 @@ int bgmac_enet_probe(struct bgmac *bgmac) if (bcm47xx_nvram_getenv("et0_no_txint", NULL, 0) == 0) bgmac->int_mask &= ~BGMAC_IS_TX_MASK; - netif_napi_add(net_dev, &bgmac->napi, bgmac_poll, NAPI_POLL_WEIGHT); + netif_napi_add(net_dev, &bgmac->napi, bgmac_poll); err = bgmac_phy_connect(bgmac); if (err) { diff --git a/drivers/net/ethernet/broadcom/bnx2.c b/drivers/net/ethernet/broadcom/bnx2.c index b97ed9b5f685..fec57f1982c8 100644 --- a/drivers/net/ethernet/broadcom/bnx2.c +++ b/drivers/net/ethernet/broadcom/bnx2.c @@ -176,12 +176,12 @@ static const struct flash_spec flash_table[] = {0x19000002, 0x5b808201, 0x000500db, 0x03840253, 0xaf020406, NONBUFFERED_FLAGS, ST_MICRO_FLASH_PAGE_BITS, ST_MICRO_FLASH_PAGE_SIZE, ST_MICRO_FLASH_BYTE_ADDR_MASK, ST_MICRO_FLASH_BASE_TOTAL_SIZE*2, - "Entry 0101: ST M45PE10 (128kB non-bufferred)"}, + "Entry 0101: ST M45PE10 (128kB non-buffered)"}, /* Entry 0110: ST M45PE20 (non-buffered flash)*/ {0x15000001, 0x57808201, 0x000500db, 0x03840253, 0xaf020406, NONBUFFERED_FLAGS, ST_MICRO_FLASH_PAGE_BITS, ST_MICRO_FLASH_PAGE_SIZE, ST_MICRO_FLASH_BYTE_ADDR_MASK, ST_MICRO_FLASH_BASE_TOTAL_SIZE*4, - "Entry 0110: ST M45PE20 (256kB non-bufferred)"}, + "Entry 0110: ST M45PE20 (256kB non-buffered)"}, /* Saifun SA25F005 (non-buffered flash) */ /* strap, cfg1, & write1 need updates */ {0x1d000003, 0x5f808201, 0x00050081, 0x03840253, 0xaf020406, @@ -7042,9 +7042,9 @@ bnx2_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { struct bnx2 *bp = netdev_priv(dev); - strlcpy(info->driver, DRV_MODULE_NAME, sizeof(info->driver)); - strlcpy(info->bus_info, pci_name(bp->pdev), sizeof(info->bus_info)); - strlcpy(info->fw_version, bp->fw_version, sizeof(info->fw_version)); + strscpy(info->driver, DRV_MODULE_NAME, sizeof(info->driver)); + strscpy(info->bus_info, pci_name(bp->pdev), sizeof(info->bus_info)); + strscpy(info->fw_version, bp->fw_version, sizeof(info->fw_version)); } #define BNX2_REGDUMP_LEN (32 * 1024) @@ -8522,7 +8522,7 @@ bnx2_init_napi(struct bnx2 *bp) else poll = bnx2_poll_msix; - netif_napi_add(bp->dev, &bp->bnx2_napi[i].napi, poll, 64); + netif_napi_add(bp->dev, &bp->bnx2_napi[i].napi, poll); bnapi->bp = bp; } } diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c index 712b5595bc39..16c490692f42 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c @@ -44,8 +44,7 @@ static void bnx2x_add_all_napi_cnic(struct bnx2x *bp) /* Add NAPI objects */ for_each_rx_queue_cnic(bp, i) { - netif_napi_add(bp->dev, &bnx2x_fp(bp, i, napi), - bnx2x_poll, NAPI_POLL_WEIGHT); + netif_napi_add(bp->dev, &bnx2x_fp(bp, i, napi), bnx2x_poll); } } @@ -55,8 +54,7 @@ static void bnx2x_add_all_napi(struct bnx2x *bp) /* Add NAPI objects */ for_each_eth_queue(bp, i) { - netif_napi_add(bp->dev, &bnx2x_fp(bp, i, napi), - bnx2x_poll, NAPI_POLL_WEIGHT); + netif_napi_add(bp->dev, &bnx2x_fp(bp, i, napi), bnx2x_poll); } } @@ -150,7 +148,7 @@ void bnx2x_fill_fw_str(struct bnx2x *bp, char *buf, size_t buf_len) phy_fw_ver[0] = '\0'; bnx2x_get_ext_phy_fw_version(&bp->link_params, phy_fw_ver, PHY_FW_VER_LEN); - strlcpy(buf, bp->fw_ver, buf_len); + strscpy(buf, bp->fw_ver, buf_len); snprintf(buf + strlen(bp->fw_ver), 32 - strlen(bp->fw_ver), "bc %d.%d.%d%s%s", (bp->common.bc_ver & 0xff0000) >> 16, @@ -789,6 +787,7 @@ static void bnx2x_tpa_stop(struct bnx2x *bp, struct bnx2x_fastpath *fp, BNX2X_ERR("skb_put is about to fail... pad %d len %d rx_buf_size %d\n", pad, len, fp->rx_buf_size); bnx2x_panic(); + bnx2x_frag_free(fp, new_data); return; } #endif diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c index 0e319ac7799f..bda3ccc28eca 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c @@ -1112,7 +1112,7 @@ static void bnx2x_get_drvinfo(struct net_device *dev, int ext_dev_info_offset; u32 mbi; - strlcpy(info->driver, DRV_MODULE_NAME, sizeof(info->driver)); + strscpy(info->driver, DRV_MODULE_NAME, sizeof(info->driver)); if (SHMEM2_HAS(bp, extended_dev_info_shared_addr)) { ext_dev_info_offset = SHMEM2_RD(bp, @@ -1126,7 +1126,7 @@ static void bnx2x_get_drvinfo(struct net_device *dev, (mbi & 0xff000000) >> 24, (mbi & 0x00ff0000) >> 16, (mbi & 0x0000ff00) >> 8); - strlcpy(info->fw_version, version, + strscpy(info->fw_version, version, sizeof(info->fw_version)); } } @@ -1135,7 +1135,7 @@ static void bnx2x_get_drvinfo(struct net_device *dev, bnx2x_fill_fw_str(bp, version, ETHTOOL_FWVERS_LEN); strlcat(info->fw_version, version, sizeof(info->fw_version)); - strlcpy(info->bus_info, pci_name(bp->pdev), sizeof(info->bus_info)); + strscpy(info->bus_info, pci_name(bp->pdev), sizeof(info->bus_info)); } static void bnx2x_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol) diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c index 962253db25b8..51b1690fd045 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c @@ -3385,7 +3385,7 @@ static void bnx2x_drv_info_ether_stat(struct bnx2x *bp) &bp->sp_objs->mac_obj; int i; - strlcpy(ether_stat->version, DRV_MODULE_VERSION, + strscpy(ether_stat->version, DRV_MODULE_VERSION, ETH_STAT_INFO_VERSION_LEN); /* get DRV_INFO_ETH_STAT_NUM_MACS_REQUIRED macs, placing them in the diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.h index 2dac704dc346..02a4e557e176 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.h @@ -518,7 +518,7 @@ int bnx2x_vfpf_storm_rx_mode(struct bnx2x *bp); static inline void bnx2x_vf_fill_fw_str(struct bnx2x *bp, char *buf, size_t buf_len) { - strlcpy(buf, bp->acquire_resp.pfdev_info.fw_ver, buf_len); + strscpy(buf, bp->acquire_resp.pfdev_info.fw_ver, buf_len); } static inline int bnx2x_vf_ustorm_prods_offset(struct bnx2x *bp, diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_vfpf.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_vfpf.c index c9129b9ba446..0657a0f5170f 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_vfpf.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_vfpf.c @@ -380,7 +380,7 @@ int bnx2x_vfpf_acquire(struct bnx2x *bp, u8 tx_count, u8 rx_count) bp->igu_base_sb = bp->acquire_resp.resc.hw_sbs[0].hw_sb_id; bp->vlan_credit = bp->acquire_resp.resc.num_vlan_filters; - strlcpy(bp->fw_ver, bp->acquire_resp.pfdev_info.fw_ver, + strscpy(bp->fw_ver, bp->acquire_resp.pfdev_info.fw_ver, sizeof(bp->fw_ver)); if (is_valid_ether_addr(bp->acquire_resp.resc.current_mac_addr)) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 96da0ba3d507..eed98c10ca9d 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -9366,16 +9366,16 @@ static void bnxt_init_napi(struct bnxt *bp) cp_nr_rings--; for (i = 0; i < cp_nr_rings; i++) { bnapi = bp->bnapi[i]; - netif_napi_add(bp->dev, &bnapi->napi, poll_fn, 64); + netif_napi_add(bp->dev, &bnapi->napi, poll_fn); } if (BNXT_CHIP_TYPE_NITRO_A0(bp)) { bnapi = bp->bnapi[cp_nr_rings]; netif_napi_add(bp->dev, &bnapi->napi, - bnxt_poll_nitroa0, 64); + bnxt_poll_nitroa0); } } else { bnapi = bp->bnapi[0]; - netif_napi_add(bp->dev, &bnapi->napi, bnxt_poll, 64); + netif_napi_add(bp->dev, &bnapi->napi, bnxt_poll); } } diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c index 87eb5362ad70..f57e524c7e30 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c @@ -1371,9 +1371,9 @@ static void bnxt_get_drvinfo(struct net_device *dev, { struct bnxt *bp = netdev_priv(dev); - strlcpy(info->driver, DRV_MODULE_NAME, sizeof(info->driver)); - strlcpy(info->fw_version, bp->fw_ver_str, sizeof(info->fw_version)); - strlcpy(info->bus_info, pci_name(bp->pdev), sizeof(info->bus_info)); + strscpy(info->driver, DRV_MODULE_NAME, sizeof(info->driver)); + strscpy(info->fw_version, bp->fw_ver_str, sizeof(info->fw_version)); + strscpy(info->bus_info, pci_name(bp->pdev), sizeof(info->bus_info)); info->n_stats = bnxt_get_num_stats(bp); info->testinfo_len = bp->num_tests; /* TODO CHIMP_FW: eeprom dump details */ @@ -3876,7 +3876,7 @@ void bnxt_ethtool_init(struct bnxt *bp) } else if (i == BNXT_IRQ_TEST_IDX) { strcpy(str, "Interrupt_test (offline)"); } else { - strlcpy(str, fw_str, ETH_GSTRING_LEN); + strscpy(str, fw_str, ETH_GSTRING_LEN); strncat(str, " test", ETH_GSTRING_LEN - strlen(str)); if (test_info->offline_mask & (1 << i)) strncat(str, " (offline)", diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ptp.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_ptp.c index 8e316367f6ce..2132ce63193c 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ptp.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ptp.c @@ -505,9 +505,13 @@ static int bnxt_hwrm_ptp_cfg(struct bnxt *bp) ptp->tstamp_filters = flags; if (netif_running(bp->dev)) { - rc = bnxt_close_nic(bp, false, false); - if (!rc) - rc = bnxt_open_nic(bp, false, false); + if (ptp->rx_filter == HWTSTAMP_FILTER_ALL) { + rc = bnxt_close_nic(bp, false, false); + if (!rc) + rc = bnxt_open_nic(bp, false, false); + } else { + bnxt_ptp_cfg_tstamp_filters(bp); + } if (!rc && !ptp->tstamp_filters) rc = -EIO; } diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.c index eb4803b11c0e..fcc65890820a 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.c @@ -222,7 +222,7 @@ static int bnxt_vf_rep_get_phys_port_name(struct net_device *dev, char *buf, static void bnxt_vf_rep_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { - strlcpy(info->driver, DRV_MODULE_NAME, sizeof(info->driver)); + strscpy(info->driver, DRV_MODULE_NAME, sizeof(info->driver)); } static int bnxt_vf_rep_get_port_parent_id(struct net_device *dev, diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.c b/drivers/net/ethernet/broadcom/genet/bcmgenet.c index 8309fb993cdb..25c450606985 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmgenet.c +++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.c @@ -1146,7 +1146,7 @@ static const struct bcmgenet_stats bcmgenet_gstrings_stats[] = { static void bcmgenet_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { - strlcpy(info->driver, "bcmgenet", sizeof(info->driver)); + strscpy(info->driver, "bcmgenet", sizeof(info->driver)); } static int bcmgenet_get_sset_count(struct net_device *dev, int string_set) @@ -2707,8 +2707,7 @@ static int bcmgenet_init_rx_ring(struct bcmgenet_priv *priv, bcmgenet_init_rx_coalesce(ring); /* Initialize Rx NAPI */ - netif_napi_add(priv->dev, &ring->napi, bcmgenet_rx_poll, - NAPI_POLL_WEIGHT); + netif_napi_add(priv->dev, &ring->napi, bcmgenet_rx_poll); bcmgenet_rdma_ring_writel(priv, index, 0, RDMA_PROD_INDEX); bcmgenet_rdma_ring_writel(priv, index, 0, RDMA_CONS_INDEX); diff --git a/drivers/net/ethernet/broadcom/tg3.c b/drivers/net/ethernet/broadcom/tg3.c index 89889d8150da..4179a12fc881 100644 --- a/drivers/net/ethernet/broadcom/tg3.c +++ b/drivers/net/ethernet/broadcom/tg3.c @@ -7380,9 +7380,9 @@ static void tg3_napi_init(struct tg3 *tp) { int i; - netif_napi_add(tp->dev, &tp->napi[0].napi, tg3_poll, 64); + netif_napi_add(tp->dev, &tp->napi[0].napi, tg3_poll); for (i = 1; i < tp->irq_cnt; i++) - netif_napi_add(tp->dev, &tp->napi[i].napi, tg3_poll_msix, 64); + netif_napi_add(tp->dev, &tp->napi[i].napi, tg3_poll_msix); } static void tg3_napi_fini(struct tg3 *tp) @@ -12302,9 +12302,9 @@ static void tg3_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info { struct tg3 *tp = netdev_priv(dev); - strlcpy(info->driver, DRV_MODULE_NAME, sizeof(info->driver)); - strlcpy(info->fw_version, tp->fw_ver, sizeof(info->fw_version)); - strlcpy(info->bus_info, pci_name(tp->pdev), sizeof(info->bus_info)); + strscpy(info->driver, DRV_MODULE_NAME, sizeof(info->driver)); + strscpy(info->fw_version, tp->fw_ver, sizeof(info->fw_version)); + strscpy(info->bus_info, pci_name(tp->pdev), sizeof(info->bus_info)); } static void tg3_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol) diff --git a/drivers/net/ethernet/brocade/bna/bnad.c b/drivers/net/ethernet/brocade/bna/bnad.c index 29dd0f93d6c0..d6d90f9722a7 100644 --- a/drivers/net/ethernet/brocade/bna/bnad.c +++ b/drivers/net/ethernet/brocade/bna/bnad.c @@ -1891,7 +1891,7 @@ bnad_napi_add(struct bnad *bnad, u32 rx_id) for (i = 0; i < bnad->num_rxp_per_rx; i++) { rx_ctrl = &bnad->rx_info[rx_id].rx_ctrl[i]; netif_napi_add(bnad->netdev, &rx_ctrl->napi, - bnad_napi_poll_rx, NAPI_POLL_WEIGHT); + bnad_napi_poll_rx); } } diff --git a/drivers/net/ethernet/brocade/bna/bnad_ethtool.c b/drivers/net/ethernet/brocade/bna/bnad_ethtool.c index 8aca768571b2..df10edff5603 100644 --- a/drivers/net/ethernet/brocade/bna/bnad_ethtool.c +++ b/drivers/net/ethernet/brocade/bna/bnad_ethtool.c @@ -114,7 +114,7 @@ static const char *bnad_net_stats_strings[] = { "mac_tx_deferral", "mac_tx_excessive_deferral", "mac_tx_single_collision", - "mac_tx_muliple_collision", + "mac_tx_multiple_collision", "mac_tx_late_collision", "mac_tx_excessive_collision", "mac_tx_total_collision", @@ -283,7 +283,7 @@ bnad_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *drvinfo) struct bfa_ioc_attr *ioc_attr; unsigned long flags; - strlcpy(drvinfo->driver, BNAD_NAME, sizeof(drvinfo->driver)); + strscpy(drvinfo->driver, BNAD_NAME, sizeof(drvinfo->driver)); ioc_attr = kzalloc(sizeof(*ioc_attr), GFP_KERNEL); if (ioc_attr) { @@ -291,12 +291,12 @@ bnad_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *drvinfo) bfa_nw_ioc_get_attr(&bnad->bna.ioceth.ioc, ioc_attr); spin_unlock_irqrestore(&bnad->bna_lock, flags); - strlcpy(drvinfo->fw_version, ioc_attr->adapter_attr.fw_ver, + strscpy(drvinfo->fw_version, ioc_attr->adapter_attr.fw_ver, sizeof(drvinfo->fw_version)); kfree(ioc_attr); } - strlcpy(drvinfo->bus_info, pci_name(bnad->pcidev), + strscpy(drvinfo->bus_info, pci_name(bnad->pcidev), sizeof(drvinfo->bus_info)); } diff --git a/drivers/net/ethernet/cadence/macb_main.c b/drivers/net/ethernet/cadence/macb_main.c index a2897549f9c4..51c9fd6f68a4 100644 --- a/drivers/net/ethernet/cadence/macb_main.c +++ b/drivers/net/ethernet/cadence/macb_main.c @@ -38,6 +38,7 @@ #include <linux/pm_runtime.h> #include <linux/ptp_classify.h> #include <linux/reset.h> +#include <linux/firmware/xlnx-zynqmp.h> #include "macb.h" /* This structure is only used for MACB on SiFive FU540 devices */ @@ -3977,8 +3978,8 @@ static int macb_init(struct platform_device *pdev) queue = &bp->queues[q]; queue->bp = bp; spin_lock_init(&queue->tx_ptr_lock); - netif_napi_add(dev, &queue->napi_rx, macb_rx_poll, NAPI_POLL_WEIGHT); - netif_napi_add(dev, &queue->napi_tx, macb_tx_poll, NAPI_POLL_WEIGHT); + netif_napi_add(dev, &queue->napi_rx, macb_rx_poll); + netif_napi_add(dev, &queue->napi_tx, macb_tx_poll); if (hw_q) { queue->ISR = GEM_ISR(hw_q - 1); queue->IER = GEM_IER(hw_q - 1); @@ -4621,6 +4622,25 @@ static int init_reset_optional(struct platform_device *pdev) "failed to init SGMII PHY\n"); } + ret = zynqmp_pm_is_function_supported(PM_IOCTL, IOCTL_SET_GEM_CONFIG); + if (!ret) { + u32 pm_info[2]; + + ret = of_property_read_u32_array(pdev->dev.of_node, "power-domains", + pm_info, ARRAY_SIZE(pm_info)); + if (ret) { + dev_err(&pdev->dev, "Failed to read power management information\n"); + goto err_out_phy_exit; + } + ret = zynqmp_pm_set_gem_config(pm_info[1], GEM_CONFIG_FIXED, 0); + if (ret) + goto err_out_phy_exit; + + ret = zynqmp_pm_set_gem_config(pm_info[1], GEM_CONFIG_SGMII_MODE, 1); + if (ret) + goto err_out_phy_exit; + } + /* Fully reset controller at hardware level if mapped in device tree */ ret = device_reset_optional(&pdev->dev); if (ret) { @@ -4629,6 +4649,8 @@ static int init_reset_optional(struct platform_device *pdev) } ret = macb_init(pdev); + +err_out_phy_exit: if (ret) phy_exit(bp->sgmii_phy); diff --git a/drivers/net/ethernet/calxeda/xgmac.c b/drivers/net/ethernet/calxeda/xgmac.c index 1281d1565ef8..f4f87dfa9687 100644 --- a/drivers/net/ethernet/calxeda/xgmac.c +++ b/drivers/net/ethernet/calxeda/xgmac.c @@ -1792,7 +1792,7 @@ static int xgmac_probe(struct platform_device *pdev) netdev_warn(ndev, "MAC address %pM not valid", ndev->dev_addr); - netif_napi_add(ndev, &priv->napi, xgmac_poll, 64); + netif_napi_add(ndev, &priv->napi, xgmac_poll); ret = register_netdev(ndev); if (ret) goto err_reg; diff --git a/drivers/net/ethernet/cavium/liquidio/cn23xx_pf_regs.h b/drivers/net/ethernet/cavium/liquidio/cn23xx_pf_regs.h index 3f1c189646f4..a0fd32476225 100644 --- a/drivers/net/ethernet/cavium/liquidio/cn23xx_pf_regs.h +++ b/drivers/net/ethernet/cavium/liquidio/cn23xx_pf_regs.h @@ -87,8 +87,8 @@ */ #define CN23XX_SLI_PKT_IN_JABBER 0x29170 /* The input jabber is used to determine the TSO max size. - * Due to H/W limitation, this need to be reduced to 60000 - * in order to to H/W TSO and avoid the WQE malfarmation + * Due to H/W limitation, this needs to be reduced to 60000 + * in order to use H/W TSO and avoid the WQE malformation * PKO_BUG_24989_WQE_LEN */ #define CN23XX_DEFAULT_INPUT_JABBER 0xEA60 /*60000*/ diff --git a/drivers/net/ethernet/cavium/liquidio/cn23xx_vf_regs.h b/drivers/net/ethernet/cavium/liquidio/cn23xx_vf_regs.h index d33dd8f4226f..e956109415cd 100644 --- a/drivers/net/ethernet/cavium/liquidio/cn23xx_vf_regs.h +++ b/drivers/net/ethernet/cavium/liquidio/cn23xx_vf_regs.h @@ -36,8 +36,8 @@ #define CN23XX_CONFIG_PCIE_FLTMSK 0x720 /* The input jabber is used to determine the TSO max size. - * Due to H/W limitation, this need to be reduced to 60000 - * in order to to H/W TSO and avoid the WQE malfarmation + * Due to H/W limitation, this needs to be reduced to 60000 + * in order to use H/W TSO and avoid the WQE malformation * PKO_BUG_24989_WQE_LEN */ #define CN23XX_DEFAULT_INPUT_JABBER 0xEA60 /*60000*/ diff --git a/drivers/net/ethernet/cavium/liquidio/lio_core.c b/drivers/net/ethernet/cavium/liquidio/lio_core.c index 73cb03266549..882b2be06ea0 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_core.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_core.c @@ -851,7 +851,7 @@ int liquidio_setup_io_queues(struct octeon_device *octeon_dev, int ifidx, napi = &droq->napi; dev_dbg(&octeon_dev->pci_dev->dev, "netif_napi_add netdev:%llx oct:%llx\n", (u64)netdev, (u64)octeon_dev); - netif_napi_add(netdev, napi, liquidio_napi_poll, 64); + netif_napi_add(netdev, napi, liquidio_napi_poll); /* designate a CPU for this droq */ droq->cpu_id = cpu_id; diff --git a/drivers/net/ethernet/cavium/liquidio/lio_main.c b/drivers/net/ethernet/cavium/liquidio/lio_main.c index bee35ce60171..d312bd594935 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_main.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_main.c @@ -92,11 +92,6 @@ static int octeon_console_debug_enabled(u32 console) /* time to wait for possible in-flight requests in milliseconds */ #define WAIT_INFLIGHT_REQUEST msecs_to_jiffies(1000) -struct lio_trusted_vf_ctx { - struct completion complete; - int status; -}; - struct oct_link_status_resp { u64 rh; struct oct_link_info link_info; diff --git a/drivers/net/ethernet/cavium/octeon/octeon_mgmt.c b/drivers/net/ethernet/cavium/octeon/octeon_mgmt.c index 103591dcea1c..edde0b8fa49c 100644 --- a/drivers/net/ethernet/cavium/octeon/octeon_mgmt.c +++ b/drivers/net/ethernet/cavium/octeon/octeon_mgmt.c @@ -1342,7 +1342,7 @@ static void octeon_mgmt_poll_controller(struct net_device *netdev) static void octeon_mgmt_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *info) { - strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); + strscpy(info->driver, DRV_NAME, sizeof(info->driver)); } static int octeon_mgmt_nway_reset(struct net_device *dev) @@ -1396,8 +1396,8 @@ static int octeon_mgmt_probe(struct platform_device *pdev) platform_set_drvdata(pdev, netdev); p = netdev_priv(netdev); - netif_napi_add(netdev, &p->napi, octeon_mgmt_napi_poll, - OCTEON_MGMT_NAPI_WEIGHT); + netif_napi_add_weight(netdev, &p->napi, octeon_mgmt_napi_poll, + OCTEON_MGMT_NAPI_WEIGHT); p->netdev = netdev; p->dev = &pdev->dev; diff --git a/drivers/net/ethernet/cavium/thunder/nicvf_ethtool.c b/drivers/net/ethernet/cavium/thunder/nicvf_ethtool.c index 5a9fad61e9ea..e5c71f907852 100644 --- a/drivers/net/ethernet/cavium/thunder/nicvf_ethtool.c +++ b/drivers/net/ethernet/cavium/thunder/nicvf_ethtool.c @@ -191,8 +191,8 @@ static void nicvf_get_drvinfo(struct net_device *netdev, { struct nicvf *nic = netdev_priv(netdev); - strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); - strlcpy(info->bus_info, pci_name(nic->pdev), sizeof(info->bus_info)); + strscpy(info->driver, DRV_NAME, sizeof(info->driver)); + strscpy(info->bus_info, pci_name(nic->pdev), sizeof(info->bus_info)); } static u32 nicvf_get_msglevel(struct net_device *netdev) diff --git a/drivers/net/ethernet/cavium/thunder/nicvf_main.c b/drivers/net/ethernet/cavium/thunder/nicvf_main.c index 768ea426d49f..98f3dc460ca7 100644 --- a/drivers/net/ethernet/cavium/thunder/nicvf_main.c +++ b/drivers/net/ethernet/cavium/thunder/nicvf_main.c @@ -1472,8 +1472,7 @@ int nicvf_open(struct net_device *netdev) } cq_poll->cq_idx = qidx; cq_poll->nicvf = nic; - netif_napi_add(netdev, &cq_poll->napi, nicvf_poll, - NAPI_POLL_WEIGHT); + netif_napi_add(netdev, &cq_poll->napi, nicvf_poll); napi_enable(&cq_poll->napi); nic->napi[qidx] = cq_poll; } diff --git a/drivers/net/ethernet/chelsio/cxgb/cxgb2.c b/drivers/net/ethernet/chelsio/cxgb/cxgb2.c index f4054d2553ea..d2286adf09fe 100644 --- a/drivers/net/ethernet/chelsio/cxgb/cxgb2.c +++ b/drivers/net/ethernet/chelsio/cxgb/cxgb2.c @@ -429,8 +429,8 @@ static void get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { struct adapter *adapter = dev->ml_priv; - strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); - strlcpy(info->bus_info, pci_name(adapter->pdev), + strscpy(info->driver, DRV_NAME, sizeof(info->driver)); + strscpy(info->bus_info, pci_name(adapter->pdev), sizeof(info->bus_info)); } @@ -1053,7 +1053,7 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent) netdev->hard_header_len += (netdev->hw_features & NETIF_F_TSO) ? sizeof(struct cpl_tx_pkt_lso) : sizeof(struct cpl_tx_pkt); - netif_napi_add(netdev, &adapter->napi, t1_poll, 64); + netif_napi_add(netdev, &adapter->napi, t1_poll); netdev->ethtool_ops = &t1_ethtool_ops; diff --git a/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c b/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c index 174b1e156669..a52e6b6e2876 100644 --- a/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c +++ b/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c @@ -609,8 +609,7 @@ static void init_napi(struct adapter *adap) struct sge_qset *qs = &adap->sge.qs[i]; if (qs->adap) - netif_napi_add(qs->netdev, &qs->napi, qs->napi.poll, - 64); + netif_napi_add(qs->netdev, &qs->napi, qs->napi.poll); } /* @@ -1627,8 +1626,8 @@ static void get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) t3_get_tp_version(adapter, &tp_vers); spin_unlock(&adapter->stats_lock); - strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); - strlcpy(info->bus_info, pci_name(adapter->pdev), + strscpy(info->driver, DRV_NAME, sizeof(info->driver)); + strscpy(info->bus_info, pci_name(adapter->pdev), sizeof(info->bus_info)); if (fw_vers) snprintf(info->fw_version, sizeof(info->fw_version), diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_ethtool.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_ethtool.c index 77897edd2bc0..8477a93cee6b 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_ethtool.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_ethtool.c @@ -199,8 +199,8 @@ static void get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) struct adapter *adapter = netdev2adap(dev); u32 exprom_vers; - strlcpy(info->driver, cxgb4_driver_name, sizeof(info->driver)); - strlcpy(info->bus_info, pci_name(adapter->pdev), + strscpy(info->driver, cxgb4_driver_name, sizeof(info->driver)); + strscpy(info->bus_info, pci_name(adapter->pdev), sizeof(info->bus_info)); info->regdump_len = get_regs_len(dev); diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c index d0061921529f..9cbce1faab26 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c @@ -3903,8 +3903,8 @@ static void cxgb4_mgmt_get_drvinfo(struct net_device *dev, { struct adapter *adapter = netdev2adap(dev); - strlcpy(info->driver, cxgb4_driver_name, sizeof(info->driver)); - strlcpy(info->bus_info, pci_name(adapter->pdev), + strscpy(info->driver, cxgb4_driver_name, sizeof(info->driver)); + strscpy(info->bus_info, pci_name(adapter->pdev), sizeof(info->bus_info)); } diff --git a/drivers/net/ethernet/chelsio/cxgb4/sge.c b/drivers/net/ethernet/chelsio/cxgb4/sge.c index ee52e3b1d74f..46809e2d94ee 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/sge.c +++ b/drivers/net/ethernet/chelsio/cxgb4/sge.c @@ -4467,7 +4467,7 @@ int t4_sge_alloc_rxq(struct adapter *adap, struct sge_rspq *iq, bool fwevtq, if (ret) goto err; - netif_napi_add(dev, &iq->napi, napi_rx_handler, 64); + netif_napi_add(dev, &iq->napi, napi_rx_handler); iq->cur_desc = iq->desc; iq->cidx = 0; iq->gen = 1; diff --git a/drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c b/drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c index c2822e635f89..54db79f4dcfe 100644 --- a/drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c +++ b/drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c @@ -1553,8 +1553,8 @@ static void cxgb4vf_get_drvinfo(struct net_device *dev, { struct adapter *adapter = netdev2adap(dev); - strlcpy(drvinfo->driver, KBUILD_MODNAME, sizeof(drvinfo->driver)); - strlcpy(drvinfo->bus_info, pci_name(to_pci_dev(dev->dev.parent)), + strscpy(drvinfo->driver, KBUILD_MODNAME, sizeof(drvinfo->driver)); + strscpy(drvinfo->bus_info, pci_name(to_pci_dev(dev->dev.parent)), sizeof(drvinfo->bus_info)); snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version), "%u.%u.%u.%u, TP %u.%u.%u.%u", diff --git a/drivers/net/ethernet/chelsio/cxgb4vf/sge.c b/drivers/net/ethernet/chelsio/cxgb4vf/sge.c index 43b2ceb6aa32..2d0cf76fb3c5 100644 --- a/drivers/net/ethernet/chelsio/cxgb4vf/sge.c +++ b/drivers/net/ethernet/chelsio/cxgb4vf/sge.c @@ -2336,7 +2336,7 @@ int t4vf_sge_alloc_rxq(struct adapter *adapter, struct sge_rspq *rspq, if (ret) goto err; - netif_napi_add(dev, &rspq->napi, napi_rx_handler, 64); + netif_napi_add(dev, &rspq->napi, napi_rx_handler); rspq->cur_desc = rspq->desc; rspq->cidx = 0; rspq->gen = 1; diff --git a/drivers/net/ethernet/chelsio/inline_crypto/chtls/chtls_cm.c b/drivers/net/ethernet/chelsio/inline_crypto/chtls/chtls_cm.c index ddfe9208529a..f90bfba4b303 100644 --- a/drivers/net/ethernet/chelsio/inline_crypto/chtls/chtls_cm.c +++ b/drivers/net/ethernet/chelsio/inline_crypto/chtls/chtls_cm.c @@ -1069,8 +1069,7 @@ static void chtls_pass_accept_rpl(struct sk_buff *skb, cxgb4_l2t_send(csk->egress_dev, skb, csk->l2t_entry); } -static void inet_inherit_port(struct inet_hashinfo *hash_info, - struct sock *lsk, struct sock *newsk) +static void inet_inherit_port(struct sock *lsk, struct sock *newsk) { local_bh_disable(); __inet_inherit_port(lsk, newsk); @@ -1240,7 +1239,7 @@ static struct sock *chtls_recv_sock(struct sock *lsk, ipv4.sysctl_tcp_window_scaling), tp->window_clamp); neigh_release(n); - inet_inherit_port(&tcp_hashinfo, lsk, newsk); + inet_inherit_port(lsk, newsk); csk_set_flag(csk, CSK_CONN_INLINE); bh_unlock_sock(newsk); /* tcp_create_openreq_child ->sk_clone_lock */ diff --git a/drivers/net/ethernet/chelsio/inline_crypto/chtls/chtls_main.c b/drivers/net/ethernet/chelsio/inline_crypto/chtls/chtls_main.c index 9098b3eed4da..1e55b12fee51 100644 --- a/drivers/net/ethernet/chelsio/inline_crypto/chtls/chtls_main.c +++ b/drivers/net/ethernet/chelsio/inline_crypto/chtls/chtls_main.c @@ -193,7 +193,7 @@ static void chtls_register_dev(struct chtls_dev *cdev) { struct tls_toe_device *tlsdev = &cdev->tlsdev; - strlcpy(tlsdev->name, "chtls", TLS_TOE_DEVICE_NAME_MAX); + strscpy(tlsdev->name, "chtls", TLS_TOE_DEVICE_NAME_MAX); strlcat(tlsdev->name, cdev->lldi->ports[0]->name, TLS_TOE_DEVICE_NAME_MAX); tlsdev->feature = chtls_inline_feature; diff --git a/drivers/net/ethernet/cirrus/ep93xx_eth.c b/drivers/net/ethernet/cirrus/ep93xx_eth.c index 21ba6e893072..8627ab19d470 100644 --- a/drivers/net/ethernet/cirrus/ep93xx_eth.c +++ b/drivers/net/ethernet/cirrus/ep93xx_eth.c @@ -689,7 +689,7 @@ static int ep93xx_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) static void ep93xx_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { - strlcpy(info->driver, DRV_MODULE_NAME, sizeof(info->driver)); + strscpy(info->driver, DRV_MODULE_NAME, sizeof(info->driver)); } static int ep93xx_get_link_ksettings(struct net_device *dev, @@ -812,7 +812,7 @@ static int ep93xx_eth_probe(struct platform_device *pdev) ep = netdev_priv(dev); ep->dev = dev; SET_NETDEV_DEV(dev, &pdev->dev); - netif_napi_add(dev, &ep->napi, ep93xx_poll, 64); + netif_napi_add(dev, &ep->napi, ep93xx_poll); platform_set_drvdata(pdev, dev); diff --git a/drivers/net/ethernet/cisco/enic/enic_ethtool.c b/drivers/net/ethernet/cisco/enic/enic_ethtool.c index 60d8c0fbc037..08b7cc0a1809 100644 --- a/drivers/net/ethernet/cisco/enic/enic_ethtool.c +++ b/drivers/net/ethernet/cisco/enic/enic_ethtool.c @@ -131,10 +131,10 @@ static void enic_get_drvinfo(struct net_device *netdev, if (err == -ENOMEM) return; - strlcpy(drvinfo->driver, DRV_NAME, sizeof(drvinfo->driver)); - strlcpy(drvinfo->fw_version, fw_info->fw_version, + strscpy(drvinfo->driver, DRV_NAME, sizeof(drvinfo->driver)); + strscpy(drvinfo->fw_version, fw_info->fw_version, sizeof(drvinfo->fw_version)); - strlcpy(drvinfo->bus_info, pci_name(enic->pdev), + strscpy(drvinfo->bus_info, pci_name(enic->pdev), sizeof(drvinfo->bus_info)); } diff --git a/drivers/net/ethernet/cisco/enic/enic_main.c b/drivers/net/ethernet/cisco/enic/enic_main.c index 372fb7b3a282..29500d32e362 100644 --- a/drivers/net/ethernet/cisco/enic/enic_main.c +++ b/drivers/net/ethernet/cisco/enic/enic_main.c @@ -2633,16 +2633,17 @@ static int enic_dev_init(struct enic *enic) switch (vnic_dev_get_intr_mode(enic->vdev)) { default: - netif_napi_add(netdev, &enic->napi[0], enic_poll, 64); + netif_napi_add(netdev, &enic->napi[0], enic_poll); break; case VNIC_DEV_INTR_MODE_MSIX: for (i = 0; i < enic->rq_count; i++) { netif_napi_add(netdev, &enic->napi[i], - enic_poll_msix_rq, NAPI_POLL_WEIGHT); + enic_poll_msix_rq); } for (i = 0; i < enic->wq_count; i++) - netif_napi_add(netdev, &enic->napi[enic_cq_wq(enic, i)], - enic_poll_msix_wq, NAPI_POLL_WEIGHT); + netif_napi_add(netdev, + &enic->napi[enic_cq_wq(enic, i)], + enic_poll_msix_wq); break; } diff --git a/drivers/net/ethernet/cortina/gemini.c b/drivers/net/ethernet/cortina/gemini.c index 6dae768671e3..fdf10318758b 100644 --- a/drivers/net/ethernet/cortina/gemini.c +++ b/drivers/net/ethernet/cortina/gemini.c @@ -2471,7 +2471,7 @@ static int gemini_ethernet_port_probe(struct platform_device *pdev) netdev->max_mtu = 10236 - VLAN_ETH_HLEN; port->freeq_refill = 0; - netif_napi_add(netdev, &port->napi, gmac_napi_poll, NAPI_POLL_WEIGHT); + netif_napi_add(netdev, &port->napi, gmac_napi_poll); ret = of_get_mac_address(np, mac); if (!ret) { diff --git a/drivers/net/ethernet/davicom/dm9000.c b/drivers/net/ethernet/davicom/dm9000.c index 0985ab216566..b21e56de6167 100644 --- a/drivers/net/ethernet/davicom/dm9000.c +++ b/drivers/net/ethernet/davicom/dm9000.c @@ -28,8 +28,7 @@ #include <linux/irq.h> #include <linux/slab.h> #include <linux/regulator/consumer.h> -#include <linux/gpio.h> -#include <linux/of_gpio.h> +#include <linux/gpio/consumer.h> #include <asm/delay.h> #include <asm/irq.h> @@ -540,8 +539,8 @@ static void dm9000_get_drvinfo(struct net_device *dev, { struct board_info *dm = to_dm9000_board(dev); - strlcpy(info->driver, CARDNAME, sizeof(info->driver)); - strlcpy(info->bus_info, to_platform_device(dm->dev)->name, + strscpy(info->driver, CARDNAME, sizeof(info->driver)); + strscpy(info->bus_info, to_platform_device(dm->dev)->name, sizeof(info->bus_info)); } @@ -1012,7 +1011,7 @@ static void dm9000_send_packet(struct net_device *dev, * Hardware start transmission. * Send a packet to media from the upper layer. */ -static int +static netdev_tx_t dm9000_start_xmit(struct sk_buff *skb, struct net_device *dev) { unsigned long flags; @@ -1421,8 +1420,7 @@ dm9000_probe(struct platform_device *pdev) int iosize; int i; u32 id_val; - int reset_gpios; - enum of_gpio_flags flags; + struct gpio_desc *reset_gpio; struct regulator *power; bool inv_mac_addr = false; u8 addr[ETH_ALEN]; @@ -1442,20 +1440,24 @@ dm9000_probe(struct platform_device *pdev) dev_dbg(dev, "regulator enabled\n"); } - reset_gpios = of_get_named_gpio_flags(dev->of_node, "reset-gpios", 0, - &flags); - if (gpio_is_valid(reset_gpios)) { - ret = devm_gpio_request_one(dev, reset_gpios, flags, - "dm9000_reset"); + reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); + ret = PTR_ERR_OR_ZERO(reset_gpio); + if (ret) { + dev_err(dev, "failed to request reset gpio: %d\n", ret); + goto out_regulator_disable; + } + + if (reset_gpio) { + ret = gpiod_set_consumer_name(reset_gpio, "dm9000_reset"); if (ret) { - dev_err(dev, "failed to request reset gpio %d: %d\n", - reset_gpios, ret); + dev_err(dev, "failed to set reset gpio name: %d\n", + ret); goto out_regulator_disable; } /* According to manual PWRST# Low Period Min 1ms */ msleep(2); - gpio_set_value(reset_gpios, 1); + gpiod_set_value_cansleep(reset_gpio, 0); /* Needs 3ms to read eeprom when PWRST is deasserted */ msleep(4); } diff --git a/drivers/net/ethernet/dec/tulip/de2104x.c b/drivers/net/ethernet/dec/tulip/de2104x.c index d51b3d24a0c8..cd3dc4b89518 100644 --- a/drivers/net/ethernet/dec/tulip/de2104x.c +++ b/drivers/net/ethernet/dec/tulip/de2104x.c @@ -1606,8 +1606,8 @@ static void de_get_drvinfo (struct net_device *dev,struct ethtool_drvinfo *info) { struct de_private *de = netdev_priv(dev); - strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); - strlcpy(info->bus_info, pci_name(de->pdev), sizeof(info->bus_info)); + strscpy(info->driver, DRV_NAME, sizeof(info->driver)); + strscpy(info->bus_info, pci_name(de->pdev), sizeof(info->bus_info)); } static int de_get_regs_len(struct net_device *dev) diff --git a/drivers/net/ethernet/dec/tulip/dmfe.c b/drivers/net/ethernet/dec/tulip/dmfe.c index 83f1727d1423..3188ba7b450f 100644 --- a/drivers/net/ethernet/dec/tulip/dmfe.c +++ b/drivers/net/ethernet/dec/tulip/dmfe.c @@ -1074,8 +1074,8 @@ static void dmfe_ethtool_get_drvinfo(struct net_device *dev, { struct dmfe_board_info *np = netdev_priv(dev); - strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); - strlcpy(info->bus_info, pci_name(np->pdev), sizeof(info->bus_info)); + strscpy(info->driver, DRV_NAME, sizeof(info->driver)); + strscpy(info->bus_info, pci_name(np->pdev), sizeof(info->bus_info)); } static int dmfe_ethtool_set_wol(struct net_device *dev, diff --git a/drivers/net/ethernet/dec/tulip/tulip_core.c b/drivers/net/ethernet/dec/tulip/tulip_core.c index b8e46c4849ef..ecfad43df45a 100644 --- a/drivers/net/ethernet/dec/tulip/tulip_core.c +++ b/drivers/net/ethernet/dec/tulip/tulip_core.c @@ -858,8 +858,8 @@ static struct net_device_stats *tulip_get_stats(struct net_device *dev) static void tulip_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { struct tulip_private *np = netdev_priv(dev); - strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); - strlcpy(info->bus_info, pci_name(np->pdev), sizeof(info->bus_info)); + strscpy(info->driver, DRV_NAME, sizeof(info->driver)); + strscpy(info->bus_info, pci_name(np->pdev), sizeof(info->bus_info)); } diff --git a/drivers/net/ethernet/dec/tulip/uli526x.c b/drivers/net/ethernet/dec/tulip/uli526x.c index 77d9058431e3..ff080ab0f116 100644 --- a/drivers/net/ethernet/dec/tulip/uli526x.c +++ b/drivers/net/ethernet/dec/tulip/uli526x.c @@ -971,8 +971,8 @@ static void netdev_get_drvinfo(struct net_device *dev, { struct uli526x_board_info *np = netdev_priv(dev); - strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); - strlcpy(info->bus_info, pci_name(np->pdev), sizeof(info->bus_info)); + strscpy(info->driver, DRV_NAME, sizeof(info->driver)); + strscpy(info->bus_info, pci_name(np->pdev), sizeof(info->bus_info)); } static int netdev_get_link_ksettings(struct net_device *dev, diff --git a/drivers/net/ethernet/dec/tulip/winbond-840.c b/drivers/net/ethernet/dec/tulip/winbond-840.c index 1db19463fd46..37fba39c0056 100644 --- a/drivers/net/ethernet/dec/tulip/winbond-840.c +++ b/drivers/net/ethernet/dec/tulip/winbond-840.c @@ -1374,8 +1374,8 @@ static void netdev_get_drvinfo (struct net_device *dev, struct ethtool_drvinfo * { struct netdev_private *np = netdev_priv(dev); - strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); - strlcpy(info->bus_info, pci_name(np->pci_dev), sizeof(info->bus_info)); + strscpy(info->driver, DRV_NAME, sizeof(info->driver)); + strscpy(info->bus_info, pci_name(np->pci_dev), sizeof(info->bus_info)); } static int netdev_get_link_ksettings(struct net_device *dev, diff --git a/drivers/net/ethernet/dlink/dl2k.c b/drivers/net/ethernet/dlink/dl2k.c index a301f7e6a440..2c67a857a42f 100644 --- a/drivers/net/ethernet/dlink/dl2k.c +++ b/drivers/net/ethernet/dlink/dl2k.c @@ -1235,8 +1235,8 @@ static void rio_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info { struct netdev_private *np = netdev_priv(dev); - strlcpy(info->driver, "dl2k", sizeof(info->driver)); - strlcpy(info->bus_info, pci_name(np->pdev), sizeof(info->bus_info)); + strscpy(info->driver, "dl2k", sizeof(info->driver)); + strscpy(info->bus_info, pci_name(np->pdev), sizeof(info->bus_info)); } static int rio_get_link_ksettings(struct net_device *dev, diff --git a/drivers/net/ethernet/dlink/sundance.c b/drivers/net/ethernet/dlink/sundance.c index 8dd7bf9014ec..43def191f26f 100644 --- a/drivers/net/ethernet/dlink/sundance.c +++ b/drivers/net/ethernet/dlink/sundance.c @@ -1644,8 +1644,8 @@ static int check_if_running(struct net_device *dev) static void get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { struct netdev_private *np = netdev_priv(dev); - strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); - strlcpy(info->bus_info, pci_name(np->pci_dev), sizeof(info->bus_info)); + strscpy(info->driver, DRV_NAME, sizeof(info->driver)); + strscpy(info->bus_info, pci_name(np->pci_dev), sizeof(info->bus_info)); } static int get_link_ksettings(struct net_device *dev, diff --git a/drivers/net/ethernet/dnet.c b/drivers/net/ethernet/dnet.c index 92462ed87bc4..08184f20f510 100644 --- a/drivers/net/ethernet/dnet.c +++ b/drivers/net/ethernet/dnet.c @@ -725,8 +725,8 @@ static struct net_device_stats *dnet_get_stats(struct net_device *dev) static void dnet_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { - strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); - strlcpy(info->bus_info, "0", sizeof(info->bus_info)); + strscpy(info->driver, DRV_NAME, sizeof(info->driver)); + strscpy(info->bus_info, "0", sizeof(info->bus_info)); } static const struct ethtool_ops dnet_ethtool_ops = { @@ -788,7 +788,7 @@ static int dnet_probe(struct platform_device *pdev) } dev->netdev_ops = &dnet_netdev_ops; - netif_napi_add(dev, &bp->napi, dnet_poll, 64); + netif_napi_add(dev, &bp->napi, dnet_poll); dev->ethtool_ops = &dnet_ethtool_ops; dev->base_addr = (unsigned long)bp->regs; diff --git a/drivers/net/ethernet/emulex/benet/be_cmds.c b/drivers/net/ethernet/emulex/benet/be_cmds.c index b4f5e57d0285..08ec84cd21c0 100644 --- a/drivers/net/ethernet/emulex/benet/be_cmds.c +++ b/drivers/net/ethernet/emulex/benet/be_cmds.c @@ -1878,9 +1878,9 @@ int be_cmd_get_fw_ver(struct be_adapter *adapter) if (!status) { struct be_cmd_resp_get_fw_version *resp = embedded_payload(wrb); - strlcpy(adapter->fw_ver, resp->firmware_version_string, + strscpy(adapter->fw_ver, resp->firmware_version_string, sizeof(adapter->fw_ver)); - strlcpy(adapter->fw_on_flash, resp->fw_on_flash_version_string, + strscpy(adapter->fw_on_flash, resp->fw_on_flash_version_string, sizeof(adapter->fw_on_flash)); } err: @@ -2373,7 +2373,7 @@ static int lancer_cmd_write_object(struct be_adapter *adapter, be_dws_cpu_to_le(ctxt, sizeof(req->context)); req->write_offset = cpu_to_le32(data_offset); - strlcpy(req->object_name, obj_name, sizeof(req->object_name)); + strscpy(req->object_name, obj_name, sizeof(req->object_name)); req->descriptor_count = cpu_to_le32(1); req->buf_len = cpu_to_le32(data_size); req->addr_low = cpu_to_le32((cmd->dma + @@ -2442,9 +2442,9 @@ int be_cmd_query_sfp_info(struct be_adapter *adapter) status = be_cmd_read_port_transceiver_data(adapter, TR_PAGE_A0, 0, PAGE_DATA_LEN, page_data); if (!status) { - strlcpy(adapter->phy.vendor_name, page_data + + strscpy(adapter->phy.vendor_name, page_data + SFP_VENDOR_NAME_OFFSET, SFP_VENDOR_NAME_LEN - 1); - strlcpy(adapter->phy.vendor_pn, + strscpy(adapter->phy.vendor_pn, page_data + SFP_VENDOR_PN_OFFSET, SFP_VENDOR_NAME_LEN - 1); } @@ -2473,7 +2473,7 @@ static int lancer_cmd_delete_object(struct be_adapter *adapter, OPCODE_COMMON_DELETE_OBJECT, sizeof(*req), wrb, NULL); - strlcpy(req->object_name, obj_name, sizeof(req->object_name)); + strscpy(req->object_name, obj_name, sizeof(req->object_name)); status = be_mcc_notify_wait(adapter); err: diff --git a/drivers/net/ethernet/emulex/benet/be_ethtool.c b/drivers/net/ethernet/emulex/benet/be_ethtool.c index bd0df189d871..77edc3d9b505 100644 --- a/drivers/net/ethernet/emulex/benet/be_ethtool.c +++ b/drivers/net/ethernet/emulex/benet/be_ethtool.c @@ -220,15 +220,15 @@ static void be_get_drvinfo(struct net_device *netdev, { struct be_adapter *adapter = netdev_priv(netdev); - strlcpy(drvinfo->driver, DRV_NAME, sizeof(drvinfo->driver)); + strscpy(drvinfo->driver, DRV_NAME, sizeof(drvinfo->driver)); if (!memcmp(adapter->fw_ver, adapter->fw_on_flash, FW_VER_LEN)) - strlcpy(drvinfo->fw_version, adapter->fw_ver, + strscpy(drvinfo->fw_version, adapter->fw_ver, sizeof(drvinfo->fw_version)); else snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version), "%s [%s]", adapter->fw_ver, adapter->fw_on_flash); - strlcpy(drvinfo->bus_info, pci_name(adapter->pdev), + strscpy(drvinfo->bus_info, pci_name(adapter->pdev), sizeof(drvinfo->bus_info)); } diff --git a/drivers/net/ethernet/emulex/benet/be_main.c b/drivers/net/ethernet/emulex/benet/be_main.c index 414362febbb9..a92a74761546 100644 --- a/drivers/net/ethernet/emulex/benet/be_main.c +++ b/drivers/net/ethernet/emulex/benet/be_main.c @@ -2982,8 +2982,7 @@ static int be_evt_queues_create(struct be_adapter *adapter) return -ENOMEM; cpumask_set_cpu(cpumask_local_spread(i, numa_node), eqo->affinity_mask); - netif_napi_add(adapter->netdev, &eqo->napi, be_poll, - NAPI_POLL_WEIGHT); + netif_napi_add(adapter->netdev, &eqo->napi, be_poll); } return 0; } diff --git a/drivers/net/ethernet/engleder/Kconfig b/drivers/net/ethernet/engleder/Kconfig index f4e2b1102d8f..3df6bf476ae7 100644 --- a/drivers/net/ethernet/engleder/Kconfig +++ b/drivers/net/ethernet/engleder/Kconfig @@ -21,6 +21,7 @@ config TSNEP depends on HAS_IOMEM && HAS_DMA depends on PTP_1588_CLOCK_OPTIONAL select PHYLIB + select PAGE_POOL help Support for the Engleder TSN endpoint Ethernet MAC IP Core. diff --git a/drivers/net/ethernet/engleder/Makefile b/drivers/net/ethernet/engleder/Makefile index cce2191cb889..b6e3b16623de 100644 --- a/drivers/net/ethernet/engleder/Makefile +++ b/drivers/net/ethernet/engleder/Makefile @@ -6,5 +6,5 @@ obj-$(CONFIG_TSNEP) += tsnep.o tsnep-objs := tsnep_main.o tsnep_ethtool.o tsnep_ptp.o tsnep_tc.o \ - $(tsnep-y) + tsnep_rxnfc.o $(tsnep-y) tsnep-$(CONFIG_TSNEP_SELFTESTS) += tsnep_selftests.o diff --git a/drivers/net/ethernet/engleder/tsnep.h b/drivers/net/ethernet/engleder/tsnep.h index 23bbece6b7de..09a723b827c7 100644 --- a/drivers/net/ethernet/engleder/tsnep.h +++ b/drivers/net/ethernet/engleder/tsnep.h @@ -21,8 +21,6 @@ #define TSNEP_RING_ENTRIES_PER_PAGE (PAGE_SIZE / TSNEP_DESC_SIZE) #define TSNEP_RING_PAGE_COUNT (TSNEP_RING_SIZE / TSNEP_RING_ENTRIES_PER_PAGE) -#define TSNEP_QUEUES 1 - struct tsnep_gcl { void __iomem *addr; @@ -39,6 +37,24 @@ struct tsnep_gcl { bool change; }; +enum tsnep_rxnfc_filter_type { + TSNEP_RXNFC_ETHER_TYPE, +}; + +struct tsnep_rxnfc_filter { + enum tsnep_rxnfc_filter_type type; + union { + u16 ether_type; + }; +}; + +struct tsnep_rxnfc_rule { + struct list_head list; + struct tsnep_rxnfc_filter filter; + int queue_index; + int location; +}; + struct tsnep_tx_entry { struct tsnep_tx_desc *desc; struct tsnep_tx_desc_wb *desc_wb; @@ -55,6 +71,7 @@ struct tsnep_tx_entry { struct tsnep_tx { struct tsnep_adapter *adapter; void __iomem *addr; + int queue_index; void *page[TSNEP_RING_PAGE_COUNT]; dma_addr_t page_dma[TSNEP_RING_PAGE_COUNT]; @@ -79,14 +96,15 @@ struct tsnep_rx_entry { u32 properties; - struct sk_buff *skb; + struct page *page; size_t len; - DEFINE_DMA_UNMAP_ADDR(dma); + dma_addr_t dma; }; struct tsnep_rx { struct tsnep_adapter *adapter; void __iomem *addr; + int queue_index; void *page[TSNEP_RING_PAGE_COUNT]; dma_addr_t page_dma[TSNEP_RING_PAGE_COUNT]; @@ -95,6 +113,7 @@ struct tsnep_rx { int read; u32 owner_counter; int increment_owner_counter; + struct page_pool *page_pool; u32 packets; u32 bytes; @@ -104,12 +123,14 @@ struct tsnep_rx { struct tsnep_queue { struct tsnep_adapter *adapter; + char name[IFNAMSIZ + 9]; struct tsnep_tx *tx; struct tsnep_rx *rx; struct napi_struct napi; + int irq; u32 irq_mask; }; @@ -125,7 +146,6 @@ struct tsnep_adapter { struct platform_device *pdev; struct device *dmadev; void __iomem *addr; - int irq; bool gate_control; /* gate control lock */ @@ -140,6 +160,12 @@ struct tsnep_adapter { /* ptp clock lock */ spinlock_t ptp_lock; + /* RX flow classification rules lock */ + struct mutex rxnfc_lock; + struct list_head rxnfc_rules; + int rxnfc_count; + int rxnfc_max; + int num_tx_queues; struct tsnep_tx tx[TSNEP_MAX_QUEUES]; int num_rx_queues; @@ -160,6 +186,18 @@ void tsnep_tc_cleanup(struct tsnep_adapter *adapter); int tsnep_tc_setup(struct net_device *netdev, enum tc_setup_type type, void *type_data); +int tsnep_rxnfc_init(struct tsnep_adapter *adapter); +void tsnep_rxnfc_cleanup(struct tsnep_adapter *adapter); +int tsnep_rxnfc_get_rule(struct tsnep_adapter *adapter, + struct ethtool_rxnfc *cmd); +int tsnep_rxnfc_get_all(struct tsnep_adapter *adapter, + struct ethtool_rxnfc *cmd, + u32 *rule_locs); +int tsnep_rxnfc_add_rule(struct tsnep_adapter *adapter, + struct ethtool_rxnfc *cmd); +int tsnep_rxnfc_del_rule(struct tsnep_adapter *adapter, + struct ethtool_rxnfc *cmd); + #if IS_ENABLED(CONFIG_TSNEP_SELFTESTS) int tsnep_ethtool_get_test_count(void); void tsnep_ethtool_get_test_strings(u8 *data); diff --git a/drivers/net/ethernet/engleder/tsnep_ethtool.c b/drivers/net/ethernet/engleder/tsnep_ethtool.c index e6760dc68ddd..a713a126b227 100644 --- a/drivers/net/ethernet/engleder/tsnep_ethtool.c +++ b/drivers/net/ethernet/engleder/tsnep_ethtool.c @@ -250,6 +250,44 @@ static int tsnep_ethtool_get_sset_count(struct net_device *netdev, int sset) } } +static int tsnep_ethtool_get_rxnfc(struct net_device *dev, + struct ethtool_rxnfc *cmd, u32 *rule_locs) +{ + struct tsnep_adapter *adapter = netdev_priv(dev); + + switch (cmd->cmd) { + case ETHTOOL_GRXRINGS: + cmd->data = adapter->num_rx_queues; + return 0; + case ETHTOOL_GRXCLSRLCNT: + cmd->rule_cnt = adapter->rxnfc_count; + cmd->data = adapter->rxnfc_max; + cmd->data |= RX_CLS_LOC_SPECIAL; + return 0; + case ETHTOOL_GRXCLSRULE: + return tsnep_rxnfc_get_rule(adapter, cmd); + case ETHTOOL_GRXCLSRLALL: + return tsnep_rxnfc_get_all(adapter, cmd, rule_locs); + default: + return -EOPNOTSUPP; + } +} + +static int tsnep_ethtool_set_rxnfc(struct net_device *dev, + struct ethtool_rxnfc *cmd) +{ + struct tsnep_adapter *adapter = netdev_priv(dev); + + switch (cmd->cmd) { + case ETHTOOL_SRXCLSRLINS: + return tsnep_rxnfc_add_rule(adapter, cmd); + case ETHTOOL_SRXCLSRLDEL: + return tsnep_rxnfc_del_rule(adapter, cmd); + default: + return -EOPNOTSUPP; + } +} + static int tsnep_ethtool_get_ts_info(struct net_device *dev, struct ethtool_ts_info *info) { @@ -287,6 +325,8 @@ const struct ethtool_ops tsnep_ethtool_ops = { .get_strings = tsnep_ethtool_get_strings, .get_ethtool_stats = tsnep_ethtool_get_ethtool_stats, .get_sset_count = tsnep_ethtool_get_sset_count, + .get_rxnfc = tsnep_ethtool_get_rxnfc, + .set_rxnfc = tsnep_ethtool_set_rxnfc, .get_ts_info = tsnep_ethtool_get_ts_info, .get_link_ksettings = phy_ethtool_get_link_ksettings, .set_link_ksettings = phy_ethtool_set_link_ksettings, diff --git a/drivers/net/ethernet/engleder/tsnep_hw.h b/drivers/net/ethernet/engleder/tsnep_hw.h index 916ceac3ada2..315dada75323 100644 --- a/drivers/net/ethernet/engleder/tsnep_hw.h +++ b/drivers/net/ethernet/engleder/tsnep_hw.h @@ -34,6 +34,7 @@ #define ECM_INT_LINK 0x00000020 #define ECM_INT_TX_0 0x00000100 #define ECM_INT_RX_0 0x00000200 +#define ECM_INT_TXRX_SHIFT 2 #define ECM_INT_ALL 0x7FFFFFFF #define ECM_INT_DISABLE 0x80000000 @@ -92,8 +93,7 @@ /* tsnep register */ #define TSNEP_INFO 0x0100 -#define TSNEP_INFO_RX_ASSIGN 0x00010000 -#define TSNEP_INFO_TX_TIME 0x00020000 +#define TSNEP_INFO_TX_TIME 0x00010000 #define TSNEP_CONTROL 0x0108 #define TSNEP_CONTROL_TX_RESET 0x00000001 #define TSNEP_CONTROL_TX_ENABLE 0x00000002 @@ -122,10 +122,6 @@ #define TSNEP_RX_STATISTIC_BUFFER_TOO_SMALL 0x0191 #define TSNEP_RX_STATISTIC_FIFO_OVERFLOW 0x0192 #define TSNEP_RX_STATISTIC_INVALID_FRAME 0x0193 -#define TSNEP_RX_ASSIGN 0x01A0 -#define TSNEP_RX_ASSIGN_ETHER_TYPE_ACTIVE 0x00000001 -#define TSNEP_RX_ASSIGN_ETHER_TYPE_MASK 0xFFFF0000 -#define TSNEP_RX_ASSIGN_ETHER_TYPE_SHIFT 16 #define TSNEP_MAC_ADDRESS_LOW 0x0800 #define TSNEP_MAC_ADDRESS_HIGH 0x0804 #define TSNEP_RX_FILTER 0x0806 @@ -152,6 +148,14 @@ #define TSNEP_GCL_A 0x2000 #define TSNEP_GCL_B 0x2800 #define TSNEP_GCL_SIZE SZ_2K +#define TSNEP_RX_ASSIGN 0x0840 +#define TSNEP_RX_ASSIGN_ACTIVE 0x00000001 +#define TSNEP_RX_ASSIGN_QUEUE_MASK 0x00000006 +#define TSNEP_RX_ASSIGN_QUEUE_SHIFT 1 +#define TSNEP_RX_ASSIGN_OFFSET 1 +#define TSNEP_RX_ASSIGN_ETHER_TYPE 0x0880 +#define TSNEP_RX_ASSIGN_ETHER_TYPE_OFFSET 2 +#define TSNEP_RX_ASSIGN_ETHER_TYPE_COUNT 2 /* tsnep gate control list operation */ struct tsnep_gcl_operation { diff --git a/drivers/net/ethernet/engleder/tsnep_main.c b/drivers/net/ethernet/engleder/tsnep_main.c index a5f7152a1716..48fb391951dd 100644 --- a/drivers/net/ethernet/engleder/tsnep_main.c +++ b/drivers/net/ethernet/engleder/tsnep_main.c @@ -27,10 +27,10 @@ #include <linux/phy.h> #include <linux/iopoll.h> -#define RX_SKB_LENGTH (round_up(TSNEP_RX_INLINE_METADATA_SIZE + ETH_HLEN + \ - TSNEP_MAX_FRAME_SIZE + ETH_FCS_LEN, 4)) -#define RX_SKB_RESERVE ((16 - TSNEP_RX_INLINE_METADATA_SIZE) + NET_IP_ALIGN) -#define RX_SKB_ALLOC_LENGTH (RX_SKB_RESERVE + RX_SKB_LENGTH) +#define TSNEP_SKB_PAD (NET_SKB_PAD + NET_IP_ALIGN) +#define TSNEP_HEADROOM ALIGN(TSNEP_SKB_PAD, 4) +#define TSNEP_MAX_RX_BUF_SIZE (PAGE_SIZE - TSNEP_HEADROOM - \ + SKB_DATA_ALIGN(sizeof(struct skb_shared_info))) #ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT #define DMA_ADDR_HIGH(dma_addr) ((u32)(((dma_addr) >> 32) & 0xFFFFFFFF)) @@ -60,22 +60,29 @@ static irqreturn_t tsnep_irq(int irq, void *arg) iowrite32(active, adapter->addr + ECM_INT_ACKNOWLEDGE); /* handle link interrupt */ - if ((active & ECM_INT_LINK) != 0) { - if (adapter->netdev->phydev) - phy_mac_interrupt(adapter->netdev->phydev); - } + if ((active & ECM_INT_LINK) != 0) + phy_mac_interrupt(adapter->netdev->phydev); /* handle TX/RX queue 0 interrupt */ if ((active & adapter->queue[0].irq_mask) != 0) { - if (adapter->netdev) { - tsnep_disable_irq(adapter, adapter->queue[0].irq_mask); - napi_schedule(&adapter->queue[0].napi); - } + tsnep_disable_irq(adapter, adapter->queue[0].irq_mask); + napi_schedule(&adapter->queue[0].napi); } return IRQ_HANDLED; } +static irqreturn_t tsnep_irq_txrx(int irq, void *arg) +{ + struct tsnep_queue *queue = arg; + + /* handle TX/RX queue interrupt */ + tsnep_disable_irq(queue->adapter, queue->irq_mask); + napi_schedule(&queue->napi); + + return IRQ_HANDLED; +} + static int tsnep_mdiobus_read(struct mii_bus *bus, int addr, int regnum) { struct tsnep_adapter *adapter = bus->priv; @@ -124,30 +131,51 @@ static int tsnep_mdiobus_write(struct mii_bus *bus, int addr, int regnum, return 0; } +static void tsnep_set_link_mode(struct tsnep_adapter *adapter) +{ + u32 mode; + + switch (adapter->phydev->speed) { + case SPEED_100: + mode = ECM_LINK_MODE_100; + break; + case SPEED_1000: + mode = ECM_LINK_MODE_1000; + break; + default: + mode = ECM_LINK_MODE_OFF; + break; + } + iowrite32(mode, adapter->addr + ECM_STATUS); +} + static void tsnep_phy_link_status_change(struct net_device *netdev) { struct tsnep_adapter *adapter = netdev_priv(netdev); struct phy_device *phydev = netdev->phydev; - u32 mode; - if (phydev->link) { - switch (phydev->speed) { - case SPEED_100: - mode = ECM_LINK_MODE_100; - break; - case SPEED_1000: - mode = ECM_LINK_MODE_1000; - break; - default: - mode = ECM_LINK_MODE_OFF; - break; - } - iowrite32(mode, adapter->addr + ECM_STATUS); - } + if (phydev->link) + tsnep_set_link_mode(adapter); phy_print_status(netdev->phydev); } +static int tsnep_phy_loopback(struct tsnep_adapter *adapter, bool enable) +{ + int retval; + + retval = phy_loopback(adapter->phydev, enable); + + /* PHY link state change is not signaled if loopback is enabled, it + * would delay a working loopback anyway, let's ensure that loopback + * is working immediately by setting link mode directly + */ + if (!retval && enable) + tsnep_set_link_mode(adapter); + + return retval; +} + static int tsnep_phy_open(struct tsnep_adapter *adapter) { struct phy_device *phydev; @@ -241,14 +269,14 @@ alloc_failed: return retval; } -static void tsnep_tx_activate(struct tsnep_tx *tx, int index, bool last) +static void tsnep_tx_activate(struct tsnep_tx *tx, int index, int length, + bool last) { struct tsnep_tx_entry *entry = &tx->entry[index]; entry->properties = 0; if (entry->skb) { - entry->properties = - skb_pagelen(entry->skb) & TSNEP_DESC_LENGTH_MASK; + entry->properties = length & TSNEP_DESC_LENGTH_MASK; entry->properties |= TSNEP_DESC_INTERRUPT_FLAG; if (skb_shinfo(entry->skb)->tx_flags & SKBTX_IN_PROGRESS) entry->properties |= TSNEP_DESC_EXTENDED_WRITEBACK_FLAG; @@ -313,6 +341,7 @@ static int tsnep_tx_map(struct sk_buff *skb, struct tsnep_tx *tx, int count) struct tsnep_tx_entry *entry; unsigned int len; dma_addr_t dma; + int map_len = 0; int i; for (i = 0; i < count; i++) { @@ -335,15 +364,18 @@ static int tsnep_tx_map(struct sk_buff *skb, struct tsnep_tx *tx, int count) dma_unmap_addr_set(entry, dma, dma); entry->desc->tx = __cpu_to_le64(dma); + + map_len += len; } - return 0; + return map_len; } -static void tsnep_tx_unmap(struct tsnep_tx *tx, int index, int count) +static int tsnep_tx_unmap(struct tsnep_tx *tx, int index, int count) { struct device *dmadev = tx->adapter->dmadev; struct tsnep_tx_entry *entry; + int map_len = 0; int i; for (i = 0; i < count; i++) { @@ -360,9 +392,12 @@ static void tsnep_tx_unmap(struct tsnep_tx *tx, int index, int count) dma_unmap_addr(entry, dma), dma_unmap_len(entry, len), DMA_TO_DEVICE); + map_len += entry->len; entry->len = 0; } } + + return map_len; } static netdev_tx_t tsnep_xmit_frame_ring(struct sk_buff *skb, @@ -371,6 +406,7 @@ static netdev_tx_t tsnep_xmit_frame_ring(struct sk_buff *skb, unsigned long flags; int count = 1; struct tsnep_tx_entry *entry; + int length; int i; int retval; @@ -394,7 +430,7 @@ static netdev_tx_t tsnep_xmit_frame_ring(struct sk_buff *skb, entry->skb = skb; retval = tsnep_tx_map(skb, tx, count); - if (retval != 0) { + if (retval < 0) { tsnep_tx_unmap(tx, tx->write, count); dev_kfree_skb_any(entry->skb); entry->skb = NULL; @@ -407,12 +443,13 @@ static netdev_tx_t tsnep_xmit_frame_ring(struct sk_buff *skb, return NETDEV_TX_OK; } + length = retval; if (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; for (i = 0; i < count; i++) - tsnep_tx_activate(tx, (tx->write + i) % TSNEP_RING_SIZE, + tsnep_tx_activate(tx, (tx->write + i) % TSNEP_RING_SIZE, length, i == (count - 1)); tx->write = (tx->write + count) % TSNEP_RING_SIZE; @@ -428,9 +465,6 @@ static netdev_tx_t tsnep_xmit_frame_ring(struct sk_buff *skb, netif_stop_queue(tx->adapter->netdev); } - tx->packets++; - tx->bytes += skb_pagelen(entry->skb) + ETH_FCS_LEN; - spin_unlock_irqrestore(&tx->lock, flags); return NETDEV_TX_OK; @@ -442,6 +476,7 @@ static bool tsnep_tx_poll(struct tsnep_tx *tx, int napi_budget) int budget = 128; struct tsnep_tx_entry *entry; int count; + int length; spin_lock_irqsave(&tx->lock, flags); @@ -464,7 +499,7 @@ static bool tsnep_tx_poll(struct tsnep_tx *tx, int napi_budget) if (skb_shinfo(entry->skb)->nr_frags > 0) count += skb_shinfo(entry->skb)->nr_frags; - tsnep_tx_unmap(tx, tx->read, count); + length = tsnep_tx_unmap(tx, tx->read, count); if ((skb_shinfo(entry->skb)->tx_flags & SKBTX_IN_PROGRESS) && (__le32_to_cpu(entry->desc_wb->properties) & @@ -491,6 +526,9 @@ static bool tsnep_tx_poll(struct tsnep_tx *tx, int napi_budget) tx->read = (tx->read + count) % TSNEP_RING_SIZE; + tx->packets++; + tx->bytes += length + ETH_FCS_LEN; + budget--; } while (likely(budget)); @@ -505,7 +543,7 @@ static bool tsnep_tx_poll(struct tsnep_tx *tx, int napi_budget) } static int tsnep_tx_open(struct tsnep_adapter *adapter, void __iomem *addr, - struct tsnep_tx *tx) + int queue_index, struct tsnep_tx *tx) { dma_addr_t dma; int retval; @@ -513,6 +551,7 @@ static int tsnep_tx_open(struct tsnep_adapter *adapter, void __iomem *addr, memset(tx, 0, sizeof(*tx)); tx->adapter = adapter; tx->addr = addr; + tx->queue_index = queue_index; retval = tsnep_tx_ring_init(tx); if (retval) @@ -548,14 +587,15 @@ static void tsnep_rx_ring_cleanup(struct tsnep_rx *rx) for (i = 0; i < TSNEP_RING_SIZE; i++) { entry = &rx->entry[i]; - if (dma_unmap_addr(entry, dma)) - dma_unmap_single(dmadev, dma_unmap_addr(entry, dma), - dma_unmap_len(entry, len), - DMA_FROM_DEVICE); - if (entry->skb) - dev_kfree_skb(entry->skb); + if (entry->page) + page_pool_put_full_page(rx->page_pool, entry->page, + false); + entry->page = NULL; } + if (rx->page_pool) + page_pool_destroy(rx->page_pool); + memset(rx->entry, 0, sizeof(rx->entry)); for (i = 0; i < TSNEP_RING_PAGE_COUNT; i++) { @@ -568,31 +608,19 @@ static void tsnep_rx_ring_cleanup(struct tsnep_rx *rx) } } -static int tsnep_rx_alloc_and_map_skb(struct tsnep_rx *rx, - struct tsnep_rx_entry *entry) +static int tsnep_rx_alloc_buffer(struct tsnep_rx *rx, + struct tsnep_rx_entry *entry) { - struct device *dmadev = rx->adapter->dmadev; - struct sk_buff *skb; - dma_addr_t dma; + struct page *page; - skb = __netdev_alloc_skb(rx->adapter->netdev, RX_SKB_ALLOC_LENGTH, - GFP_ATOMIC | GFP_DMA); - if (!skb) + page = page_pool_dev_alloc_pages(rx->page_pool); + if (unlikely(!page)) return -ENOMEM; - skb_reserve(skb, RX_SKB_RESERVE); - - dma = dma_map_single(dmadev, skb->data, RX_SKB_LENGTH, - DMA_FROM_DEVICE); - if (dma_mapping_error(dmadev, dma)) { - dev_kfree_skb(skb); - return -ENOMEM; - } - - entry->skb = skb; - entry->len = RX_SKB_LENGTH; - dma_unmap_addr_set(entry, dma, dma); - entry->desc->rx = __cpu_to_le64(dma); + entry->page = page; + entry->len = TSNEP_MAX_RX_BUF_SIZE; + entry->dma = page_pool_get_dma_addr(entry->page); + entry->desc->rx = __cpu_to_le64(entry->dma + TSNEP_SKB_PAD); return 0; } @@ -601,6 +629,7 @@ static int tsnep_rx_ring_init(struct tsnep_rx *rx) { struct device *dmadev = rx->adapter->dmadev; struct tsnep_rx_entry *entry; + struct page_pool_params pp_params = { 0 }; struct tsnep_rx_entry *next_entry; int i, j; int retval; @@ -622,12 +651,28 @@ static int tsnep_rx_ring_init(struct tsnep_rx *rx) entry->desc_dma = rx->page_dma[i] + TSNEP_DESC_SIZE * j; } } + + pp_params.flags = PP_FLAG_DMA_MAP | PP_FLAG_DMA_SYNC_DEV; + pp_params.order = 0; + pp_params.pool_size = TSNEP_RING_SIZE; + pp_params.nid = dev_to_node(dmadev); + pp_params.dev = dmadev; + pp_params.dma_dir = DMA_FROM_DEVICE; + pp_params.max_len = TSNEP_MAX_RX_BUF_SIZE; + pp_params.offset = TSNEP_SKB_PAD; + rx->page_pool = page_pool_create(&pp_params); + if (IS_ERR(rx->page_pool)) { + retval = PTR_ERR(rx->page_pool); + rx->page_pool = NULL; + goto failed; + } + for (i = 0; i < TSNEP_RING_SIZE; i++) { entry = &rx->entry[i]; next_entry = &rx->entry[(i + 1) % TSNEP_RING_SIZE]; entry->desc->next = __cpu_to_le64(next_entry->desc_dma); - retval = tsnep_rx_alloc_and_map_skb(rx, entry); + retval = tsnep_rx_alloc_buffer(rx, entry); if (retval) goto failed; } @@ -643,7 +688,7 @@ static void tsnep_rx_activate(struct tsnep_rx *rx, int index) { struct tsnep_rx_entry *entry = &rx->entry[index]; - /* RX_SKB_LENGTH is a multiple of 4 */ + /* TSNEP_MAX_RX_BUF_SIZE is a multiple of 4 */ entry->properties = entry->len & TSNEP_DESC_LENGTH_MASK; entry->properties |= TSNEP_DESC_INTERRUPT_FLAG; if (index == rx->increment_owner_counter) { @@ -666,19 +711,52 @@ static void tsnep_rx_activate(struct tsnep_rx *rx, int index) entry->desc->properties = __cpu_to_le32(entry->properties); } +static struct sk_buff *tsnep_build_skb(struct tsnep_rx *rx, struct page *page, + int length) +{ + struct sk_buff *skb; + + skb = napi_build_skb(page_address(page), PAGE_SIZE); + if (unlikely(!skb)) + return NULL; + + /* update pointers within the skb to store the data */ + skb_reserve(skb, TSNEP_SKB_PAD + TSNEP_RX_INLINE_METADATA_SIZE); + __skb_put(skb, length - TSNEP_RX_INLINE_METADATA_SIZE - ETH_FCS_LEN); + + if (rx->adapter->hwtstamp_config.rx_filter == HWTSTAMP_FILTER_ALL) { + struct skb_shared_hwtstamps *hwtstamps = skb_hwtstamps(skb); + struct tsnep_rx_inline *rx_inline = + (struct tsnep_rx_inline *)(page_address(page) + + TSNEP_SKB_PAD); + + skb_shinfo(skb)->tx_flags |= + SKBTX_HW_TSTAMP_NETDEV; + memset(hwtstamps, 0, sizeof(*hwtstamps)); + hwtstamps->netdev_data = rx_inline; + } + + skb_record_rx_queue(skb, rx->queue_index); + skb->protocol = eth_type_trans(skb, rx->adapter->netdev); + + return skb; +} + static int tsnep_rx_poll(struct tsnep_rx *rx, struct napi_struct *napi, int budget) { struct device *dmadev = rx->adapter->dmadev; int done = 0; + enum dma_data_direction dma_dir; struct tsnep_rx_entry *entry; + struct page *page; struct sk_buff *skb; - size_t len; - dma_addr_t dma; int length; bool enable = false; int retval; + dma_dir = page_pool_get_dma_dir(rx->page_pool); + while (likely(done < budget)) { entry = &rx->entry[rx->read]; if ((__le32_to_cpu(entry->desc_wb->properties) & @@ -691,42 +769,34 @@ static int tsnep_rx_poll(struct tsnep_rx *rx, struct napi_struct *napi, */ dma_rmb(); - skb = entry->skb; - len = dma_unmap_len(entry, len); - dma = dma_unmap_addr(entry, dma); + prefetch(page_address(entry->page) + TSNEP_SKB_PAD); + length = __le32_to_cpu(entry->desc_wb->properties) & + TSNEP_DESC_LENGTH_MASK; + dma_sync_single_range_for_cpu(dmadev, entry->dma, TSNEP_SKB_PAD, + length, dma_dir); + page = entry->page; /* forward skb only if allocation is successful, otherwise - * skb is reused and frame dropped + * page is reused and frame dropped */ - retval = tsnep_rx_alloc_and_map_skb(rx, entry); + retval = tsnep_rx_alloc_buffer(rx, entry); if (!retval) { - dma_unmap_single(dmadev, dma, len, DMA_FROM_DEVICE); - - length = __le32_to_cpu(entry->desc_wb->properties) & - TSNEP_DESC_LENGTH_MASK; - skb_put(skb, length - ETH_FCS_LEN); - if (rx->adapter->hwtstamp_config.rx_filter == - HWTSTAMP_FILTER_ALL) { - struct skb_shared_hwtstamps *hwtstamps = - skb_hwtstamps(skb); - struct tsnep_rx_inline *rx_inline = - (struct tsnep_rx_inline *)skb->data; - - skb_shinfo(skb)->tx_flags |= - SKBTX_HW_TSTAMP_NETDEV; - memset(hwtstamps, 0, sizeof(*hwtstamps)); - hwtstamps->netdev_data = rx_inline; - } - skb_pull(skb, TSNEP_RX_INLINE_METADATA_SIZE); - skb->protocol = eth_type_trans(skb, - rx->adapter->netdev); + skb = tsnep_build_skb(rx, page, length); + if (skb) { + page_pool_release_page(rx->page_pool, page); - rx->packets++; - rx->bytes += length - TSNEP_RX_INLINE_METADATA_SIZE; - if (skb->pkt_type == PACKET_MULTICAST) - rx->multicast++; + rx->packets++; + rx->bytes += length - + TSNEP_RX_INLINE_METADATA_SIZE; + if (skb->pkt_type == PACKET_MULTICAST) + rx->multicast++; - napi_gro_receive(napi, skb); + napi_gro_receive(napi, skb); + } else { + page_pool_recycle_direct(rx->page_pool, page); + + rx->dropped++; + } done++; } else { rx->dropped++; @@ -752,7 +822,7 @@ static int tsnep_rx_poll(struct tsnep_rx *rx, struct napi_struct *napi, } static int tsnep_rx_open(struct tsnep_adapter *adapter, void __iomem *addr, - struct tsnep_rx *rx) + int queue_index, struct tsnep_rx *rx) { dma_addr_t dma; int i; @@ -761,6 +831,7 @@ static int tsnep_rx_open(struct tsnep_adapter *adapter, void __iomem *addr, memset(rx, 0, sizeof(*rx)); rx->adapter = adapter; rx->addr = addr; + rx->queue_index = queue_index; retval = tsnep_rx_ring_init(rx); if (retval) @@ -821,6 +892,56 @@ static int tsnep_poll(struct napi_struct *napi, int budget) return min(done, budget - 1); } +static int tsnep_request_irq(struct tsnep_queue *queue, bool first) +{ + const char *name = netdev_name(queue->adapter->netdev); + irq_handler_t handler; + void *dev; + int retval; + + if (first) { + sprintf(queue->name, "%s-mac", name); + handler = tsnep_irq; + dev = queue->adapter; + } else { + if (queue->tx && queue->rx) + sprintf(queue->name, "%s-txrx-%d", name, + queue->rx->queue_index); + else if (queue->tx) + sprintf(queue->name, "%s-tx-%d", name, + queue->tx->queue_index); + else + sprintf(queue->name, "%s-rx-%d", name, + queue->rx->queue_index); + handler = tsnep_irq_txrx; + dev = queue; + } + + retval = request_irq(queue->irq, handler, 0, queue->name, dev); + if (retval) { + /* if name is empty, then interrupt won't be freed */ + memset(queue->name, 0, sizeof(queue->name)); + } + + return retval; +} + +static void tsnep_free_irq(struct tsnep_queue *queue, bool first) +{ + void *dev; + + if (!strlen(queue->name)) + return; + + if (first) + dev = queue->adapter; + else + dev = queue; + + free_irq(queue->irq, dev); + memset(queue->name, 0, sizeof(queue->name)); +} + static int tsnep_netdev_open(struct net_device *netdev) { struct tsnep_adapter *adapter = netdev_priv(netdev); @@ -830,15 +951,11 @@ static int tsnep_netdev_open(struct net_device *netdev) int rx_queue_index = 0; int retval; - retval = tsnep_phy_open(adapter); - if (retval) - return retval; - for (i = 0; i < adapter->num_queues; i++) { adapter->queue[i].adapter = adapter; if (adapter->queue[i].tx) { addr = adapter->addr + TSNEP_QUEUE(tx_queue_index); - retval = tsnep_tx_open(adapter, addr, + retval = tsnep_tx_open(adapter, addr, tx_queue_index, adapter->queue[i].tx); if (retval) goto failed; @@ -847,11 +964,20 @@ static int tsnep_netdev_open(struct net_device *netdev) if (adapter->queue[i].rx) { addr = adapter->addr + TSNEP_QUEUE(rx_queue_index); retval = tsnep_rx_open(adapter, addr, + rx_queue_index, adapter->queue[i].rx); if (retval) goto failed; rx_queue_index++; } + + retval = tsnep_request_irq(&adapter->queue[i], i == 0); + if (retval) { + netif_err(adapter, drv, adapter->netdev, + "can't get assigned irq %d.\n", + adapter->queue[i].irq); + goto failed; + } } retval = netif_set_real_num_tx_queues(adapter->netdev, @@ -863,9 +989,14 @@ static int tsnep_netdev_open(struct net_device *netdev) if (retval) goto failed; + tsnep_enable_irq(adapter, ECM_INT_LINK); + retval = tsnep_phy_open(adapter); + if (retval) + goto phy_failed; + for (i = 0; i < adapter->num_queues; i++) { netif_napi_add(adapter->netdev, &adapter->queue[i].napi, - tsnep_poll, 64); + tsnep_poll); napi_enable(&adapter->queue[i].napi); tsnep_enable_irq(adapter, adapter->queue[i].irq_mask); @@ -873,14 +1004,18 @@ static int tsnep_netdev_open(struct net_device *netdev) return 0; +phy_failed: + tsnep_disable_irq(adapter, ECM_INT_LINK); + tsnep_phy_close(adapter); failed: for (i = 0; i < adapter->num_queues; i++) { + tsnep_free_irq(&adapter->queue[i], i == 0); + if (adapter->queue[i].rx) tsnep_rx_close(adapter->queue[i].rx); if (adapter->queue[i].tx) tsnep_tx_close(adapter->queue[i].tx); } - tsnep_phy_close(adapter); return retval; } @@ -889,20 +1024,23 @@ static int tsnep_netdev_close(struct net_device *netdev) struct tsnep_adapter *adapter = netdev_priv(netdev); int i; + tsnep_disable_irq(adapter, ECM_INT_LINK); + tsnep_phy_close(adapter); + for (i = 0; i < adapter->num_queues; i++) { tsnep_disable_irq(adapter, adapter->queue[i].irq_mask); napi_disable(&adapter->queue[i].napi); netif_napi_del(&adapter->queue[i].napi); + tsnep_free_irq(&adapter->queue[i], i == 0); + if (adapter->queue[i].rx) tsnep_rx_close(adapter->queue[i].rx); if (adapter->queue[i].tx) tsnep_tx_close(adapter->queue[i].tx); } - tsnep_phy_close(adapter); - return 0; } @@ -1017,6 +1155,22 @@ static int tsnep_netdev_set_mac_address(struct net_device *netdev, void *addr) return 0; } +static int tsnep_netdev_set_features(struct net_device *netdev, + netdev_features_t features) +{ + struct tsnep_adapter *adapter = netdev_priv(netdev); + netdev_features_t changed = netdev->features ^ features; + bool enable; + int retval = 0; + + if (changed & NETIF_F_LOOPBACK) { + enable = !!(features & NETIF_F_LOOPBACK); + retval = tsnep_phy_loopback(adapter, enable); + } + + return retval; +} + static ktime_t tsnep_netdev_get_tstamp(struct net_device *netdev, const struct skb_shared_hwtstamps *hwtstamps, bool cycles) @@ -1038,9 +1192,9 @@ static const struct net_device_ops tsnep_netdev_ops = { .ndo_start_xmit = tsnep_netdev_xmit_frame, .ndo_eth_ioctl = tsnep_netdev_ioctl, .ndo_set_rx_mode = tsnep_netdev_set_multicast, - .ndo_get_stats64 = tsnep_netdev_get_stats64, .ndo_set_mac_address = tsnep_netdev_set_mac_address, + .ndo_set_features = tsnep_netdev_set_features, .ndo_get_tstamp = tsnep_netdev_get_tstamp, .ndo_setup_tc = tsnep_tc_setup, }; @@ -1141,6 +1295,52 @@ static int tsnep_phy_init(struct tsnep_adapter *adapter) return 0; } +static int tsnep_queue_init(struct tsnep_adapter *adapter, int queue_count) +{ + u32 irq_mask = ECM_INT_TX_0 | ECM_INT_RX_0; + char name[8]; + int i; + int retval; + + /* one TX/RX queue pair for netdev is mandatory */ + if (platform_irq_count(adapter->pdev) == 1) + retval = platform_get_irq(adapter->pdev, 0); + else + retval = platform_get_irq_byname(adapter->pdev, "mac"); + if (retval < 0) + return retval; + adapter->num_tx_queues = 1; + adapter->num_rx_queues = 1; + adapter->num_queues = 1; + adapter->queue[0].irq = retval; + adapter->queue[0].tx = &adapter->tx[0]; + adapter->queue[0].rx = &adapter->rx[0]; + adapter->queue[0].irq_mask = irq_mask; + + adapter->netdev->irq = adapter->queue[0].irq; + + /* add additional TX/RX queue pairs only if dedicated interrupt is + * available + */ + for (i = 1; i < queue_count; i++) { + sprintf(name, "txrx-%d", i); + retval = platform_get_irq_byname_optional(adapter->pdev, name); + if (retval < 0) + break; + + adapter->num_tx_queues++; + adapter->num_rx_queues++; + adapter->num_queues++; + adapter->queue[i].irq = retval; + adapter->queue[i].tx = &adapter->tx[i]; + adapter->queue[i].rx = &adapter->rx[i]; + adapter->queue[i].irq_mask = + irq_mask << (ECM_INT_TXRX_SHIFT * i); + } + + return 0; +} + static int tsnep_probe(struct platform_device *pdev) { struct tsnep_adapter *adapter; @@ -1149,6 +1349,7 @@ static int tsnep_probe(struct platform_device *pdev) u32 type; int revision; int version; + int queue_count; int retval; netdev = devm_alloc_etherdev_mqs(&pdev->dev, @@ -1170,41 +1371,39 @@ static int tsnep_probe(struct platform_device *pdev) netdev->max_mtu = TSNEP_MAX_FRAME_SIZE; mutex_init(&adapter->gate_control_lock); + mutex_init(&adapter->rxnfc_lock); + INIT_LIST_HEAD(&adapter->rxnfc_rules); io = platform_get_resource(pdev, IORESOURCE_MEM, 0); adapter->addr = devm_ioremap_resource(&pdev->dev, io); if (IS_ERR(adapter->addr)) return PTR_ERR(adapter->addr); - adapter->irq = platform_get_irq(pdev, 0); netdev->mem_start = io->start; netdev->mem_end = io->end; - netdev->irq = adapter->irq; type = ioread32(adapter->addr + ECM_TYPE); revision = (type & ECM_REVISION_MASK) >> ECM_REVISION_SHIFT; version = (type & ECM_VERSION_MASK) >> ECM_VERSION_SHIFT; + queue_count = (type & ECM_QUEUE_COUNT_MASK) >> ECM_QUEUE_COUNT_SHIFT; adapter->gate_control = type & ECM_GATE_CONTROL; - - adapter->num_tx_queues = TSNEP_QUEUES; - adapter->num_rx_queues = TSNEP_QUEUES; - adapter->num_queues = TSNEP_QUEUES; - adapter->queue[0].tx = &adapter->tx[0]; - adapter->queue[0].rx = &adapter->rx[0]; - adapter->queue[0].irq_mask = ECM_INT_TX_0 | ECM_INT_RX_0; + adapter->rxnfc_max = TSNEP_RX_ASSIGN_ETHER_TYPE_COUNT; tsnep_disable_irq(adapter, ECM_INT_ALL); - retval = devm_request_irq(&adapter->pdev->dev, adapter->irq, tsnep_irq, - 0, TSNEP, adapter); - if (retval != 0) { - dev_err(&adapter->pdev->dev, "can't get assigned irq %d.\n", - adapter->irq); + + retval = tsnep_queue_init(adapter, queue_count); + if (retval) + return retval; + + retval = dma_set_mask_and_coherent(&adapter->pdev->dev, + DMA_BIT_MASK(64)); + if (retval) { + dev_err(&adapter->pdev->dev, "no usable DMA configuration.\n"); return retval; } - tsnep_enable_irq(adapter, ECM_INT_LINK); retval = tsnep_mac_init(adapter); if (retval) - goto mac_init_failed; + return retval; retval = tsnep_mdio_init(adapter); if (retval) @@ -1222,10 +1421,14 @@ static int tsnep_probe(struct platform_device *pdev) if (retval) goto tc_init_failed; + retval = tsnep_rxnfc_init(adapter); + if (retval) + goto rxnfc_init_failed; + netdev->netdev_ops = &tsnep_netdev_ops; netdev->ethtool_ops = &tsnep_ethtool_ops; netdev->features = NETIF_F_SG; - netdev->hw_features = netdev->features; + netdev->hw_features = netdev->features | NETIF_F_LOOPBACK; /* carrier off reporting is important to ethtool even BEFORE open */ netif_carrier_off(netdev); @@ -1242,6 +1445,8 @@ static int tsnep_probe(struct platform_device *pdev) return 0; register_failed: + tsnep_rxnfc_cleanup(adapter); +rxnfc_init_failed: tsnep_tc_cleanup(adapter); tc_init_failed: tsnep_ptp_cleanup(adapter); @@ -1250,8 +1455,6 @@ phy_init_failed: if (adapter->mdiobus) mdiobus_unregister(adapter->mdiobus); mdio_init_failed: -mac_init_failed: - tsnep_disable_irq(adapter, ECM_INT_ALL); return retval; } @@ -1261,6 +1464,8 @@ static int tsnep_remove(struct platform_device *pdev) unregister_netdev(adapter->netdev); + tsnep_rxnfc_cleanup(adapter); + tsnep_tc_cleanup(adapter); tsnep_ptp_cleanup(adapter); diff --git a/drivers/net/ethernet/engleder/tsnep_rxnfc.c b/drivers/net/ethernet/engleder/tsnep_rxnfc.c new file mode 100644 index 000000000000..9ac2a0cf3833 --- /dev/null +++ b/drivers/net/ethernet/engleder/tsnep_rxnfc.c @@ -0,0 +1,307 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (C) 2022 Gerhard Engleder <gerhard@engleder-embedded.com> */ + +#include "tsnep.h" + +#define ETHER_TYPE_FULL_MASK ((__force __be16)~0) + +static void tsnep_enable_rule(struct tsnep_adapter *adapter, + struct tsnep_rxnfc_rule *rule) +{ + u8 rx_assign; + void __iomem *addr; + + rx_assign = TSNEP_RX_ASSIGN_ACTIVE; + rx_assign |= (rule->queue_index << TSNEP_RX_ASSIGN_QUEUE_SHIFT) & + TSNEP_RX_ASSIGN_QUEUE_MASK; + + addr = adapter->addr + TSNEP_RX_ASSIGN_ETHER_TYPE + + TSNEP_RX_ASSIGN_ETHER_TYPE_OFFSET * rule->location; + iowrite16(rule->filter.ether_type, addr); + + /* enable rule after all settings are done */ + addr = adapter->addr + TSNEP_RX_ASSIGN + + TSNEP_RX_ASSIGN_OFFSET * rule->location; + iowrite8(rx_assign, addr); +} + +static void tsnep_disable_rule(struct tsnep_adapter *adapter, + struct tsnep_rxnfc_rule *rule) +{ + void __iomem *addr; + + addr = adapter->addr + TSNEP_RX_ASSIGN + + TSNEP_RX_ASSIGN_OFFSET * rule->location; + iowrite8(0, addr); +} + +static struct tsnep_rxnfc_rule *tsnep_get_rule(struct tsnep_adapter *adapter, + int location) +{ + struct tsnep_rxnfc_rule *rule; + + list_for_each_entry(rule, &adapter->rxnfc_rules, list) { + if (rule->location == location) + return rule; + if (rule->location > location) + break; + } + + return NULL; +} + +static void tsnep_add_rule(struct tsnep_adapter *adapter, + struct tsnep_rxnfc_rule *rule) +{ + struct tsnep_rxnfc_rule *pred, *cur; + + tsnep_enable_rule(adapter, rule); + + pred = NULL; + list_for_each_entry(cur, &adapter->rxnfc_rules, list) { + if (cur->location >= rule->location) + break; + pred = cur; + } + + list_add(&rule->list, pred ? &pred->list : &adapter->rxnfc_rules); + adapter->rxnfc_count++; +} + +static void tsnep_delete_rule(struct tsnep_adapter *adapter, + struct tsnep_rxnfc_rule *rule) +{ + tsnep_disable_rule(adapter, rule); + + list_del(&rule->list); + adapter->rxnfc_count--; + + kfree(rule); +} + +static void tsnep_flush_rules(struct tsnep_adapter *adapter) +{ + struct tsnep_rxnfc_rule *rule, *tmp; + + mutex_lock(&adapter->rxnfc_lock); + + list_for_each_entry_safe(rule, tmp, &adapter->rxnfc_rules, list) + tsnep_delete_rule(adapter, rule); + + mutex_unlock(&adapter->rxnfc_lock); +} + +int tsnep_rxnfc_get_rule(struct tsnep_adapter *adapter, + struct ethtool_rxnfc *cmd) +{ + struct ethtool_rx_flow_spec *fsp = &cmd->fs; + struct tsnep_rxnfc_rule *rule = NULL; + + cmd->data = adapter->rxnfc_max; + + mutex_lock(&adapter->rxnfc_lock); + + rule = tsnep_get_rule(adapter, fsp->location); + if (!rule) { + mutex_unlock(&adapter->rxnfc_lock); + + return -ENOENT; + } + + fsp->flow_type = ETHER_FLOW; + fsp->ring_cookie = rule->queue_index; + + if (rule->filter.type == TSNEP_RXNFC_ETHER_TYPE) { + fsp->h_u.ether_spec.h_proto = htons(rule->filter.ether_type); + fsp->m_u.ether_spec.h_proto = ETHER_TYPE_FULL_MASK; + } + + mutex_unlock(&adapter->rxnfc_lock); + + return 0; +} + +int tsnep_rxnfc_get_all(struct tsnep_adapter *adapter, + struct ethtool_rxnfc *cmd, + u32 *rule_locs) +{ + struct tsnep_rxnfc_rule *rule; + int count = 0; + + cmd->data = adapter->rxnfc_max; + + mutex_lock(&adapter->rxnfc_lock); + + list_for_each_entry(rule, &adapter->rxnfc_rules, list) { + if (count == cmd->rule_cnt) { + mutex_unlock(&adapter->rxnfc_lock); + + return -EMSGSIZE; + } + + rule_locs[count] = rule->location; + count++; + } + + mutex_unlock(&adapter->rxnfc_lock); + + cmd->rule_cnt = count; + + return 0; +} + +static int tsnep_rxnfc_find_location(struct tsnep_adapter *adapter) +{ + struct tsnep_rxnfc_rule *tmp; + int location = 0; + + list_for_each_entry(tmp, &adapter->rxnfc_rules, list) { + if (tmp->location == location) + location++; + else + return location; + } + + if (location >= adapter->rxnfc_max) + return -ENOSPC; + + return location; +} + +static void tsnep_rxnfc_init_rule(struct tsnep_rxnfc_rule *rule, + const struct ethtool_rx_flow_spec *fsp) +{ + INIT_LIST_HEAD(&rule->list); + + rule->queue_index = fsp->ring_cookie; + rule->location = fsp->location; + + rule->filter.type = TSNEP_RXNFC_ETHER_TYPE; + rule->filter.ether_type = ntohs(fsp->h_u.ether_spec.h_proto); +} + +static int tsnep_rxnfc_check_rule(struct tsnep_adapter *adapter, + struct tsnep_rxnfc_rule *rule) +{ + struct net_device *dev = adapter->netdev; + struct tsnep_rxnfc_rule *tmp; + + list_for_each_entry(tmp, &adapter->rxnfc_rules, list) { + if (!memcmp(&rule->filter, &tmp->filter, sizeof(rule->filter)) && + tmp->location != rule->location) { + netdev_dbg(dev, "rule already exists\n"); + + return -EEXIST; + } + } + + return 0; +} + +int tsnep_rxnfc_add_rule(struct tsnep_adapter *adapter, + struct ethtool_rxnfc *cmd) +{ + struct net_device *netdev = adapter->netdev; + struct ethtool_rx_flow_spec *fsp = + (struct ethtool_rx_flow_spec *)&cmd->fs; + struct tsnep_rxnfc_rule *rule, *old_rule; + int retval; + + /* only EtherType is supported */ + if (fsp->flow_type != ETHER_FLOW || + !is_zero_ether_addr(fsp->m_u.ether_spec.h_dest) || + !is_zero_ether_addr(fsp->m_u.ether_spec.h_source) || + fsp->m_u.ether_spec.h_proto != ETHER_TYPE_FULL_MASK) { + netdev_dbg(netdev, "only ethernet protocol is supported\n"); + + return -EOPNOTSUPP; + } + + if (fsp->ring_cookie > + (TSNEP_RX_ASSIGN_QUEUE_MASK >> TSNEP_RX_ASSIGN_QUEUE_SHIFT)) { + netdev_dbg(netdev, "invalid action\n"); + + return -EINVAL; + } + + if (fsp->location != RX_CLS_LOC_ANY && + fsp->location >= adapter->rxnfc_max) { + netdev_dbg(netdev, "invalid location\n"); + + return -EINVAL; + } + + rule = kzalloc(sizeof(*rule), GFP_KERNEL); + if (!rule) + return -ENOMEM; + + mutex_lock(&adapter->rxnfc_lock); + + if (fsp->location == RX_CLS_LOC_ANY) { + retval = tsnep_rxnfc_find_location(adapter); + if (retval < 0) + goto failed; + fsp->location = retval; + } + + tsnep_rxnfc_init_rule(rule, fsp); + + retval = tsnep_rxnfc_check_rule(adapter, rule); + if (retval) + goto failed; + + old_rule = tsnep_get_rule(adapter, fsp->location); + if (old_rule) + tsnep_delete_rule(adapter, old_rule); + + tsnep_add_rule(adapter, rule); + + mutex_unlock(&adapter->rxnfc_lock); + + return 0; + +failed: + mutex_unlock(&adapter->rxnfc_lock); + kfree(rule); + return retval; +} + +int tsnep_rxnfc_del_rule(struct tsnep_adapter *adapter, + struct ethtool_rxnfc *cmd) +{ + struct ethtool_rx_flow_spec *fsp = + (struct ethtool_rx_flow_spec *)&cmd->fs; + struct tsnep_rxnfc_rule *rule; + + mutex_lock(&adapter->rxnfc_lock); + + rule = tsnep_get_rule(adapter, fsp->location); + if (!rule) { + mutex_unlock(&adapter->rxnfc_lock); + + return -ENOENT; + } + + tsnep_delete_rule(adapter, rule); + + mutex_unlock(&adapter->rxnfc_lock); + + return 0; +} + +int tsnep_rxnfc_init(struct tsnep_adapter *adapter) +{ + int i; + + /* disable all rules */ + for (i = 0; i < adapter->rxnfc_max; + i += sizeof(u32) / TSNEP_RX_ASSIGN_OFFSET) + iowrite32(0, adapter->addr + TSNEP_RX_ASSIGN + i); + + return 0; +} + +void tsnep_rxnfc_cleanup(struct tsnep_adapter *adapter) +{ + tsnep_flush_rules(adapter); +} diff --git a/drivers/net/ethernet/ethoc.c b/drivers/net/ethernet/ethoc.c index 437c5acfe222..95cbad198b4b 100644 --- a/drivers/net/ethernet/ethoc.c +++ b/drivers/net/ethernet/ethoc.c @@ -1224,7 +1224,7 @@ static int ethoc_probe(struct platform_device *pdev) netdev->ethtool_ops = ðoc_ethtool_ops; /* setup NAPI */ - netif_napi_add(netdev, &priv->napi, ethoc_poll, 64); + netif_napi_add(netdev, &priv->napi, ethoc_poll); spin_lock_init(&priv->lock); diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/faraday/ftgmac100.c index c03663785a8d..a03879a27b04 100644 --- a/drivers/net/ethernet/faraday/ftgmac100.c +++ b/drivers/net/ethernet/faraday/ftgmac100.c @@ -1063,8 +1063,8 @@ static int ftgmac100_mdiobus_write(struct mii_bus *bus, int phy_addr, static void ftgmac100_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *info) { - strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); - strlcpy(info->bus_info, dev_name(&netdev->dev), sizeof(info->bus_info)); + strscpy(info->driver, DRV_NAME, sizeof(info->driver)); + strscpy(info->bus_info, dev_name(&netdev->dev), sizeof(info->bus_info)); } static void @@ -1506,7 +1506,7 @@ static int ftgmac100_open(struct net_device *netdev) goto err_hw; /* Initialize NAPI */ - netif_napi_add(netdev, &priv->napi, ftgmac100_poll, 64); + netif_napi_add(netdev, &priv->napi, ftgmac100_poll); /* Grab our interrupt */ err = request_irq(netdev->irq, ftgmac100_interrupt, 0, netdev->name, netdev); @@ -1701,10 +1701,14 @@ err_register_mdiobus: static void ftgmac100_phy_disconnect(struct net_device *netdev) { + struct ftgmac100 *priv = netdev_priv(netdev); + if (!netdev->phydev) return; phy_disconnect(netdev->phydev); + if (of_phy_is_fixed_link(priv->dev->of_node)) + of_phy_deregister_fixed_link(priv->dev->of_node); } static void ftgmac100_destroy_mdio(struct net_device *netdev) @@ -1867,6 +1871,26 @@ static int ftgmac100_probe(struct platform_device *pdev) err = -EINVAL; goto err_phy_connect; } + } else if (np && of_phy_is_fixed_link(np)) { + struct phy_device *phy; + + err = of_phy_register_fixed_link(np); + if (err) { + dev_err(&pdev->dev, "Failed to register fixed PHY\n"); + goto err_phy_connect; + } + + phy = of_phy_get_and_connect(priv->netdev, np, + &ftgmac100_adjust_link); + if (!phy) { + dev_err(&pdev->dev, "Failed to connect to fixed PHY\n"); + of_phy_deregister_fixed_link(np); + err = -EINVAL; + goto err_phy_connect; + } + + /* Display what we found */ + phy_attached_info(phy); } else if (np && of_get_property(np, "phy-handle", NULL)) { struct phy_device *phy; diff --git a/drivers/net/ethernet/faraday/ftmac100.c b/drivers/net/ethernet/faraday/ftmac100.c index 8a341e2d5833..d95d78230828 100644 --- a/drivers/net/ethernet/faraday/ftmac100.c +++ b/drivers/net/ethernet/faraday/ftmac100.c @@ -807,8 +807,8 @@ static void ftmac100_mdio_write(struct net_device *netdev, int phy_id, int reg, static void ftmac100_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *info) { - strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); - strlcpy(info->bus_info, dev_name(&netdev->dev), sizeof(info->bus_info)); + strscpy(info->driver, DRV_NAME, sizeof(info->driver)); + strscpy(info->bus_info, dev_name(&netdev->dev), sizeof(info->bus_info)); } static int ftmac100_get_link_ksettings(struct net_device *netdev, @@ -1075,6 +1075,11 @@ static int ftmac100_probe(struct platform_device *pdev) SET_NETDEV_DEV(netdev, &pdev->dev); netdev->ethtool_ops = &ftmac100_ethtool_ops; netdev->netdev_ops = &ftmac100_netdev_ops; + netdev->max_mtu = MAX_PKT_SIZE; + + err = platform_get_ethdev_address(&pdev->dev, netdev); + if (err == -EPROBE_DEFER) + goto defer_get_mac; platform_set_drvdata(pdev, netdev); @@ -1086,7 +1091,7 @@ static int ftmac100_probe(struct platform_device *pdev) spin_lock_init(&priv->tx_lock); /* initialize NAPI */ - netif_napi_add(netdev, &priv->napi, ftmac100_poll, 64); + netif_napi_add(netdev, &priv->napi, ftmac100_poll); /* map io memory */ priv->res = request_mem_region(res->start, resource_size(res), @@ -1137,6 +1142,7 @@ err_ioremap: release_resource(priv->res); err_req_mem: netif_napi_del(&priv->napi); +defer_get_mac: free_netdev(netdev); err_alloc_etherdev: return err; diff --git a/drivers/net/ethernet/faraday/ftmac100.h b/drivers/net/ethernet/faraday/ftmac100.h index fe986f1673fc..8af32f9070f4 100644 --- a/drivers/net/ethernet/faraday/ftmac100.h +++ b/drivers/net/ethernet/faraday/ftmac100.h @@ -122,9 +122,9 @@ * Transmit descriptor, aligned to 16 bytes */ struct ftmac100_txdes { - unsigned int txdes0; - unsigned int txdes1; - unsigned int txdes2; /* TXBUF_BADR */ + __le32 txdes0; + __le32 txdes1; + __le32 txdes2; /* TXBUF_BADR */ unsigned int txdes3; /* not used by HW */ } __attribute__ ((aligned(16))); @@ -143,9 +143,9 @@ struct ftmac100_txdes { * Receive descriptor, aligned to 16 bytes */ struct ftmac100_rxdes { - unsigned int rxdes0; - unsigned int rxdes1; - unsigned int rxdes2; /* RXBUF_BADR */ + __le32 rxdes0; + __le32 rxdes1; + __le32 rxdes2; /* RXBUF_BADR */ unsigned int rxdes3; /* not used by HW */ } __attribute__ ((aligned(16))); diff --git a/drivers/net/ethernet/fealnx.c b/drivers/net/ethernet/fealnx.c index b3939a5f7b03..ed18450fd2cc 100644 --- a/drivers/net/ethernet/fealnx.c +++ b/drivers/net/ethernet/fealnx.c @@ -1809,8 +1809,8 @@ static void netdev_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *i { struct netdev_private *np = netdev_priv(dev); - strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); - strlcpy(info->bus_info, pci_name(np->pci_dev), sizeof(info->bus_info)); + strscpy(info->driver, DRV_NAME, sizeof(info->driver)); + strscpy(info->bus_info, pci_name(np->pci_dev), sizeof(info->bus_info)); } static int netdev_get_link_ksettings(struct net_device *dev, diff --git a/drivers/net/ethernet/freescale/Kconfig b/drivers/net/ethernet/freescale/Kconfig index e04e1c5cb013..ce866ae3df03 100644 --- a/drivers/net/ethernet/freescale/Kconfig +++ b/drivers/net/ethernet/freescale/Kconfig @@ -9,7 +9,7 @@ config NET_VENDOR_FREESCALE depends on FSL_SOC || QUICC_ENGINE || CPM1 || CPM2 || PPC_MPC512x || \ M523x || M527x || M5272 || M528x || M520x || M532x || \ ARCH_MXC || ARCH_MXS || (PPC_MPC52xx && PPC_BESTCOMM) || \ - ARCH_LAYERSCAPE || COMPILE_TEST + ARCH_LAYERSCAPE || ARCH_S32 || COMPILE_TEST help If you have a network (Ethernet) card belonging to this class, say Y. @@ -23,15 +23,16 @@ if NET_VENDOR_FREESCALE config FEC tristate "FEC ethernet controller (of ColdFire and some i.MX CPUs)" depends on (M523x || M527x || M5272 || M528x || M520x || M532x || \ - ARCH_MXC || SOC_IMX28 || COMPILE_TEST) + ARCH_MXC || ARCH_S32 || SOC_IMX28 || COMPILE_TEST) default ARCH_MXC || SOC_IMX28 if ARM depends on PTP_1588_CLOCK_OPTIONAL select CRC32 select PHYLIB + select PAGE_POOL imply NET_SELFTESTS help Say Y here if you want to use the built-in 10/100 Fast ethernet - controller on some Motorola ColdFire and Freescale i.MX processors. + controller on some Motorola ColdFire and Freescale i.MX/S32 processors. config FEC_MPC52xx tristate "FEC MPC52xx driver" diff --git a/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c b/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c index a770bab4d1ed..31cfa121333d 100644 --- a/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c +++ b/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c @@ -197,12 +197,15 @@ static int dpaa_rx_extra_headroom; #define dpaa_get_max_mtu() \ (dpaa_max_frm - (VLAN_ETH_HLEN + ETH_FCS_LEN)) +static void dpaa_eth_cgr_set_speed(struct mac_device *mac_dev, int speed); + static int dpaa_netdev_init(struct net_device *net_dev, const struct net_device_ops *dpaa_ops, u16 tx_timeout) { struct dpaa_priv *priv = netdev_priv(net_dev); struct device *dev = net_dev->dev.parent; + struct mac_device *mac_dev = priv->mac_dev; struct dpaa_percpu_priv *percpu_priv; const u8 *mac_addr; int i, err; @@ -216,10 +219,10 @@ static int dpaa_netdev_init(struct net_device *net_dev, } net_dev->netdev_ops = dpaa_ops; - mac_addr = priv->mac_dev->addr; + mac_addr = mac_dev->addr; - net_dev->mem_start = priv->mac_dev->res->start; - net_dev->mem_end = priv->mac_dev->res->end; + net_dev->mem_start = (unsigned long)mac_dev->vaddr; + net_dev->mem_end = (unsigned long)mac_dev->vaddr_end; net_dev->min_mtu = ETH_MIN_MTU; net_dev->max_mtu = dpaa_get_max_mtu(); @@ -246,7 +249,7 @@ static int dpaa_netdev_init(struct net_device *net_dev, eth_hw_addr_set(net_dev, mac_addr); } else { eth_hw_addr_random(net_dev); - err = priv->mac_dev->change_addr(priv->mac_dev->fman_mac, + err = mac_dev->change_addr(mac_dev->fman_mac, (const enet_addr_t *)net_dev->dev_addr); if (err) { dev_err(dev, "Failed to set random MAC address\n"); @@ -261,6 +264,9 @@ static int dpaa_netdev_init(struct net_device *net_dev, net_dev->needed_headroom = priv->tx_headroom; net_dev->watchdog_timeo = msecs_to_jiffies(tx_timeout); + mac_dev->net_dev = net_dev; + mac_dev->update_speed = dpaa_eth_cgr_set_speed; + /* start without the RUNNING flag, phylib controls it later */ netif_carrier_off(net_dev); @@ -288,10 +294,9 @@ static int dpaa_stop(struct net_device *net_dev) */ msleep(200); - err = mac_dev->stop(mac_dev); - if (err < 0) - netif_err(priv, ifdown, net_dev, "mac_dev->stop() = %d\n", - err); + if (mac_dev->phy_dev) + phy_stop(mac_dev->phy_dev); + mac_dev->disable(mac_dev->fman_mac); for (i = 0; i < ARRAY_SIZE(mac_dev->port); i++) { error = fman_port_disable(mac_dev->port[i]); @@ -826,10 +831,10 @@ static int dpaa_eth_cgr_init(struct dpaa_priv *priv) initcgr.we_mask = cpu_to_be16(QM_CGR_WE_CSCN_EN | QM_CGR_WE_CS_THRES); initcgr.cgr.cscn_en = QM_CGR_EN; - /* Set different thresholds based on the MAC speed. - * This may turn suboptimal if the MAC is reconfigured at a speed - * lower than its max, e.g. if a dTSEC later negotiates a 100Mbps link. - * In such cases, we ought to reconfigure the threshold, too. + /* Set different thresholds based on the configured MAC speed. + * This may turn suboptimal if the MAC is reconfigured at another + * speed, so MACs must call dpaa_eth_cgr_set_speed in their adjust_link + * callback. */ if (priv->mac_dev->if_support & SUPPORTED_10000baseT_Full) cs_th = DPAA_CS_THRESHOLD_10G; @@ -858,6 +863,31 @@ out_error: return err; } +static void dpaa_eth_cgr_set_speed(struct mac_device *mac_dev, int speed) +{ + struct net_device *net_dev = mac_dev->net_dev; + struct dpaa_priv *priv = netdev_priv(net_dev); + struct qm_mcc_initcgr opts = { }; + u32 cs_th; + int err; + + opts.we_mask = cpu_to_be16(QM_CGR_WE_CS_THRES); + switch (speed) { + case SPEED_10000: + cs_th = DPAA_CS_THRESHOLD_10G; + break; + case SPEED_1000: + default: + cs_th = DPAA_CS_THRESHOLD_1G; + break; + } + qm_cgr_cs_thres_set64(&opts.cgr.cs_thres, cs_th, 1); + + err = qman_update_cgr_safe(&priv->cgr_data.cgr, &opts); + if (err) + netdev_err(net_dev, "could not update speed: %d\n", err); +} + static inline void dpaa_setup_ingress(const struct dpaa_priv *priv, struct dpaa_fq *fq, const struct qman_fq *template) @@ -2946,11 +2976,12 @@ static int dpaa_open(struct net_device *net_dev) goto mac_start_failed; } - err = priv->mac_dev->start(mac_dev); + err = priv->mac_dev->enable(mac_dev->fman_mac); if (err < 0) { - netif_err(priv, ifup, net_dev, "mac_dev->start() = %d\n", err); + netif_err(priv, ifup, net_dev, "mac_dev->enable() = %d\n", err); goto mac_start_failed; } + phy_start(priv->mac_dev->phy_dev); netif_tx_start_all_queues(net_dev); @@ -3152,8 +3183,7 @@ static int dpaa_napi_add(struct net_device *net_dev) for_each_possible_cpu(cpu) { percpu_priv = per_cpu_ptr(priv->percpu_priv, cpu); - netif_napi_add(net_dev, &percpu_priv->np.napi, - dpaa_eth_poll, NAPI_POLL_WEIGHT); + netif_napi_add(net_dev, &percpu_priv->np.napi, dpaa_eth_poll); } return 0; diff --git a/drivers/net/ethernet/freescale/dpaa/dpaa_eth_sysfs.c b/drivers/net/ethernet/freescale/dpaa/dpaa_eth_sysfs.c index 4fee74c024bd..258eb6c8f4c0 100644 --- a/drivers/net/ethernet/freescale/dpaa/dpaa_eth_sysfs.c +++ b/drivers/net/ethernet/freescale/dpaa/dpaa_eth_sysfs.c @@ -18,7 +18,7 @@ static ssize_t dpaa_eth_show_addr(struct device *dev, if (mac_dev) return sprintf(buf, "%llx", - (unsigned long long)mac_dev->res->start); + (unsigned long long)mac_dev->vaddr); else return sprintf(buf, "none"); } diff --git a/drivers/net/ethernet/freescale/dpaa/dpaa_ethtool.c b/drivers/net/ethernet/freescale/dpaa/dpaa_ethtool.c index 73f07881ce2d..769e936a263c 100644 --- a/drivers/net/ethernet/freescale/dpaa/dpaa_ethtool.c +++ b/drivers/net/ethernet/freescale/dpaa/dpaa_ethtool.c @@ -80,9 +80,9 @@ static int dpaa_set_link_ksettings(struct net_device *net_dev, static void dpaa_get_drvinfo(struct net_device *net_dev, struct ethtool_drvinfo *drvinfo) { - strlcpy(drvinfo->driver, KBUILD_MODNAME, + strscpy(drvinfo->driver, KBUILD_MODNAME, sizeof(drvinfo->driver)); - strlcpy(drvinfo->bus_info, dev_name(net_dev->dev.parent->parent), + strscpy(drvinfo->bus_info, dev_name(net_dev->dev.parent->parent), sizeof(drvinfo->bus_info)); } diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c index 75d51572693d..8d029addddad 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c @@ -4565,8 +4565,7 @@ static void dpaa2_eth_add_ch_napi(struct dpaa2_eth_priv *priv) for (i = 0; i < priv->num_channels; i++) { ch = priv->channel[i]; /* NAPI weight *MUST* be a multiple of DPAA2_ETH_STORE_SIZE */ - netif_napi_add(priv->net_dev, &ch->napi, dpaa2_eth_poll, - NAPI_POLL_WEIGHT); + netif_napi_add(priv->net_dev, &ch->napi, dpaa2_eth_poll); } } diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c index c9bee9a0c9b2..49ff85633783 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c @@ -549,7 +549,7 @@ void dpaa2_mac_get_strings(u8 *data) int i; for (i = 0; i < DPAA2_MAC_NUM_STATS; i++) { - strlcpy(p, dpaa2_mac_ethtool_stats[i], ETH_GSTRING_LEN); + strscpy(p, dpaa2_mac_ethtool_stats[i], ETH_GSTRING_LEN); p += ETH_GSTRING_LEN; } } diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c index e507e9065214..2b5909fa93cf 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c @@ -3373,9 +3373,8 @@ static int dpaa2_switch_probe(struct fsl_mc_device *sw_dev) * different queues for each switch ports. */ for (i = 0; i < DPAA2_SWITCH_RX_NUM_FQS; i++) - netif_napi_add(ethsw->ports[0]->netdev, - ðsw->fq[i].napi, dpaa2_switch_poll, - NAPI_POLL_WEIGHT); + netif_napi_add(ethsw->ports[0]->netdev, ðsw->fq[i].napi, + dpaa2_switch_poll); /* Setup IRQs */ err = dpaa2_switch_setup_irqs(sw_dev); diff --git a/drivers/net/ethernet/freescale/enetc/enetc.c b/drivers/net/ethernet/freescale/enetc/enetc.c index 9f5b921039bd..54bc92fc6bf0 100644 --- a/drivers/net/ethernet/freescale/enetc/enetc.c +++ b/drivers/net/ethernet/freescale/enetc/enetc.c @@ -2116,13 +2116,14 @@ static void enetc_setup_rxbdr(struct enetc_hw *hw, struct enetc_bdr *rx_ring) static void enetc_setup_bdrs(struct enetc_ndev_priv *priv) { + struct enetc_hw *hw = &priv->si->hw; int i; for (i = 0; i < priv->num_tx_rings; i++) - enetc_setup_txbdr(&priv->si->hw, priv->tx_ring[i]); + enetc_setup_txbdr(hw, priv->tx_ring[i]); for (i = 0; i < priv->num_rx_rings; i++) - enetc_setup_rxbdr(&priv->si->hw, priv->rx_ring[i]); + enetc_setup_rxbdr(hw, priv->rx_ring[i]); } static void enetc_clear_rxbdr(struct enetc_hw *hw, struct enetc_bdr *rx_ring) @@ -2155,13 +2156,14 @@ static void enetc_clear_txbdr(struct enetc_hw *hw, struct enetc_bdr *tx_ring) static void enetc_clear_bdrs(struct enetc_ndev_priv *priv) { + struct enetc_hw *hw = &priv->si->hw; int i; for (i = 0; i < priv->num_tx_rings; i++) - enetc_clear_txbdr(&priv->si->hw, priv->tx_ring[i]); + enetc_clear_txbdr(hw, priv->tx_ring[i]); for (i = 0; i < priv->num_rx_rings; i++) - enetc_clear_rxbdr(&priv->si->hw, priv->rx_ring[i]); + enetc_clear_rxbdr(hw, priv->rx_ring[i]); udelay(1); } @@ -2169,13 +2171,13 @@ static void enetc_clear_bdrs(struct enetc_ndev_priv *priv) static int enetc_setup_irqs(struct enetc_ndev_priv *priv) { struct pci_dev *pdev = priv->si->pdev; + struct enetc_hw *hw = &priv->si->hw; int i, j, err; for (i = 0; i < priv->bdr_int_num; i++) { int irq = pci_irq_vector(pdev, ENETC_BDR_INT_BASE_IDX + i); struct enetc_int_vector *v = priv->int_vector[i]; int entry = ENETC_BDR_INT_BASE_IDX + i; - struct enetc_hw *hw = &priv->si->hw; snprintf(v->name, sizeof(v->name), "%s-rxtx%d", priv->ndev->name, i); @@ -2263,13 +2265,14 @@ static void enetc_setup_interrupts(struct enetc_ndev_priv *priv) static void enetc_clear_interrupts(struct enetc_ndev_priv *priv) { + struct enetc_hw *hw = &priv->si->hw; int i; for (i = 0; i < priv->num_tx_rings; i++) - enetc_txbdr_wr(&priv->si->hw, i, ENETC_TBIER, 0); + enetc_txbdr_wr(hw, i, ENETC_TBIER, 0); for (i = 0; i < priv->num_rx_rings; i++) - enetc_rxbdr_wr(&priv->si->hw, i, ENETC_RBIER, 0); + enetc_rxbdr_wr(hw, i, ENETC_RBIER, 0); } static int enetc_phylink_connect(struct net_device *ndev) @@ -2436,6 +2439,7 @@ int enetc_setup_tc_mqprio(struct net_device *ndev, void *type_data) { struct enetc_ndev_priv *priv = netdev_priv(ndev); struct tc_mqprio_qopt *mqprio = type_data; + struct enetc_hw *hw = &priv->si->hw; struct enetc_bdr *tx_ring; int num_stack_tx_queues; u8 num_tc; @@ -2452,7 +2456,7 @@ int enetc_setup_tc_mqprio(struct net_device *ndev, void *type_data) /* Reset all ring priorities to 0 */ for (i = 0; i < priv->num_tx_rings; i++) { tx_ring = priv->tx_ring[i]; - enetc_set_bdr_prio(&priv->si->hw, tx_ring->index, 0); + enetc_set_bdr_prio(hw, tx_ring->index, 0); } return 0; @@ -2471,7 +2475,7 @@ int enetc_setup_tc_mqprio(struct net_device *ndev, void *type_data) */ for (i = 0; i < num_tc; i++) { tx_ring = priv->tx_ring[i]; - enetc_set_bdr_prio(&priv->si->hw, tx_ring->index, i); + enetc_set_bdr_prio(hw, tx_ring->index, i); } /* Reset the number of netdev queues based on the TC count */ @@ -2584,19 +2588,21 @@ static int enetc_set_rss(struct net_device *ndev, int en) static void enetc_enable_rxvlan(struct net_device *ndev, bool en) { struct enetc_ndev_priv *priv = netdev_priv(ndev); + struct enetc_hw *hw = &priv->si->hw; int i; for (i = 0; i < priv->num_rx_rings; i++) - enetc_bdr_enable_rxvlan(&priv->si->hw, i, en); + enetc_bdr_enable_rxvlan(hw, i, en); } static void enetc_enable_txvlan(struct net_device *ndev, bool en) { struct enetc_ndev_priv *priv = netdev_priv(ndev); + struct enetc_hw *hw = &priv->si->hw; int i; for (i = 0; i < priv->num_tx_rings; i++) - enetc_bdr_enable_txvlan(&priv->si->hw, i, en); + enetc_bdr_enable_txvlan(hw, i, en); } void enetc_set_features(struct net_device *ndev, netdev_features_t features) @@ -2759,8 +2765,7 @@ int enetc_alloc_msix(struct enetc_ndev_priv *priv) v->rx_dim_en = true; } INIT_WORK(&v->rx_dim.work, enetc_rx_dim_work); - netif_napi_add(priv->ndev, &v->napi, enetc_poll, - NAPI_POLL_WEIGHT); + netif_napi_add(priv->ndev, &v->napi, enetc_poll); v->count_tx_rings = v_tx_rings; for (j = 0; j < v_tx_rings; j++) { diff --git a/drivers/net/ethernet/freescale/enetc/enetc.h b/drivers/net/ethernet/freescale/enetc/enetc.h index 2cfe6944ebd3..161930a65f61 100644 --- a/drivers/net/ethernet/freescale/enetc/enetc.h +++ b/drivers/net/ethernet/freescale/enetc/enetc.h @@ -453,7 +453,11 @@ static inline void enetc_cbd_free_data_mem(struct enetc_si *si, int size, data, *dma); } +void enetc_reset_ptcmsdur(struct enetc_hw *hw); +void enetc_set_ptcmsdur(struct enetc_hw *hw, u32 *queue_max_sdu); + #ifdef CONFIG_FSL_ENETC_QOS +int enetc_qos_query_caps(struct net_device *ndev, void *type_data); int enetc_setup_tc_taprio(struct net_device *ndev, void *type_data); void enetc_sched_speed_set(struct enetc_ndev_priv *priv, int speed); int enetc_setup_tc_cbs(struct net_device *ndev, void *type_data); @@ -467,19 +471,20 @@ int enetc_set_psfp(struct net_device *ndev, bool en); static inline void enetc_get_max_cap(struct enetc_ndev_priv *priv) { + struct enetc_hw *hw = &priv->si->hw; u32 reg; - reg = enetc_port_rd(&priv->si->hw, ENETC_PSIDCAPR); + reg = enetc_port_rd(hw, ENETC_PSIDCAPR); priv->psfp_cap.max_streamid = reg & ENETC_PSIDCAPR_MSK; /* Port stream filter capability */ - reg = enetc_port_rd(&priv->si->hw, ENETC_PSFCAPR); + reg = enetc_port_rd(hw, ENETC_PSFCAPR); priv->psfp_cap.max_psfp_filter = reg & ENETC_PSFCAPR_MSK; /* Port stream gate capability */ - reg = enetc_port_rd(&priv->si->hw, ENETC_PSGCAPR); + reg = enetc_port_rd(hw, ENETC_PSGCAPR); priv->psfp_cap.max_psfp_gate = (reg & ENETC_PSGCAPR_SGIT_MSK); priv->psfp_cap.max_psfp_gatelist = (reg & ENETC_PSGCAPR_GCL_MSK) >> 16; /* Port flow meter capability */ - reg = enetc_port_rd(&priv->si->hw, ENETC_PFMCAPR); + reg = enetc_port_rd(hw, ENETC_PFMCAPR); priv->psfp_cap.max_psfp_meter = reg & ENETC_PFMCAPR_MSK; } @@ -520,6 +525,7 @@ static inline int enetc_psfp_disable(struct enetc_ndev_priv *priv) } #else +#define enetc_qos_query_caps(ndev, type_data) -EOPNOTSUPP #define enetc_setup_tc_taprio(ndev, type_data) -EOPNOTSUPP #define enetc_sched_speed_set(priv, speed) (void)0 #define enetc_setup_tc_cbs(ndev, type_data) -EOPNOTSUPP diff --git a/drivers/net/ethernet/freescale/enetc/enetc_ethtool.c b/drivers/net/ethernet/freescale/enetc/enetc_ethtool.c index ff872e40ce85..c8369e3752b0 100644 --- a/drivers/net/ethernet/freescale/enetc/enetc_ethtool.c +++ b/drivers/net/ethernet/freescale/enetc/enetc_ethtool.c @@ -125,68 +125,68 @@ static const struct { int reg; char name[ETH_GSTRING_LEN]; } enetc_port_counters[] = { - { ENETC_PM0_REOCT, "MAC rx ethernet octets" }, - { ENETC_PM0_RALN, "MAC rx alignment errors" }, - { ENETC_PM0_RXPF, "MAC rx valid pause frames" }, - { ENETC_PM0_RFRM, "MAC rx valid frames" }, - { ENETC_PM0_RFCS, "MAC rx fcs errors" }, - { ENETC_PM0_RVLAN, "MAC rx VLAN frames" }, - { ENETC_PM0_RERR, "MAC rx frame errors" }, - { ENETC_PM0_RUCA, "MAC rx unicast frames" }, - { ENETC_PM0_RMCA, "MAC rx multicast frames" }, - { ENETC_PM0_RBCA, "MAC rx broadcast frames" }, - { ENETC_PM0_RDRP, "MAC rx dropped packets" }, - { ENETC_PM0_RPKT, "MAC rx packets" }, - { ENETC_PM0_RUND, "MAC rx undersized packets" }, - { ENETC_PM0_R64, "MAC rx 64 byte packets" }, - { ENETC_PM0_R127, "MAC rx 65-127 byte packets" }, - { ENETC_PM0_R255, "MAC rx 128-255 byte packets" }, - { ENETC_PM0_R511, "MAC rx 256-511 byte packets" }, - { ENETC_PM0_R1023, "MAC rx 512-1023 byte packets" }, - { ENETC_PM0_R1522, "MAC rx 1024-1522 byte packets" }, - { ENETC_PM0_R1523X, "MAC rx 1523 to max-octet packets" }, - { ENETC_PM0_ROVR, "MAC rx oversized packets" }, - { ENETC_PM0_RJBR, "MAC rx jabber packets" }, - { ENETC_PM0_RFRG, "MAC rx fragment packets" }, - { ENETC_PM0_RCNP, "MAC rx control packets" }, - { ENETC_PM0_RDRNTP, "MAC rx fifo drop" }, - { ENETC_PM0_TEOCT, "MAC tx ethernet octets" }, - { ENETC_PM0_TOCT, "MAC tx octets" }, - { ENETC_PM0_TCRSE, "MAC tx carrier sense errors" }, - { ENETC_PM0_TXPF, "MAC tx valid pause frames" }, - { ENETC_PM0_TFRM, "MAC tx frames" }, - { ENETC_PM0_TFCS, "MAC tx fcs errors" }, - { ENETC_PM0_TVLAN, "MAC tx VLAN frames" }, - { ENETC_PM0_TERR, "MAC tx frame errors" }, - { ENETC_PM0_TUCA, "MAC tx unicast frames" }, - { ENETC_PM0_TMCA, "MAC tx multicast frames" }, - { ENETC_PM0_TBCA, "MAC tx broadcast frames" }, - { ENETC_PM0_TPKT, "MAC tx packets" }, - { ENETC_PM0_TUND, "MAC tx undersized packets" }, - { ENETC_PM0_T64, "MAC tx 64 byte packets" }, - { ENETC_PM0_T127, "MAC tx 65-127 byte packets" }, - { ENETC_PM0_T255, "MAC tx 128-255 byte packets" }, - { ENETC_PM0_T511, "MAC tx 256-511 byte packets" }, - { ENETC_PM0_T1023, "MAC tx 512-1023 byte packets" }, - { ENETC_PM0_T1522, "MAC tx 1024-1522 byte packets" }, - { ENETC_PM0_T1523X, "MAC tx 1523 to max-octet packets" }, - { ENETC_PM0_TCNP, "MAC tx control packets" }, - { ENETC_PM0_TDFR, "MAC tx deferred packets" }, - { ENETC_PM0_TMCOL, "MAC tx multiple collisions" }, - { ENETC_PM0_TSCOL, "MAC tx single collisions" }, - { ENETC_PM0_TLCOL, "MAC tx late collisions" }, - { ENETC_PM0_TECOL, "MAC tx excessive collisions" }, - { ENETC_UFDMF, "SI MAC nomatch u-cast discards" }, - { ENETC_MFDMF, "SI MAC nomatch m-cast discards" }, - { ENETC_PBFDSIR, "SI MAC nomatch b-cast discards" }, - { ENETC_PUFDVFR, "SI VLAN nomatch u-cast discards" }, - { ENETC_PMFDVFR, "SI VLAN nomatch m-cast discards" }, - { ENETC_PBFDVFR, "SI VLAN nomatch b-cast discards" }, - { ENETC_PFDMSAPR, "SI pruning discarded frames" }, - { ENETC_PICDR(0), "ICM DR0 discarded frames" }, - { ENETC_PICDR(1), "ICM DR1 discarded frames" }, - { ENETC_PICDR(2), "ICM DR2 discarded frames" }, - { ENETC_PICDR(3), "ICM DR3 discarded frames" }, + { ENETC_PM_REOCT(0), "MAC rx ethernet octets" }, + { ENETC_PM_RALN(0), "MAC rx alignment errors" }, + { ENETC_PM_RXPF(0), "MAC rx valid pause frames" }, + { ENETC_PM_RFRM(0), "MAC rx valid frames" }, + { ENETC_PM_RFCS(0), "MAC rx fcs errors" }, + { ENETC_PM_RVLAN(0), "MAC rx VLAN frames" }, + { ENETC_PM_RERR(0), "MAC rx frame errors" }, + { ENETC_PM_RUCA(0), "MAC rx unicast frames" }, + { ENETC_PM_RMCA(0), "MAC rx multicast frames" }, + { ENETC_PM_RBCA(0), "MAC rx broadcast frames" }, + { ENETC_PM_RDRP(0), "MAC rx dropped packets" }, + { ENETC_PM_RPKT(0), "MAC rx packets" }, + { ENETC_PM_RUND(0), "MAC rx undersized packets" }, + { ENETC_PM_R64(0), "MAC rx 64 byte packets" }, + { ENETC_PM_R127(0), "MAC rx 65-127 byte packets" }, + { ENETC_PM_R255(0), "MAC rx 128-255 byte packets" }, + { ENETC_PM_R511(0), "MAC rx 256-511 byte packets" }, + { ENETC_PM_R1023(0), "MAC rx 512-1023 byte packets" }, + { ENETC_PM_R1522(0), "MAC rx 1024-1522 byte packets" }, + { ENETC_PM_R1523X(0), "MAC rx 1523 to max-octet packets" }, + { ENETC_PM_ROVR(0), "MAC rx oversized packets" }, + { ENETC_PM_RJBR(0), "MAC rx jabber packets" }, + { ENETC_PM_RFRG(0), "MAC rx fragment packets" }, + { ENETC_PM_RCNP(0), "MAC rx control packets" }, + { ENETC_PM_RDRNTP(0), "MAC rx fifo drop" }, + { ENETC_PM_TEOCT(0), "MAC tx ethernet octets" }, + { ENETC_PM_TOCT(0), "MAC tx octets" }, + { ENETC_PM_TCRSE(0), "MAC tx carrier sense errors" }, + { ENETC_PM_TXPF(0), "MAC tx valid pause frames" }, + { ENETC_PM_TFRM(0), "MAC tx frames" }, + { ENETC_PM_TFCS(0), "MAC tx fcs errors" }, + { ENETC_PM_TVLAN(0), "MAC tx VLAN frames" }, + { ENETC_PM_TERR(0), "MAC tx frame errors" }, + { ENETC_PM_TUCA(0), "MAC tx unicast frames" }, + { ENETC_PM_TMCA(0), "MAC tx multicast frames" }, + { ENETC_PM_TBCA(0), "MAC tx broadcast frames" }, + { ENETC_PM_TPKT(0), "MAC tx packets" }, + { ENETC_PM_TUND(0), "MAC tx undersized packets" }, + { ENETC_PM_T64(0), "MAC tx 64 byte packets" }, + { ENETC_PM_T127(0), "MAC tx 65-127 byte packets" }, + { ENETC_PM_T255(0), "MAC tx 128-255 byte packets" }, + { ENETC_PM_T511(0), "MAC tx 256-511 byte packets" }, + { ENETC_PM_T1023(0), "MAC tx 512-1023 byte packets" }, + { ENETC_PM_T1522(0), "MAC tx 1024-1522 byte packets" }, + { ENETC_PM_T1523X(0), "MAC tx 1523 to max-octet packets" }, + { ENETC_PM_TCNP(0), "MAC tx control packets" }, + { ENETC_PM_TDFR(0), "MAC tx deferred packets" }, + { ENETC_PM_TMCOL(0), "MAC tx multiple collisions" }, + { ENETC_PM_TSCOL(0), "MAC tx single collisions" }, + { ENETC_PM_TLCOL(0), "MAC tx late collisions" }, + { ENETC_PM_TECOL(0), "MAC tx excessive collisions" }, + { ENETC_UFDMF, "SI MAC nomatch u-cast discards" }, + { ENETC_MFDMF, "SI MAC nomatch m-cast discards" }, + { ENETC_PBFDSIR, "SI MAC nomatch b-cast discards" }, + { ENETC_PUFDVFR, "SI VLAN nomatch u-cast discards" }, + { ENETC_PMFDVFR, "SI VLAN nomatch m-cast discards" }, + { ENETC_PBFDVFR, "SI VLAN nomatch b-cast discards" }, + { ENETC_PFDMSAPR, "SI pruning discarded frames" }, + { ENETC_PICDR(0), "ICM DR0 discarded frames" }, + { ENETC_PICDR(1), "ICM DR1 discarded frames" }, + { ENETC_PICDR(2), "ICM DR2 discarded frames" }, + { ENETC_PICDR(3), "ICM DR3 discarded frames" }, }; static const char rx_ring_stats[][ETH_GSTRING_LEN] = { @@ -236,7 +236,7 @@ static void enetc_get_strings(struct net_device *ndev, u32 stringset, u8 *data) switch (stringset) { case ETH_SS_STATS: for (i = 0; i < ARRAY_SIZE(enetc_si_counters); i++) { - strlcpy(p, enetc_si_counters[i].name, ETH_GSTRING_LEN); + strscpy(p, enetc_si_counters[i].name, ETH_GSTRING_LEN); p += ETH_GSTRING_LEN; } for (i = 0; i < priv->num_tx_rings; i++) { @@ -258,7 +258,7 @@ static void enetc_get_strings(struct net_device *ndev, u32 stringset, u8 *data) break; for (i = 0; i < ARRAY_SIZE(enetc_port_counters); i++) { - strlcpy(p, enetc_port_counters[i].name, + strscpy(p, enetc_port_counters[i].name, ETH_GSTRING_LEN); p += ETH_GSTRING_LEN; } @@ -301,6 +301,113 @@ static void enetc_get_ethtool_stats(struct net_device *ndev, data[o++] = enetc_port_rd(hw, enetc_port_counters[i].reg); } +static void enetc_get_pause_stats(struct net_device *ndev, + struct ethtool_pause_stats *pause_stats) +{ + struct enetc_ndev_priv *priv = netdev_priv(ndev); + struct enetc_hw *hw = &priv->si->hw; + + pause_stats->tx_pause_frames = enetc_port_rd(hw, ENETC_PM_TXPF(0)); + pause_stats->rx_pause_frames = enetc_port_rd(hw, ENETC_PM_RXPF(0)); +} + +static void enetc_mac_stats(struct enetc_hw *hw, int mac, + struct ethtool_eth_mac_stats *s) +{ + s->FramesTransmittedOK = enetc_port_rd(hw, ENETC_PM_TFRM(mac)); + s->SingleCollisionFrames = enetc_port_rd(hw, ENETC_PM_TSCOL(mac)); + s->MultipleCollisionFrames = enetc_port_rd(hw, ENETC_PM_TMCOL(mac)); + s->FramesReceivedOK = enetc_port_rd(hw, ENETC_PM_RFRM(mac)); + s->FrameCheckSequenceErrors = enetc_port_rd(hw, ENETC_PM_RFCS(mac)); + s->AlignmentErrors = enetc_port_rd(hw, ENETC_PM_RALN(mac)); + s->OctetsTransmittedOK = enetc_port_rd(hw, ENETC_PM_TEOCT(mac)); + s->FramesWithDeferredXmissions = enetc_port_rd(hw, ENETC_PM_TDFR(mac)); + s->LateCollisions = enetc_port_rd(hw, ENETC_PM_TLCOL(mac)); + s->FramesAbortedDueToXSColls = enetc_port_rd(hw, ENETC_PM_TECOL(mac)); + s->FramesLostDueToIntMACXmitError = enetc_port_rd(hw, ENETC_PM_TERR(mac)); + s->CarrierSenseErrors = enetc_port_rd(hw, ENETC_PM_TCRSE(mac)); + s->OctetsReceivedOK = enetc_port_rd(hw, ENETC_PM_REOCT(mac)); + s->FramesLostDueToIntMACRcvError = enetc_port_rd(hw, ENETC_PM_RDRNTP(mac)); + s->MulticastFramesXmittedOK = enetc_port_rd(hw, ENETC_PM_TMCA(mac)); + s->BroadcastFramesXmittedOK = enetc_port_rd(hw, ENETC_PM_TBCA(mac)); + s->MulticastFramesReceivedOK = enetc_port_rd(hw, ENETC_PM_RMCA(mac)); + s->BroadcastFramesReceivedOK = enetc_port_rd(hw, ENETC_PM_RBCA(mac)); +} + +static void enetc_ctrl_stats(struct enetc_hw *hw, int mac, + struct ethtool_eth_ctrl_stats *s) +{ + s->MACControlFramesTransmitted = enetc_port_rd(hw, ENETC_PM_TCNP(mac)); + s->MACControlFramesReceived = enetc_port_rd(hw, ENETC_PM_RCNP(mac)); +} + +static const struct ethtool_rmon_hist_range enetc_rmon_ranges[] = { + { 64, 64 }, + { 65, 127 }, + { 128, 255 }, + { 256, 511 }, + { 512, 1023 }, + { 1024, 1522 }, + { 1523, ENETC_MAC_MAXFRM_SIZE }, + {}, +}; + +static void enetc_rmon_stats(struct enetc_hw *hw, int mac, + struct ethtool_rmon_stats *s, + const struct ethtool_rmon_hist_range **ranges) +{ + s->undersize_pkts = enetc_port_rd(hw, ENETC_PM_RUND(mac)); + s->oversize_pkts = enetc_port_rd(hw, ENETC_PM_ROVR(mac)); + s->fragments = enetc_port_rd(hw, ENETC_PM_RFRG(mac)); + s->jabbers = enetc_port_rd(hw, ENETC_PM_RJBR(mac)); + + s->hist[0] = enetc_port_rd(hw, ENETC_PM_R64(mac)); + s->hist[1] = enetc_port_rd(hw, ENETC_PM_R127(mac)); + s->hist[2] = enetc_port_rd(hw, ENETC_PM_R255(mac)); + s->hist[3] = enetc_port_rd(hw, ENETC_PM_R511(mac)); + s->hist[4] = enetc_port_rd(hw, ENETC_PM_R1023(mac)); + s->hist[5] = enetc_port_rd(hw, ENETC_PM_R1522(mac)); + s->hist[6] = enetc_port_rd(hw, ENETC_PM_R1523X(mac)); + + s->hist_tx[0] = enetc_port_rd(hw, ENETC_PM_T64(mac)); + s->hist_tx[1] = enetc_port_rd(hw, ENETC_PM_T127(mac)); + s->hist_tx[2] = enetc_port_rd(hw, ENETC_PM_T255(mac)); + s->hist_tx[3] = enetc_port_rd(hw, ENETC_PM_T511(mac)); + s->hist_tx[4] = enetc_port_rd(hw, ENETC_PM_T1023(mac)); + s->hist_tx[5] = enetc_port_rd(hw, ENETC_PM_T1522(mac)); + s->hist_tx[6] = enetc_port_rd(hw, ENETC_PM_T1523X(mac)); + + *ranges = enetc_rmon_ranges; +} + +static void enetc_get_eth_mac_stats(struct net_device *ndev, + struct ethtool_eth_mac_stats *mac_stats) +{ + struct enetc_ndev_priv *priv = netdev_priv(ndev); + struct enetc_hw *hw = &priv->si->hw; + + enetc_mac_stats(hw, 0, mac_stats); +} + +static void enetc_get_eth_ctrl_stats(struct net_device *ndev, + struct ethtool_eth_ctrl_stats *ctrl_stats) +{ + struct enetc_ndev_priv *priv = netdev_priv(ndev); + struct enetc_hw *hw = &priv->si->hw; + + enetc_ctrl_stats(hw, 0, ctrl_stats); +} + +static void enetc_get_rmon_stats(struct net_device *ndev, + struct ethtool_rmon_stats *rmon_stats, + const struct ethtool_rmon_hist_range **ranges) +{ + struct enetc_ndev_priv *priv = netdev_priv(ndev); + struct enetc_hw *hw = &priv->si->hw; + + enetc_rmon_stats(hw, 0, rmon_stats, ranges); +} + #define ENETC_RSSHASH_L3 (RXH_L2DA | RXH_VLAN | RXH_L3_PROTO | RXH_IP_SRC | \ RXH_IP_DST) #define ENETC_RSSHASH_L4 (ENETC_RSSHASH_L3 | RXH_L4_B_0_1 | RXH_L4_B_2_3) @@ -766,6 +873,10 @@ static const struct ethtool_ops enetc_pf_ethtool_ops = { .get_sset_count = enetc_get_sset_count, .get_strings = enetc_get_strings, .get_ethtool_stats = enetc_get_ethtool_stats, + .get_pause_stats = enetc_get_pause_stats, + .get_rmon_stats = enetc_get_rmon_stats, + .get_eth_ctrl_stats = enetc_get_eth_ctrl_stats, + .get_eth_mac_stats = enetc_get_eth_mac_stats, .get_rxnfc = enetc_get_rxnfc, .set_rxnfc = enetc_set_rxnfc, .get_rxfh_key_size = enetc_get_rxfh_key_size, diff --git a/drivers/net/ethernet/freescale/enetc/enetc_hw.h b/drivers/net/ethernet/freescale/enetc/enetc_hw.h index 647c87f73bf7..18ca1f42b1f7 100644 --- a/drivers/net/ethernet/freescale/enetc/enetc_hw.h +++ b/drivers/net/ethernet/freescale/enetc/enetc_hw.h @@ -276,58 +276,60 @@ enum enetc_bdr_type {TX, RX}; #define ENETC_PFMCAPR 0x1b38 #define ENETC_PFMCAPR_MSK GENMASK(15, 0) -/* MAC counters */ -#define ENETC_PM0_REOCT 0x8100 -#define ENETC_PM0_RALN 0x8110 -#define ENETC_PM0_RXPF 0x8118 -#define ENETC_PM0_RFRM 0x8120 -#define ENETC_PM0_RFCS 0x8128 -#define ENETC_PM0_RVLAN 0x8130 -#define ENETC_PM0_RERR 0x8138 -#define ENETC_PM0_RUCA 0x8140 -#define ENETC_PM0_RMCA 0x8148 -#define ENETC_PM0_RBCA 0x8150 -#define ENETC_PM0_RDRP 0x8158 -#define ENETC_PM0_RPKT 0x8160 -#define ENETC_PM0_RUND 0x8168 -#define ENETC_PM0_R64 0x8170 -#define ENETC_PM0_R127 0x8178 -#define ENETC_PM0_R255 0x8180 -#define ENETC_PM0_R511 0x8188 -#define ENETC_PM0_R1023 0x8190 -#define ENETC_PM0_R1522 0x8198 -#define ENETC_PM0_R1523X 0x81A0 -#define ENETC_PM0_ROVR 0x81A8 -#define ENETC_PM0_RJBR 0x81B0 -#define ENETC_PM0_RFRG 0x81B8 -#define ENETC_PM0_RCNP 0x81C0 -#define ENETC_PM0_RDRNTP 0x81C8 -#define ENETC_PM0_TEOCT 0x8200 -#define ENETC_PM0_TOCT 0x8208 -#define ENETC_PM0_TCRSE 0x8210 -#define ENETC_PM0_TXPF 0x8218 -#define ENETC_PM0_TFRM 0x8220 -#define ENETC_PM0_TFCS 0x8228 -#define ENETC_PM0_TVLAN 0x8230 -#define ENETC_PM0_TERR 0x8238 -#define ENETC_PM0_TUCA 0x8240 -#define ENETC_PM0_TMCA 0x8248 -#define ENETC_PM0_TBCA 0x8250 -#define ENETC_PM0_TPKT 0x8260 -#define ENETC_PM0_TUND 0x8268 -#define ENETC_PM0_T64 0x8270 -#define ENETC_PM0_T127 0x8278 -#define ENETC_PM0_T255 0x8280 -#define ENETC_PM0_T511 0x8288 -#define ENETC_PM0_T1023 0x8290 -#define ENETC_PM0_T1522 0x8298 -#define ENETC_PM0_T1523X 0x82A0 -#define ENETC_PM0_TCNP 0x82C0 -#define ENETC_PM0_TDFR 0x82D0 -#define ENETC_PM0_TMCOL 0x82D8 -#define ENETC_PM0_TSCOL 0x82E0 -#define ENETC_PM0_TLCOL 0x82E8 -#define ENETC_PM0_TECOL 0x82F0 +/* Port MAC counters: Port MAC 0 corresponds to the eMAC and + * Port MAC 1 to the pMAC. + */ +#define ENETC_PM_REOCT(mac) (0x8100 + 0x1000 * (mac)) +#define ENETC_PM_RALN(mac) (0x8110 + 0x1000 * (mac)) +#define ENETC_PM_RXPF(mac) (0x8118 + 0x1000 * (mac)) +#define ENETC_PM_RFRM(mac) (0x8120 + 0x1000 * (mac)) +#define ENETC_PM_RFCS(mac) (0x8128 + 0x1000 * (mac)) +#define ENETC_PM_RVLAN(mac) (0x8130 + 0x1000 * (mac)) +#define ENETC_PM_RERR(mac) (0x8138 + 0x1000 * (mac)) +#define ENETC_PM_RUCA(mac) (0x8140 + 0x1000 * (mac)) +#define ENETC_PM_RMCA(mac) (0x8148 + 0x1000 * (mac)) +#define ENETC_PM_RBCA(mac) (0x8150 + 0x1000 * (mac)) +#define ENETC_PM_RDRP(mac) (0x8158 + 0x1000 * (mac)) +#define ENETC_PM_RPKT(mac) (0x8160 + 0x1000 * (mac)) +#define ENETC_PM_RUND(mac) (0x8168 + 0x1000 * (mac)) +#define ENETC_PM_R64(mac) (0x8170 + 0x1000 * (mac)) +#define ENETC_PM_R127(mac) (0x8178 + 0x1000 * (mac)) +#define ENETC_PM_R255(mac) (0x8180 + 0x1000 * (mac)) +#define ENETC_PM_R511(mac) (0x8188 + 0x1000 * (mac)) +#define ENETC_PM_R1023(mac) (0x8190 + 0x1000 * (mac)) +#define ENETC_PM_R1522(mac) (0x8198 + 0x1000 * (mac)) +#define ENETC_PM_R1523X(mac) (0x81A0 + 0x1000 * (mac)) +#define ENETC_PM_ROVR(mac) (0x81A8 + 0x1000 * (mac)) +#define ENETC_PM_RJBR(mac) (0x81B0 + 0x1000 * (mac)) +#define ENETC_PM_RFRG(mac) (0x81B8 + 0x1000 * (mac)) +#define ENETC_PM_RCNP(mac) (0x81C0 + 0x1000 * (mac)) +#define ENETC_PM_RDRNTP(mac) (0x81C8 + 0x1000 * (mac)) +#define ENETC_PM_TEOCT(mac) (0x8200 + 0x1000 * (mac)) +#define ENETC_PM_TOCT(mac) (0x8208 + 0x1000 * (mac)) +#define ENETC_PM_TCRSE(mac) (0x8210 + 0x1000 * (mac)) +#define ENETC_PM_TXPF(mac) (0x8218 + 0x1000 * (mac)) +#define ENETC_PM_TFRM(mac) (0x8220 + 0x1000 * (mac)) +#define ENETC_PM_TFCS(mac) (0x8228 + 0x1000 * (mac)) +#define ENETC_PM_TVLAN(mac) (0x8230 + 0x1000 * (mac)) +#define ENETC_PM_TERR(mac) (0x8238 + 0x1000 * (mac)) +#define ENETC_PM_TUCA(mac) (0x8240 + 0x1000 * (mac)) +#define ENETC_PM_TMCA(mac) (0x8248 + 0x1000 * (mac)) +#define ENETC_PM_TBCA(mac) (0x8250 + 0x1000 * (mac)) +#define ENETC_PM_TPKT(mac) (0x8260 + 0x1000 * (mac)) +#define ENETC_PM_TUND(mac) (0x8268 + 0x1000 * (mac)) +#define ENETC_PM_T64(mac) (0x8270 + 0x1000 * (mac)) +#define ENETC_PM_T127(mac) (0x8278 + 0x1000 * (mac)) +#define ENETC_PM_T255(mac) (0x8280 + 0x1000 * (mac)) +#define ENETC_PM_T511(mac) (0x8288 + 0x1000 * (mac)) +#define ENETC_PM_T1023(mac) (0x8290 + 0x1000 * (mac)) +#define ENETC_PM_T1522(mac) (0x8298 + 0x1000 * (mac)) +#define ENETC_PM_T1523X(mac) (0x82A0 + 0x1000 * (mac)) +#define ENETC_PM_TCNP(mac) (0x82C0 + 0x1000 * (mac)) +#define ENETC_PM_TDFR(mac) (0x82D0 + 0x1000 * (mac)) +#define ENETC_PM_TMCOL(mac) (0x82D8 + 0x1000 * (mac)) +#define ENETC_PM_TSCOL(mac) (0x82E0 + 0x1000 * (mac)) +#define ENETC_PM_TLCOL(mac) (0x82E8 + 0x1000 * (mac)) +#define ENETC_PM_TECOL(mac) (0x82F0 + 0x1000 * (mac)) /* Port counters */ #define ENETC_PICDR(n) (0x0700 + (n) * 8) /* n = [0..3] */ @@ -943,13 +945,13 @@ static inline u32 enetc_usecs_to_cycles(u32 usecs) } /* port time gating control register */ -#define ENETC_QBV_PTGCR_OFFSET 0x11a00 -#define ENETC_QBV_TGE BIT(31) -#define ENETC_QBV_TGPE BIT(30) +#define ENETC_PTGCR 0x11a00 +#define ENETC_PTGCR_TGE BIT(31) +#define ENETC_PTGCR_TGPE BIT(30) /* Port time gating capability register */ -#define ENETC_QBV_PTGCAPR_OFFSET 0x11a08 -#define ENETC_QBV_MAX_GCL_LEN_MASK GENMASK(15, 0) +#define ENETC_PTGCAPR 0x11a08 +#define ENETC_PTGCAPR_MAX_GCL_LEN_MASK GENMASK(15, 0) /* Port time specific departure */ #define ENETC_PTCTSDR(n) (0x1210 + 4 * (n)) diff --git a/drivers/net/ethernet/freescale/enetc/enetc_pf.c b/drivers/net/ethernet/freescale/enetc/enetc_pf.c index bb7750222691..bdf94335ee99 100644 --- a/drivers/net/ethernet/freescale/enetc/enetc_pf.c +++ b/drivers/net/ethernet/freescale/enetc/enetc_pf.c @@ -516,15 +516,34 @@ static void enetc_port_si_configure(struct enetc_si *si) enetc_port_wr(hw, ENETC_PSIVLANFMR, ENETC_PSIVLANFMR_VS); } -static void enetc_configure_port_mac(struct enetc_hw *hw) +void enetc_set_ptcmsdur(struct enetc_hw *hw, u32 *max_sdu) { int tc; - enetc_port_wr(hw, ENETC_PM0_MAXFRM, - ENETC_SET_MAXFRM(ENETC_RX_MAXFRM_SIZE)); + for (tc = 0; tc < 8; tc++) { + u32 val = ENETC_MAC_MAXFRM_SIZE; + + if (max_sdu[tc]) + val = max_sdu[tc] + VLAN_ETH_HLEN; + + enetc_port_wr(hw, ENETC_PTCMSDUR(tc), val); + } +} + +void enetc_reset_ptcmsdur(struct enetc_hw *hw) +{ + int tc; for (tc = 0; tc < 8; tc++) enetc_port_wr(hw, ENETC_PTCMSDUR(tc), ENETC_MAC_MAXFRM_SIZE); +} + +static void enetc_configure_port_mac(struct enetc_hw *hw) +{ + enetc_port_wr(hw, ENETC_PM0_MAXFRM, + ENETC_SET_MAXFRM(ENETC_RX_MAXFRM_SIZE)); + + enetc_reset_ptcmsdur(hw); enetc_port_wr(hw, ENETC_PM0_CMD_CFG, ENETC_PM0_CMD_PHY_TX_EN | ENETC_PM0_CMD_TXP | ENETC_PM0_PROMISC); @@ -738,6 +757,8 @@ static int enetc_pf_setup_tc(struct net_device *ndev, enum tc_setup_type type, void *type_data) { switch (type) { + case TC_QUERY_CAPS: + return enetc_qos_query_caps(ndev, type_data); case TC_SETUP_QDISC_MQPRIO: return enetc_setup_tc_mqprio(ndev, type_data); case TC_SETUP_QDISC_TAPRIO: diff --git a/drivers/net/ethernet/freescale/enetc/enetc_qos.c b/drivers/net/ethernet/freescale/enetc/enetc_qos.c index f8a2f02ce22d..e6416332ec79 100644 --- a/drivers/net/ethernet/freescale/enetc/enetc_qos.c +++ b/drivers/net/ethernet/freescale/enetc/enetc_qos.c @@ -7,18 +7,19 @@ #include <linux/math64.h> #include <linux/refcount.h> #include <net/pkt_cls.h> +#include <net/pkt_sched.h> #include <net/tc_act/tc_gate.h> static u16 enetc_get_max_gcl_len(struct enetc_hw *hw) { - return enetc_rd(hw, ENETC_QBV_PTGCAPR_OFFSET) - & ENETC_QBV_MAX_GCL_LEN_MASK; + return enetc_rd(hw, ENETC_PTGCAPR) & ENETC_PTGCAPR_MAX_GCL_LEN_MASK; } void enetc_sched_speed_set(struct enetc_ndev_priv *priv, int speed) { + struct enetc_hw *hw = &priv->si->hw; u32 old_speed = priv->speed; - u32 pspeed; + u32 pspeed, tmp; if (speed == old_speed) return; @@ -39,16 +40,15 @@ void enetc_sched_speed_set(struct enetc_ndev_priv *priv, int speed) } priv->speed = speed; - enetc_port_wr(&priv->si->hw, ENETC_PMR, - (enetc_port_rd(&priv->si->hw, ENETC_PMR) - & (~ENETC_PMR_PSPEED_MASK)) - | pspeed); + tmp = enetc_port_rd(hw, ENETC_PMR); + enetc_port_wr(hw, ENETC_PMR, (tmp & ~ENETC_PMR_PSPEED_MASK) | pspeed); } static int enetc_setup_taprio(struct net_device *ndev, struct tc_taprio_qopt_offload *admin_conf) { struct enetc_ndev_priv *priv = netdev_priv(ndev); + struct enetc_hw *hw = &priv->si->hw; struct enetc_cbd cbd = {.cmd = 0}; struct tgs_gcl_conf *gcl_config; struct tgs_gcl_data *gcl_data; @@ -61,15 +61,14 @@ static int enetc_setup_taprio(struct net_device *ndev, int err; int i; - if (admin_conf->num_entries > enetc_get_max_gcl_len(&priv->si->hw)) + if (admin_conf->num_entries > enetc_get_max_gcl_len(hw)) return -EINVAL; gcl_len = admin_conf->num_entries; - tge = enetc_rd(&priv->si->hw, ENETC_QBV_PTGCR_OFFSET); + tge = enetc_rd(hw, ENETC_PTGCR); if (!admin_conf->enable) { - enetc_wr(&priv->si->hw, - ENETC_QBV_PTGCR_OFFSET, - tge & (~ENETC_QBV_TGE)); + enetc_wr(hw, ENETC_PTGCR, tge & ~ENETC_PTGCR_TGE); + enetc_reset_ptcmsdur(hw); priv->active_offloads &= ~ENETC_F_QBV; @@ -117,27 +116,28 @@ static int enetc_setup_taprio(struct net_device *ndev, cbd.cls = BDCR_CMD_PORT_GCL; cbd.status_flags = 0; - enetc_wr(&priv->si->hw, ENETC_QBV_PTGCR_OFFSET, - tge | ENETC_QBV_TGE); + enetc_wr(hw, ENETC_PTGCR, tge | ENETC_PTGCR_TGE); err = enetc_send_cmd(priv->si, &cbd); if (err) - enetc_wr(&priv->si->hw, - ENETC_QBV_PTGCR_OFFSET, - tge & (~ENETC_QBV_TGE)); + enetc_wr(hw, ENETC_PTGCR, tge & ~ENETC_PTGCR_TGE); enetc_cbd_free_data_mem(priv->si, data_size, tmp, &dma); - if (!err) - priv->active_offloads |= ENETC_F_QBV; + if (err) + return err; - return err; + enetc_set_ptcmsdur(hw, admin_conf->max_sdu); + priv->active_offloads |= ENETC_F_QBV; + + return 0; } int enetc_setup_tc_taprio(struct net_device *ndev, void *type_data) { struct tc_taprio_qopt_offload *taprio = type_data; struct enetc_ndev_priv *priv = netdev_priv(ndev); + struct enetc_hw *hw = &priv->si->hw; int err; int i; @@ -147,16 +147,14 @@ int enetc_setup_tc_taprio(struct net_device *ndev, void *type_data) return -EBUSY; for (i = 0; i < priv->num_tx_rings; i++) - enetc_set_bdr_prio(&priv->si->hw, - priv->tx_ring[i]->index, + enetc_set_bdr_prio(hw, priv->tx_ring[i]->index, taprio->enable ? i : 0); err = enetc_setup_taprio(ndev, taprio); if (err) for (i = 0; i < priv->num_tx_rings; i++) - enetc_set_bdr_prio(&priv->si->hw, - priv->tx_ring[i]->index, + enetc_set_bdr_prio(hw, priv->tx_ring[i]->index, taprio->enable ? 0 : i); return err; @@ -178,7 +176,7 @@ int enetc_setup_tc_cbs(struct net_device *ndev, void *type_data) struct tc_cbs_qopt_offload *cbs = type_data; u32 port_transmit_rate = priv->speed; u8 tc_nums = netdev_get_num_tc(ndev); - struct enetc_si *si = priv->si; + struct enetc_hw *hw = &priv->si->hw; u32 hi_credit_bit, hi_credit_reg; u32 max_interference_size; u32 port_frame_max_size; @@ -199,15 +197,15 @@ int enetc_setup_tc_cbs(struct net_device *ndev, void *type_data) * lower than this TC have been disabled. */ if (tc == prio_top && - enetc_get_cbs_enable(&si->hw, prio_next)) { + enetc_get_cbs_enable(hw, prio_next)) { dev_err(&ndev->dev, "Disable TC%d before disable TC%d\n", prio_next, tc); return -EINVAL; } - enetc_port_wr(&si->hw, ENETC_PTCCBSR1(tc), 0); - enetc_port_wr(&si->hw, ENETC_PTCCBSR0(tc), 0); + enetc_port_wr(hw, ENETC_PTCCBSR1(tc), 0); + enetc_port_wr(hw, ENETC_PTCCBSR0(tc), 0); return 0; } @@ -224,13 +222,13 @@ int enetc_setup_tc_cbs(struct net_device *ndev, void *type_data) * higher than this TC have been enabled. */ if (tc == prio_next) { - if (!enetc_get_cbs_enable(&si->hw, prio_top)) { + if (!enetc_get_cbs_enable(hw, prio_top)) { dev_err(&ndev->dev, "Enable TC%d first before enable TC%d\n", prio_top, prio_next); return -EINVAL; } - bw_sum += enetc_get_cbs_bw(&si->hw, prio_top); + bw_sum += enetc_get_cbs_bw(hw, prio_top); } if (bw_sum + bw >= 100) { @@ -239,7 +237,7 @@ int enetc_setup_tc_cbs(struct net_device *ndev, void *type_data) return -EINVAL; } - enetc_port_rd(&si->hw, ENETC_PTCMSDUR(tc)); + enetc_port_rd(hw, ENETC_PTCMSDUR(tc)); /* For top prio TC, the max_interfrence_size is maxSizedFrame. * @@ -259,8 +257,8 @@ int enetc_setup_tc_cbs(struct net_device *ndev, void *type_data) u32 m0, ma, r0, ra; m0 = port_frame_max_size * 8; - ma = enetc_port_rd(&si->hw, ENETC_PTCMSDUR(prio_top)) * 8; - ra = enetc_get_cbs_bw(&si->hw, prio_top) * + ma = enetc_port_rd(hw, ENETC_PTCMSDUR(prio_top)) * 8; + ra = enetc_get_cbs_bw(hw, prio_top) * port_transmit_rate * 10000ULL; r0 = port_transmit_rate * 1000000ULL; max_interference_size = m0 + ma + @@ -280,10 +278,10 @@ int enetc_setup_tc_cbs(struct net_device *ndev, void *type_data) hi_credit_reg = (u32)div_u64((ENETC_CLK * 100ULL) * hi_credit_bit, port_transmit_rate * 1000000ULL); - enetc_port_wr(&si->hw, ENETC_PTCCBSR1(tc), hi_credit_reg); + enetc_port_wr(hw, ENETC_PTCCBSR1(tc), hi_credit_reg); /* Set bw register and enable this traffic class */ - enetc_port_wr(&si->hw, ENETC_PTCCBSR0(tc), bw | ENETC_CBSE); + enetc_port_wr(hw, ENETC_PTCCBSR0(tc), bw | ENETC_CBSE); return 0; } @@ -293,6 +291,7 @@ int enetc_setup_tc_txtime(struct net_device *ndev, void *type_data) struct enetc_ndev_priv *priv = netdev_priv(ndev); struct tc_etf_qopt_offload *qopt = type_data; u8 tc_nums = netdev_get_num_tc(ndev); + struct enetc_hw *hw = &priv->si->hw; int tc; if (!tc_nums) @@ -304,12 +303,11 @@ int enetc_setup_tc_txtime(struct net_device *ndev, void *type_data) return -EINVAL; /* TSD and Qbv are mutually exclusive in hardware */ - if (enetc_rd(&priv->si->hw, ENETC_QBV_PTGCR_OFFSET) & ENETC_QBV_TGE) + if (enetc_rd(hw, ENETC_PTGCR) & ENETC_PTGCR_TGE) return -EBUSY; priv->tx_ring[tc]->tsd_enable = qopt->enable; - enetc_port_wr(&priv->si->hw, ENETC_PTCTSDR(tc), - qopt->enable ? ENETC_TSDE : 0); + enetc_port_wr(hw, ENETC_PTCTSDR(tc), qopt->enable ? ENETC_TSDE : 0); return 0; } @@ -1601,3 +1599,23 @@ int enetc_setup_tc_psfp(struct net_device *ndev, void *type_data) return 0; } + +int enetc_qos_query_caps(struct net_device *ndev, void *type_data) +{ + struct enetc_ndev_priv *priv = netdev_priv(ndev); + struct tc_query_caps_base *base = type_data; + struct enetc_si *si = priv->si; + + switch (base->type) { + case TC_SETUP_QDISC_TAPRIO: { + struct tc_taprio_caps *caps = base->caps; + + if (si->hw_features & ENETC_SI_F_QBV) + caps->supports_queue_max_sdu = true; + + return 0; + } + default: + return -EOPNOTSUPP; + } +} diff --git a/drivers/net/ethernet/freescale/fec.h b/drivers/net/ethernet/freescale/fec.h index a5fed00cb971..33f84a30e167 100644 --- a/drivers/net/ethernet/freescale/fec.h +++ b/drivers/net/ethernet/freescale/fec.h @@ -17,8 +17,11 @@ #include <linux/clocksource.h> #include <linux/net_tstamp.h> #include <linux/pm_qos.h> +#include <linux/bpf.h> #include <linux/ptp_clock_kernel.h> #include <linux/timecounter.h> +#include <dt-bindings/firmware/imx/rsrc.h> +#include <linux/firmware/imx/sci.h> #if defined(CONFIG_M523x) || defined(CONFIG_M527x) || defined(CONFIG_M528x) || \ defined(CONFIG_M520x) || defined(CONFIG_M532x) || defined(CONFIG_ARM) || \ @@ -344,8 +347,11 @@ struct bufdesc_ex { * the skbuffer directly. */ +#define FEC_ENET_XDP_HEADROOM (XDP_PACKET_HEADROOM) + #define FEC_ENET_RX_PAGES 256 -#define FEC_ENET_RX_FRSIZE 2048 +#define FEC_ENET_RX_FRSIZE (PAGE_SIZE - FEC_ENET_XDP_HEADROOM \ + - SKB_DATA_ALIGN(sizeof(struct skb_shared_info))) #define FEC_ENET_RX_FRPPG (PAGE_SIZE / FEC_ENET_RX_FRSIZE) #define RX_RING_SIZE (FEC_ENET_RX_FRPPG * FEC_ENET_RX_PAGES) #define FEC_ENET_TX_FRSIZE 2048 @@ -515,6 +521,12 @@ struct bufdesc_prop { unsigned char dsize_log2; }; +struct fec_enet_priv_txrx_info { + int offset; + struct page *page; + struct sk_buff *skb; +}; + struct fec_enet_priv_tx_q { struct bufdesc_prop bd; unsigned char *tx_bounce[TX_RING_SIZE]; @@ -530,7 +542,14 @@ struct fec_enet_priv_tx_q { struct fec_enet_priv_rx_q { struct bufdesc_prop bd; - struct sk_buff *rx_skbuff[RX_RING_SIZE]; + struct fec_enet_priv_txrx_info rx_skb_info[RX_RING_SIZE]; + + /* page_pool */ + struct page_pool *page_pool; + struct xdp_rxq_info xdp_rxq; + + /* rx queue number, in the range 0-7 */ + u8 id; }; struct fec_stop_mode_gpr { @@ -583,6 +602,7 @@ struct fec_enet_private { struct device_node *phy_node; bool rgmii_txc_dly; bool rgmii_rxc_dly; + bool rpm_active; int link; int full_duplex; int speed; @@ -639,6 +659,8 @@ struct fec_enet_private { int pps_enable; unsigned int next_counter; + struct imx_sc_ipc *ipc_handle; + u64 ethtool_stats[]; }; diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c index 92c55e1a5507..98d5cd313fdd 100644 --- a/drivers/net/ethernet/freescale/fec_main.c +++ b/drivers/net/ethernet/freescale/fec_main.c @@ -66,6 +66,8 @@ #include <linux/mfd/syscon.h> #include <linux/regmap.h> #include <soc/imx/cpuidle.h> +#include <linux/filter.h> +#include <linux/bpf.h> #include <asm/cacheflush.h> @@ -156,6 +158,13 @@ static const struct fec_devinfo fec_imx8qm_info = { FEC_QUIRK_DELAYED_CLKS_SUPPORT, }; +static const struct fec_devinfo fec_s32v234_info = { + .quirks = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_GBIT | + FEC_QUIRK_HAS_BUFDESC_EX | FEC_QUIRK_HAS_CSUM | + FEC_QUIRK_HAS_VLAN | FEC_QUIRK_HAS_AVB | + FEC_QUIRK_ERR007885 | FEC_QUIRK_BUG_CAPTURE, +}; + static struct platform_device_id fec_devtype[] = { { /* keep it for coldfire */ @@ -189,6 +198,9 @@ static struct platform_device_id fec_devtype[] = { .name = "imx8qm-fec", .driver_data = (kernel_ulong_t)&fec_imx8qm_info, }, { + .name = "s32v234-fec", + .driver_data = (kernel_ulong_t)&fec_s32v234_info, + }, { /* sentinel */ } }; @@ -204,6 +216,7 @@ enum imx_fec_type { IMX6UL_FEC, IMX8MQ_FEC, IMX8QM_FEC, + S32V234_FEC, }; static const struct of_device_id fec_dt_ids[] = { @@ -216,6 +229,7 @@ static const struct of_device_id fec_dt_ids[] = { { .compatible = "fsl,imx6ul-fec", .data = &fec_devtype[IMX6UL_FEC], }, { .compatible = "fsl,imx8mq-fec", .data = &fec_devtype[IMX8MQ_FEC], }, { .compatible = "fsl,imx8qm-fec", .data = &fec_devtype[IMX8QM_FEC], }, + { .compatible = "fsl,s32v234-fec", .data = &fec_devtype[S32V234_FEC], }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, fec_dt_ids); @@ -410,6 +424,48 @@ fec_enet_clear_csum(struct sk_buff *skb, struct net_device *ndev) return 0; } +static int +fec_enet_create_page_pool(struct fec_enet_private *fep, + struct fec_enet_priv_rx_q *rxq, int size) +{ + struct page_pool_params pp_params = { + .order = 0, + .flags = PP_FLAG_DMA_MAP | PP_FLAG_DMA_SYNC_DEV, + .pool_size = size, + .nid = dev_to_node(&fep->pdev->dev), + .dev = &fep->pdev->dev, + .dma_dir = DMA_FROM_DEVICE, + .offset = FEC_ENET_XDP_HEADROOM, + .max_len = FEC_ENET_RX_FRSIZE, + }; + int err; + + rxq->page_pool = page_pool_create(&pp_params); + if (IS_ERR(rxq->page_pool)) { + err = PTR_ERR(rxq->page_pool); + rxq->page_pool = NULL; + return err; + } + + err = xdp_rxq_info_reg(&rxq->xdp_rxq, fep->netdev, rxq->id, 0); + if (err < 0) + goto err_free_pp; + + err = xdp_rxq_info_reg_mem_model(&rxq->xdp_rxq, MEM_TYPE_PAGE_POOL, + rxq->page_pool); + if (err) + goto err_unregister_rxq; + + return 0; + +err_unregister_rxq: + xdp_rxq_info_unreg(&rxq->xdp_rxq); +err_free_pp: + page_pool_destroy(rxq->page_pool); + rxq->page_pool = NULL; + return err; +} + static struct bufdesc * fec_enet_txq_submit_frag_skb(struct fec_enet_priv_tx_q *txq, struct sk_buff *skb, @@ -1168,6 +1224,34 @@ fec_restart(struct net_device *ndev) } +static int fec_enet_ipc_handle_init(struct fec_enet_private *fep) +{ + if (!(of_machine_is_compatible("fsl,imx8qm") || + of_machine_is_compatible("fsl,imx8qxp") || + of_machine_is_compatible("fsl,imx8dxl"))) + return 0; + + return imx_scu_get_handle(&fep->ipc_handle); +} + +static void fec_enet_ipg_stop_set(struct fec_enet_private *fep, bool enabled) +{ + struct device_node *np = fep->pdev->dev.of_node; + u32 rsrc_id, val; + int idx; + + if (!np || !fep->ipc_handle) + return; + + idx = of_alias_get_id(np, "ethernet"); + if (idx < 0) + idx = 0; + rsrc_id = idx ? IMX_SC_R_ENET_1 : IMX_SC_R_ENET_0; + + val = enabled ? 1 : 0; + imx_sc_misc_set_control(fep->ipc_handle, rsrc_id, IMX_SC_C_IPG_STOP, val); +} + static void fec_enet_stop_mode(struct fec_enet_private *fep, bool enabled) { struct fec_platform_data *pdata = fep->pdev->dev.platform_data; @@ -1183,6 +1267,8 @@ static void fec_enet_stop_mode(struct fec_enet_private *fep, bool enabled) BIT(stop_gpr->bit), 0); } else if (pdata && pdata->sleep_mode_enable) { pdata->sleep_mode_enable(enabled); + } else { + fec_enet_ipg_stop_set(fep, enabled); } } @@ -1408,7 +1494,7 @@ static void fec_enet_tx(struct net_device *ndev) fec_enet_tx_queue(ndev, i); } -static int +static int __maybe_unused fec_enet_new_rxbdp(struct net_device *ndev, struct bufdesc *bdp, struct sk_buff *skb) { struct fec_enet_private *fep = netdev_priv(ndev); @@ -1428,8 +1514,9 @@ fec_enet_new_rxbdp(struct net_device *ndev, struct bufdesc *bdp, struct sk_buff return 0; } -static bool fec_enet_copybreak(struct net_device *ndev, struct sk_buff **skb, - struct bufdesc *bdp, u32 length, bool swap) +static bool __maybe_unused +fec_enet_copybreak(struct net_device *ndev, struct sk_buff **skb, + struct bufdesc *bdp, u32 length, bool swap) { struct fec_enet_private *fep = netdev_priv(ndev); struct sk_buff *new_skb; @@ -1454,6 +1541,21 @@ static bool fec_enet_copybreak(struct net_device *ndev, struct sk_buff **skb, return true; } +static void fec_enet_update_cbd(struct fec_enet_priv_rx_q *rxq, + struct bufdesc *bdp, int index) +{ + struct page *new_page; + dma_addr_t phys_addr; + + new_page = page_pool_dev_alloc_pages(rxq->page_pool); + WARN_ON(!new_page); + rxq->rx_skb_info[index].page = new_page; + + rxq->rx_skb_info[index].offset = FEC_ENET_XDP_HEADROOM; + phys_addr = page_pool_get_dma_addr(new_page) + FEC_ENET_XDP_HEADROOM; + bdp->cbd_bufaddr = cpu_to_fec32(phys_addr); +} + /* During a receive, the bd_rx.cur points to the current incoming buffer. * When we update through the ring, if the next incoming buffer has * not been given to the system, we just set the empty indicator, @@ -1466,7 +1568,6 @@ fec_enet_rx_queue(struct net_device *ndev, int budget, u16 queue_id) struct fec_enet_priv_rx_q *rxq; struct bufdesc *bdp; unsigned short status; - struct sk_buff *skb_new = NULL; struct sk_buff *skb; ushort pkt_len; __u8 *data; @@ -1475,8 +1576,8 @@ fec_enet_rx_queue(struct net_device *ndev, int budget, u16 queue_id) bool vlan_packet_rcvd = false; u16 vlan_tag; int index = 0; - bool is_copybreak; bool need_swap = fep->quirks & FEC_QUIRK_SWAP_FRAME; + struct page *page; #ifdef CONFIG_M532x flush_cache_all(); @@ -1528,31 +1629,25 @@ fec_enet_rx_queue(struct net_device *ndev, int budget, u16 queue_id) ndev->stats.rx_bytes += pkt_len; index = fec_enet_get_bd_index(bdp, &rxq->bd); - skb = rxq->rx_skbuff[index]; + page = rxq->rx_skb_info[index].page; + dma_sync_single_for_cpu(&fep->pdev->dev, + fec32_to_cpu(bdp->cbd_bufaddr), + pkt_len, + DMA_FROM_DEVICE); + prefetch(page_address(page)); + fec_enet_update_cbd(rxq, bdp, index); /* The packet length includes FCS, but we don't want to * include that when passing upstream as it messes up * bridging applications. */ - is_copybreak = fec_enet_copybreak(ndev, &skb, bdp, pkt_len - 4, - need_swap); - if (!is_copybreak) { - skb_new = netdev_alloc_skb(ndev, FEC_ENET_RX_FRSIZE); - if (unlikely(!skb_new)) { - ndev->stats.rx_dropped++; - goto rx_processing_done; - } - dma_unmap_single(&fep->pdev->dev, - fec32_to_cpu(bdp->cbd_bufaddr), - FEC_ENET_RX_FRSIZE - fep->rx_align, - DMA_FROM_DEVICE); - } - - prefetch(skb->data - NET_IP_ALIGN); + skb = build_skb(page_address(page), PAGE_SIZE); + skb_reserve(skb, FEC_ENET_XDP_HEADROOM); skb_put(skb, pkt_len - 4); + skb_mark_for_recycle(skb); data = skb->data; - if (!is_copybreak && need_swap) + if (need_swap) swap_buffer(data, pkt_len); #if !defined(CONFIG_M5272) @@ -1607,16 +1702,6 @@ fec_enet_rx_queue(struct net_device *ndev, int budget, u16 queue_id) skb_record_rx_queue(skb, queue_id); napi_gro_receive(&fep->napi, skb); - if (is_copybreak) { - dma_sync_single_for_device(&fep->pdev->dev, - fec32_to_cpu(bdp->cbd_bufaddr), - FEC_ENET_RX_FRSIZE - fep->rx_align, - DMA_FROM_DEVICE); - } else { - rxq->rx_skbuff[index] = skb_new; - fec_enet_new_rxbdp(ndev, bdp, skb_new); - } - rx_processing_done: /* Clear the status flags for this buffer */ status &= ~BD_ENET_RX_STATS; @@ -2105,13 +2190,13 @@ static int fec_enet_mii_probe(struct net_device *ndev) continue; if (dev_id--) continue; - strlcpy(mdio_bus_id, fep->mii_bus->id, MII_BUS_ID_SIZE); + strscpy(mdio_bus_id, fep->mii_bus->id, MII_BUS_ID_SIZE); break; } if (phy_id >= PHY_MAX_ADDR) { netdev_info(ndev, "no PHY, assuming direct connection to switch\n"); - strlcpy(mdio_bus_id, "fixed-0", MII_BUS_ID_SIZE); + strscpy(mdio_bus_id, "fixed-0", MII_BUS_ID_SIZE); phy_id = 0; } @@ -2295,9 +2380,9 @@ static void fec_enet_get_drvinfo(struct net_device *ndev, { struct fec_enet_private *fep = netdev_priv(ndev); - strlcpy(info->driver, fep->pdev->dev.driver->name, + strscpy(info->driver, fep->pdev->dev.driver->name, sizeof(info->driver)); - strlcpy(info->bus_info, dev_name(&ndev->dev), sizeof(info->bus_info)); + strscpy(info->bus_info, dev_name(&ndev->dev), sizeof(info->bus_info)); } static int fec_enet_get_regs_len(struct net_device *ndev) @@ -2960,26 +3045,19 @@ static void fec_enet_free_buffers(struct net_device *ndev) struct fec_enet_private *fep = netdev_priv(ndev); unsigned int i; struct sk_buff *skb; - struct bufdesc *bdp; struct fec_enet_priv_tx_q *txq; struct fec_enet_priv_rx_q *rxq; unsigned int q; for (q = 0; q < fep->num_rx_queues; q++) { rxq = fep->rx_queue[q]; - bdp = rxq->bd.base; - for (i = 0; i < rxq->bd.ring_size; i++) { - skb = rxq->rx_skbuff[i]; - rxq->rx_skbuff[i] = NULL; - if (skb) { - dma_unmap_single(&fep->pdev->dev, - fec32_to_cpu(bdp->cbd_bufaddr), - FEC_ENET_RX_FRSIZE - fep->rx_align, - DMA_FROM_DEVICE); - dev_kfree_skb(skb); - } - bdp = fec_enet_get_nextdesc(bdp, &rxq->bd); - } + for (i = 0; i < rxq->bd.ring_size; i++) + page_pool_release_page(rxq->page_pool, rxq->rx_skb_info[i].page); + + if (xdp_rxq_info_is_reg(&rxq->xdp_rxq)) + xdp_rxq_info_unreg(&rxq->xdp_rxq); + page_pool_destroy(rxq->page_pool); + rxq->page_pool = NULL; } for (q = 0; q < fep->num_tx_queues; q++) { @@ -3069,24 +3147,31 @@ static int fec_enet_alloc_rxq_buffers(struct net_device *ndev, unsigned int queue) { struct fec_enet_private *fep = netdev_priv(ndev); - unsigned int i; - struct sk_buff *skb; - struct bufdesc *bdp; struct fec_enet_priv_rx_q *rxq; + dma_addr_t phys_addr; + struct bufdesc *bdp; + struct page *page; + int i, err; rxq = fep->rx_queue[queue]; bdp = rxq->bd.base; + + err = fec_enet_create_page_pool(fep, rxq, rxq->bd.ring_size); + if (err < 0) { + netdev_err(ndev, "%s failed queue %d (%d)\n", __func__, queue, err); + return err; + } + for (i = 0; i < rxq->bd.ring_size; i++) { - skb = __netdev_alloc_skb(ndev, FEC_ENET_RX_FRSIZE, GFP_KERNEL); - if (!skb) + page = page_pool_dev_alloc_pages(rxq->page_pool); + if (!page) goto err_alloc; - if (fec_enet_new_rxbdp(ndev, bdp, skb)) { - dev_kfree_skb(skb); - goto err_alloc; - } + phys_addr = page_pool_get_dma_addr(page) + FEC_ENET_XDP_HEADROOM; + bdp->cbd_bufaddr = cpu_to_fec32(phys_addr); - rxq->rx_skbuff[i] = skb; + rxq->rx_skb_info[i].page = page; + rxq->rx_skb_info[i].offset = FEC_ENET_XDP_HEADROOM; bdp->cbd_sc = cpu_to_fec16(BD_ENET_RX_EMPTY); if (fep->bufdesc_ex) { @@ -3566,7 +3651,7 @@ static int fec_enet_init(struct net_device *ndev) ndev->ethtool_ops = &fec_enet_ethtool_ops; writel(FEC_RX_DISABLED_IMASK, fep->hwp + FEC_IMASK); - netif_napi_add(ndev, &fep->napi, fec_enet_rx_napi, NAPI_POLL_WEIGHT); + netif_napi_add(ndev, &fep->napi, fec_enet_rx_napi); if (fep->quirks & FEC_QUIRK_HAS_VLAN) /* enable hw VLAN support */ @@ -3824,6 +3909,10 @@ fec_probe(struct platform_device *pdev) !of_property_read_bool(np, "fsl,err006687-workaround-present")) fep->quirks |= FEC_QUIRK_ERR006687; + ret = fec_enet_ipc_handle_init(fep); + if (ret) + goto failed_ipc_init; + if (of_get_property(np, "fsl,magic-packet", NULL)) fep->wol_flag |= FEC_WOL_HAS_MAGIC_PACKET; @@ -4021,6 +4110,7 @@ failed_rgmii_delay: of_phy_deregister_fixed_link(np); of_node_put(phy_node); failed_stop_mode: +failed_ipc_init: failed_phy: dev_id--; failed_ioremap: @@ -4065,6 +4155,7 @@ static int __maybe_unused fec_suspend(struct device *dev) { struct net_device *ndev = dev_get_drvdata(dev); struct fec_enet_private *fep = netdev_priv(ndev); + int ret; rtnl_lock(); if (netif_running(ndev)) { @@ -4089,6 +4180,15 @@ static int __maybe_unused fec_suspend(struct device *dev) } /* It's safe to disable clocks since interrupts are masked */ fec_enet_clk_enable(ndev, false); + + fep->rpm_active = !pm_runtime_status_suspended(dev); + if (fep->rpm_active) { + ret = pm_runtime_force_suspend(dev); + if (ret < 0) { + rtnl_unlock(); + return ret; + } + } } rtnl_unlock(); @@ -4119,6 +4219,9 @@ static int __maybe_unused fec_resume(struct device *dev) rtnl_lock(); if (netif_running(ndev)) { + if (fep->rpm_active) + pm_runtime_force_resume(dev); + ret = fec_enet_clk_enable(ndev, true); if (ret) { rtnl_unlock(); diff --git a/drivers/net/ethernet/freescale/fec_ptp.c b/drivers/net/ethernet/freescale/fec_ptp.c index 3dc3c0b626c2..cffd9ad499dd 100644 --- a/drivers/net/ethernet/freescale/fec_ptp.c +++ b/drivers/net/ethernet/freescale/fec_ptp.c @@ -578,7 +578,7 @@ void fec_ptp_init(struct platform_device *pdev, int irq_idx) int ret; fep->ptp_caps.owner = THIS_MODULE; - strlcpy(fep->ptp_caps.name, "fec ptp", sizeof(fep->ptp_caps.name)); + strscpy(fep->ptp_caps.name, "fec ptp", sizeof(fep->ptp_caps.name)); fep->ptp_caps.max_adj = 250000000; fep->ptp_caps.n_alarm = 0; diff --git a/drivers/net/ethernet/freescale/fman/fman.c b/drivers/net/ethernet/freescale/fman/fman.c index 8f0db61cb1f6..9d85fb136e34 100644 --- a/drivers/net/ethernet/freescale/fman/fman.c +++ b/drivers/net/ethernet/freescale/fman/fman.c @@ -1,34 +1,7 @@ +// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-or-later /* - * Copyright 2008-2015 Freescale Semiconductor Inc. + * Copyright 2008 - 2015 Freescale Semiconductor Inc. * Copyright 2020 NXP - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of Freescale Semiconductor nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * - * ALTERNATIVELY, this software may be distributed under the terms of the - * GNU General Public License ("GPL") as published by the Free Software - * Foundation, either version 2 of that License or (at your option) any - * later version. - * - * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt diff --git a/drivers/net/ethernet/freescale/fman/fman.h b/drivers/net/ethernet/freescale/fman/fman.h index f2ede1360f03..2ea575a46675 100644 --- a/drivers/net/ethernet/freescale/fman/fman.h +++ b/drivers/net/ethernet/freescale/fman/fman.h @@ -1,34 +1,7 @@ +/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-or-later */ /* - * Copyright 2008-2015 Freescale Semiconductor Inc. + * Copyright 2008 - 2015 Freescale Semiconductor Inc. * Copyright 2020 NXP - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of Freescale Semiconductor nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * - * ALTERNATIVELY, this software may be distributed under the terms of the - * GNU General Public License ("GPL") as published by the Free Software - * Foundation, either version 2 of that License or (at your option) any - * later version. - * - * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef __FM_H diff --git a/drivers/net/ethernet/freescale/fman/fman_dtsec.c b/drivers/net/ethernet/freescale/fman/fman_dtsec.c index 1950a8936bc0..6617932fd3fd 100644 --- a/drivers/net/ethernet/freescale/fman/fman_dtsec.c +++ b/drivers/net/ethernet/freescale/fman/fman_dtsec.c @@ -1,39 +1,13 @@ +// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-or-later /* - * Copyright 2008-2015 Freescale Semiconductor Inc. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of Freescale Semiconductor nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * - * ALTERNATIVELY, this software may be distributed under the terms of the - * GNU General Public License ("GPL") as published by the Free Software - * Foundation, either version 2 of that License or (at your option) any - * later version. - * - * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Copyright 2008 - 2015 Freescale Semiconductor Inc. */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include "fman_dtsec.h" #include "fman.h" +#include "mac.h" #include <linux/slab.h> #include <linux/bitrev.h> @@ -327,7 +301,7 @@ struct fman_mac { /* Ethernet physical interface */ phy_interface_t phy_if; u16 max_speed; - void *dev_id; /* device cookie used by the exception cbs */ + struct mac_device *dev_id; /* device cookie used by the exception cbs */ fman_mac_exception_cb *exception_cb; fman_mac_exception_cb *event_cb; /* Number of individual addresses in registers for this station */ @@ -840,73 +814,45 @@ static void free_init_resources(struct fman_mac *dtsec) dtsec->unicast_addr_hash = NULL; } -int dtsec_cfg_max_frame_len(struct fman_mac *dtsec, u16 new_val) -{ - if (is_init_done(dtsec->dtsec_drv_param)) - return -EINVAL; - - dtsec->dtsec_drv_param->maximum_frame = new_val; - - return 0; -} - -int dtsec_cfg_pad_and_crc(struct fman_mac *dtsec, bool new_val) -{ - if (is_init_done(dtsec->dtsec_drv_param)) - return -EINVAL; - - dtsec->dtsec_drv_param->tx_pad_crc = new_val; - - return 0; -} - -static void graceful_start(struct fman_mac *dtsec, enum comm_mode mode) +static void graceful_start(struct fman_mac *dtsec) { struct dtsec_regs __iomem *regs = dtsec->regs; - if (mode & COMM_MODE_TX) - iowrite32be(ioread32be(®s->tctrl) & - ~TCTRL_GTS, ®s->tctrl); - if (mode & COMM_MODE_RX) - iowrite32be(ioread32be(®s->rctrl) & - ~RCTRL_GRS, ®s->rctrl); + iowrite32be(ioread32be(®s->tctrl) & ~TCTRL_GTS, ®s->tctrl); + iowrite32be(ioread32be(®s->rctrl) & ~RCTRL_GRS, ®s->rctrl); } -static void graceful_stop(struct fman_mac *dtsec, enum comm_mode mode) +static void graceful_stop(struct fman_mac *dtsec) { struct dtsec_regs __iomem *regs = dtsec->regs; u32 tmp; /* Graceful stop - Assert the graceful Rx stop bit */ - if (mode & COMM_MODE_RX) { - tmp = ioread32be(®s->rctrl) | RCTRL_GRS; - iowrite32be(tmp, ®s->rctrl); + tmp = ioread32be(®s->rctrl) | RCTRL_GRS; + iowrite32be(tmp, ®s->rctrl); - if (dtsec->fm_rev_info.major == 2) { - /* Workaround for dTSEC Errata A002 */ - usleep_range(100, 200); - } else { - /* Workaround for dTSEC Errata A004839 */ - usleep_range(10, 50); - } + if (dtsec->fm_rev_info.major == 2) { + /* Workaround for dTSEC Errata A002 */ + usleep_range(100, 200); + } else { + /* Workaround for dTSEC Errata A004839 */ + usleep_range(10, 50); } /* Graceful stop - Assert the graceful Tx stop bit */ - if (mode & COMM_MODE_TX) { - if (dtsec->fm_rev_info.major == 2) { - /* dTSEC Errata A004: Do not use TCTRL[GTS]=1 */ - pr_debug("GTS not supported due to DTSEC_A004 Errata.\n"); - } else { - tmp = ioread32be(®s->tctrl) | TCTRL_GTS; - iowrite32be(tmp, ®s->tctrl); + if (dtsec->fm_rev_info.major == 2) { + /* dTSEC Errata A004: Do not use TCTRL[GTS]=1 */ + pr_debug("GTS not supported due to DTSEC_A004 Errata.\n"); + } else { + tmp = ioread32be(®s->tctrl) | TCTRL_GTS; + iowrite32be(tmp, ®s->tctrl); - /* Workaround for dTSEC Errata A0012, A0014 */ - usleep_range(10, 50); - } + /* Workaround for dTSEC Errata A0012, A0014 */ + usleep_range(10, 50); } } -int dtsec_enable(struct fman_mac *dtsec, enum comm_mode mode) +static int dtsec_enable(struct fman_mac *dtsec) { struct dtsec_regs __iomem *regs = dtsec->regs; u32 tmp; @@ -916,58 +862,42 @@ int dtsec_enable(struct fman_mac *dtsec, enum comm_mode mode) /* Enable */ tmp = ioread32be(®s->maccfg1); - if (mode & COMM_MODE_RX) - tmp |= MACCFG1_RX_EN; - if (mode & COMM_MODE_TX) - tmp |= MACCFG1_TX_EN; - + tmp |= MACCFG1_RX_EN | MACCFG1_TX_EN; iowrite32be(tmp, ®s->maccfg1); /* Graceful start - clear the graceful Rx/Tx stop bit */ - graceful_start(dtsec, mode); + graceful_start(dtsec); return 0; } -int dtsec_disable(struct fman_mac *dtsec, enum comm_mode mode) +static void dtsec_disable(struct fman_mac *dtsec) { struct dtsec_regs __iomem *regs = dtsec->regs; u32 tmp; - if (!is_init_done(dtsec->dtsec_drv_param)) - return -EINVAL; + WARN_ON_ONCE(!is_init_done(dtsec->dtsec_drv_param)); /* Graceful stop - Assert the graceful Rx/Tx stop bit */ - graceful_stop(dtsec, mode); + graceful_stop(dtsec); tmp = ioread32be(®s->maccfg1); - if (mode & COMM_MODE_RX) - tmp &= ~MACCFG1_RX_EN; - if (mode & COMM_MODE_TX) - tmp &= ~MACCFG1_TX_EN; - + tmp &= ~(MACCFG1_RX_EN | MACCFG1_TX_EN); iowrite32be(tmp, ®s->maccfg1); - - return 0; } -int dtsec_set_tx_pause_frames(struct fman_mac *dtsec, - u8 __maybe_unused priority, - u16 pause_time, u16 __maybe_unused thresh_time) +static int dtsec_set_tx_pause_frames(struct fman_mac *dtsec, + u8 __maybe_unused priority, + u16 pause_time, + u16 __maybe_unused thresh_time) { struct dtsec_regs __iomem *regs = dtsec->regs; - enum comm_mode mode = COMM_MODE_NONE; u32 ptv = 0; if (!is_init_done(dtsec->dtsec_drv_param)) return -EINVAL; - if ((ioread32be(®s->rctrl) & RCTRL_GRS) == 0) - mode |= COMM_MODE_RX; - if ((ioread32be(®s->tctrl) & TCTRL_GTS) == 0) - mode |= COMM_MODE_TX; - - graceful_stop(dtsec, mode); + graceful_stop(dtsec); if (pause_time) { /* FM_BAD_TX_TS_IN_B_2_B_ERRATA_DTSEC_A003 Errata workaround */ @@ -989,26 +919,20 @@ int dtsec_set_tx_pause_frames(struct fman_mac *dtsec, iowrite32be(ioread32be(®s->maccfg1) & ~MACCFG1_TX_FLOW, ®s->maccfg1); - graceful_start(dtsec, mode); + graceful_start(dtsec); return 0; } -int dtsec_accept_rx_pause_frames(struct fman_mac *dtsec, bool en) +static int dtsec_accept_rx_pause_frames(struct fman_mac *dtsec, bool en) { struct dtsec_regs __iomem *regs = dtsec->regs; - enum comm_mode mode = COMM_MODE_NONE; u32 tmp; if (!is_init_done(dtsec->dtsec_drv_param)) return -EINVAL; - if ((ioread32be(®s->rctrl) & RCTRL_GRS) == 0) - mode |= COMM_MODE_RX; - if ((ioread32be(®s->tctrl) & TCTRL_GTS) == 0) - mode |= COMM_MODE_TX; - - graceful_stop(dtsec, mode); + graceful_stop(dtsec); tmp = ioread32be(®s->maccfg1); if (en) @@ -1017,25 +941,18 @@ int dtsec_accept_rx_pause_frames(struct fman_mac *dtsec, bool en) tmp &= ~MACCFG1_RX_FLOW; iowrite32be(tmp, ®s->maccfg1); - graceful_start(dtsec, mode); + graceful_start(dtsec); return 0; } -int dtsec_modify_mac_address(struct fman_mac *dtsec, const enet_addr_t *enet_addr) +static int dtsec_modify_mac_address(struct fman_mac *dtsec, + const enet_addr_t *enet_addr) { - struct dtsec_regs __iomem *regs = dtsec->regs; - enum comm_mode mode = COMM_MODE_NONE; - if (!is_init_done(dtsec->dtsec_drv_param)) return -EINVAL; - if ((ioread32be(®s->rctrl) & RCTRL_GRS) == 0) - mode |= COMM_MODE_RX; - if ((ioread32be(®s->tctrl) & TCTRL_GTS) == 0) - mode |= COMM_MODE_TX; - - graceful_stop(dtsec, mode); + graceful_stop(dtsec); /* Initialize MAC Station Address registers (1 & 2) * Station address have to be swapped (big endian to little endian @@ -1043,12 +960,13 @@ int dtsec_modify_mac_address(struct fman_mac *dtsec, const enet_addr_t *enet_add dtsec->addr = ENET_ADDR_TO_UINT64(*enet_addr); set_mac_address(dtsec->regs, (const u8 *)(*enet_addr)); - graceful_start(dtsec, mode); + graceful_start(dtsec); return 0; } -int dtsec_add_hash_mac_address(struct fman_mac *dtsec, enet_addr_t *eth_addr) +static int dtsec_add_hash_mac_address(struct fman_mac *dtsec, + enet_addr_t *eth_addr) { struct dtsec_regs __iomem *regs = dtsec->regs; struct eth_hash_entry *hash_entry; @@ -1114,7 +1032,7 @@ int dtsec_add_hash_mac_address(struct fman_mac *dtsec, enet_addr_t *eth_addr) return 0; } -int dtsec_set_allmulti(struct fman_mac *dtsec, bool enable) +static int dtsec_set_allmulti(struct fman_mac *dtsec, bool enable) { u32 tmp; struct dtsec_regs __iomem *regs = dtsec->regs; @@ -1133,7 +1051,7 @@ int dtsec_set_allmulti(struct fman_mac *dtsec, bool enable) return 0; } -int dtsec_set_tstamp(struct fman_mac *dtsec, bool enable) +static int dtsec_set_tstamp(struct fman_mac *dtsec, bool enable) { struct dtsec_regs __iomem *regs = dtsec->regs; u32 rctrl, tctrl; @@ -1158,7 +1076,8 @@ int dtsec_set_tstamp(struct fman_mac *dtsec, bool enable) return 0; } -int dtsec_del_hash_mac_address(struct fman_mac *dtsec, enet_addr_t *eth_addr) +static int dtsec_del_hash_mac_address(struct fman_mac *dtsec, + enet_addr_t *eth_addr) { struct dtsec_regs __iomem *regs = dtsec->regs; struct list_head *pos; @@ -1229,7 +1148,7 @@ int dtsec_del_hash_mac_address(struct fman_mac *dtsec, enet_addr_t *eth_addr) return 0; } -int dtsec_set_promiscuous(struct fman_mac *dtsec, bool new_val) +static int dtsec_set_promiscuous(struct fman_mac *dtsec, bool new_val) { struct dtsec_regs __iomem *regs = dtsec->regs; u32 tmp; @@ -1258,21 +1177,15 @@ int dtsec_set_promiscuous(struct fman_mac *dtsec, bool new_val) return 0; } -int dtsec_adjust_link(struct fman_mac *dtsec, u16 speed) +static int dtsec_adjust_link(struct fman_mac *dtsec, u16 speed) { struct dtsec_regs __iomem *regs = dtsec->regs; - enum comm_mode mode = COMM_MODE_NONE; u32 tmp; if (!is_init_done(dtsec->dtsec_drv_param)) return -EINVAL; - if ((ioread32be(®s->rctrl) & RCTRL_GRS) == 0) - mode |= COMM_MODE_RX; - if ((ioread32be(®s->tctrl) & TCTRL_GTS) == 0) - mode |= COMM_MODE_TX; - - graceful_stop(dtsec, mode); + graceful_stop(dtsec); tmp = ioread32be(®s->maccfg2); @@ -1293,12 +1206,12 @@ int dtsec_adjust_link(struct fman_mac *dtsec, u16 speed) tmp &= ~DTSEC_ECNTRL_R100M; iowrite32be(tmp, ®s->ecntrl); - graceful_start(dtsec, mode); + graceful_start(dtsec); return 0; } -int dtsec_restart_autoneg(struct fman_mac *dtsec) +static int dtsec_restart_autoneg(struct fman_mac *dtsec) { u16 tmp_reg16; @@ -1316,20 +1229,31 @@ int dtsec_restart_autoneg(struct fman_mac *dtsec) return 0; } -int dtsec_get_version(struct fman_mac *dtsec, u32 *mac_version) +static void adjust_link_dtsec(struct mac_device *mac_dev) { - struct dtsec_regs __iomem *regs = dtsec->regs; + struct phy_device *phy_dev = mac_dev->phy_dev; + struct fman_mac *fman_mac; + bool rx_pause, tx_pause; + int err; - if (!is_init_done(dtsec->dtsec_drv_param)) - return -EINVAL; + fman_mac = mac_dev->fman_mac; + if (!phy_dev->link) { + dtsec_restart_autoneg(fman_mac); - *mac_version = ioread32be(®s->tsec_id); + return; + } - return 0; + dtsec_adjust_link(fman_mac, phy_dev->speed); + mac_dev->update_speed(mac_dev, phy_dev->speed); + fman_get_pause_cfg(mac_dev, &rx_pause, &tx_pause); + err = fman_set_mac_active_pause(mac_dev, rx_pause, tx_pause); + if (err < 0) + dev_err(mac_dev->dev, "fman_set_mac_active_pause() = %d\n", + err); } -int dtsec_set_exception(struct fman_mac *dtsec, - enum fman_mac_exceptions exception, bool enable) +static int dtsec_set_exception(struct fman_mac *dtsec, + enum fman_mac_exceptions exception, bool enable) { struct dtsec_regs __iomem *regs = dtsec->regs; u32 bit_mask = 0; @@ -1382,7 +1306,7 @@ int dtsec_set_exception(struct fman_mac *dtsec, return 0; } -int dtsec_init(struct fman_mac *dtsec) +static int dtsec_init(struct fman_mac *dtsec) { struct dtsec_regs __iomem *regs = dtsec->regs; struct dtsec_cfg *dtsec_drv_param; @@ -1476,7 +1400,7 @@ int dtsec_init(struct fman_mac *dtsec) return 0; } -int dtsec_free(struct fman_mac *dtsec) +static int dtsec_free(struct fman_mac *dtsec) { free_init_resources(dtsec); @@ -1487,13 +1411,11 @@ int dtsec_free(struct fman_mac *dtsec) return 0; } -struct fman_mac *dtsec_config(struct fman_mac_params *params) +static struct fman_mac *dtsec_config(struct mac_device *mac_dev, + struct fman_mac_params *params) { struct fman_mac *dtsec; struct dtsec_cfg *dtsec_drv_param; - void __iomem *base_addr; - - base_addr = params->base_addr; /* allocate memory for the UCC GETH data structure. */ dtsec = kzalloc(sizeof(*dtsec), GFP_KERNEL); @@ -1510,10 +1432,10 @@ struct fman_mac *dtsec_config(struct fman_mac_params *params) set_dflts(dtsec_drv_param); - dtsec->regs = base_addr; - dtsec->addr = ENET_ADDR_TO_UINT64(params->addr); + dtsec->regs = mac_dev->vaddr; + dtsec->addr = ENET_ADDR_TO_UINT64(mac_dev->addr); dtsec->max_speed = params->max_speed; - dtsec->phy_if = params->phy_if; + dtsec->phy_if = mac_dev->phy_if; dtsec->mac_id = params->mac_id; dtsec->exceptions = (DTSEC_IMASK_BREN | DTSEC_IMASK_RXCEN | @@ -1530,34 +1452,87 @@ struct fman_mac *dtsec_config(struct fman_mac_params *params) DTSEC_IMASK_RDPEEN); dtsec->exception_cb = params->exception_cb; dtsec->event_cb = params->event_cb; - dtsec->dev_id = params->dev_id; + dtsec->dev_id = mac_dev; dtsec->ptp_tsu_enabled = dtsec->dtsec_drv_param->ptp_tsu_en; dtsec->en_tsu_err_exception = dtsec->dtsec_drv_param->ptp_exception_en; dtsec->fm = params->fm; dtsec->basex_if = params->basex_if; - if (!params->internal_phy_node) { + /* Save FMan revision */ + fman_get_revision(dtsec->fm, &dtsec->fm_rev_info); + + return dtsec; + +err_dtsec: + kfree(dtsec); + return NULL; +} + +int dtsec_initialization(struct mac_device *mac_dev, + struct device_node *mac_node, + struct fman_mac_params *params) +{ + int err; + struct fman_mac *dtsec; + struct device_node *phy_node; + + mac_dev->set_promisc = dtsec_set_promiscuous; + mac_dev->change_addr = dtsec_modify_mac_address; + mac_dev->add_hash_mac_addr = dtsec_add_hash_mac_address; + mac_dev->remove_hash_mac_addr = dtsec_del_hash_mac_address; + mac_dev->set_tx_pause = dtsec_set_tx_pause_frames; + mac_dev->set_rx_pause = dtsec_accept_rx_pause_frames; + mac_dev->set_exception = dtsec_set_exception; + mac_dev->set_allmulti = dtsec_set_allmulti; + mac_dev->set_tstamp = dtsec_set_tstamp; + mac_dev->set_multi = fman_set_multi; + mac_dev->adjust_link = adjust_link_dtsec; + mac_dev->enable = dtsec_enable; + mac_dev->disable = dtsec_disable; + + mac_dev->fman_mac = dtsec_config(mac_dev, params); + if (!mac_dev->fman_mac) { + err = -EINVAL; + goto _return; + } + + dtsec = mac_dev->fman_mac; + dtsec->dtsec_drv_param->maximum_frame = fman_get_max_frm(); + dtsec->dtsec_drv_param->tx_pad_crc = true; + + phy_node = of_parse_phandle(mac_node, "tbi-handle", 0); + if (!phy_node) { pr_err("TBI PHY node is not available\n"); - goto err_dtsec_drv_param; + err = -EINVAL; + goto _return_fm_mac_free; } - dtsec->tbiphy = of_phy_find_device(params->internal_phy_node); + dtsec->tbiphy = of_phy_find_device(phy_node); if (!dtsec->tbiphy) { pr_err("of_phy_find_device (TBI PHY) failed\n"); - goto err_dtsec_drv_param; + err = -EINVAL; + goto _return_fm_mac_free; } - put_device(&dtsec->tbiphy->mdio.dev); - /* Save FMan revision */ - fman_get_revision(dtsec->fm, &dtsec->fm_rev_info); + err = dtsec_init(dtsec); + if (err < 0) + goto _return_fm_mac_free; - return dtsec; + /* For 1G MAC, disable by default the MIB counters overflow interrupt */ + err = dtsec_set_exception(dtsec, FM_MAC_EX_1G_RX_MIB_CNT_OVFL, false); + if (err < 0) + goto _return_fm_mac_free; -err_dtsec_drv_param: - kfree(dtsec_drv_param); -err_dtsec: - kfree(dtsec); - return NULL; + dev_info(mac_dev->dev, "FMan dTSEC version: 0x%08x\n", + ioread32be(&dtsec->regs->tsec_id)); + + goto _return; + +_return_fm_mac_free: + dtsec_free(dtsec); + +_return: + return err; } diff --git a/drivers/net/ethernet/freescale/fman/fman_dtsec.h b/drivers/net/ethernet/freescale/fman/fman_dtsec.h index 68512c3bd6e5..8c72d280c51a 100644 --- a/drivers/net/ethernet/freescale/fman/fman_dtsec.h +++ b/drivers/net/ethernet/freescale/fman/fman_dtsec.h @@ -1,33 +1,6 @@ +/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-or-later */ /* - * Copyright 2008-2015 Freescale Semiconductor Inc. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of Freescale Semiconductor nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * - * ALTERNATIVELY, this software may be distributed under the terms of the - * GNU General Public License ("GPL") as published by the Free Software - * Foundation, either version 2 of that License or (at your option) any - * later version. - * - * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Copyright 2008 - 2015 Freescale Semiconductor Inc. */ #ifndef __DTSEC_H @@ -35,27 +8,10 @@ #include "fman_mac.h" -struct fman_mac *dtsec_config(struct fman_mac_params *params); -int dtsec_set_promiscuous(struct fman_mac *dtsec, bool new_val); -int dtsec_modify_mac_address(struct fman_mac *dtsec, const enet_addr_t *enet_addr); -int dtsec_adjust_link(struct fman_mac *dtsec, - u16 speed); -int dtsec_restart_autoneg(struct fman_mac *dtsec); -int dtsec_cfg_max_frame_len(struct fman_mac *dtsec, u16 new_val); -int dtsec_cfg_pad_and_crc(struct fman_mac *dtsec, bool new_val); -int dtsec_enable(struct fman_mac *dtsec, enum comm_mode mode); -int dtsec_disable(struct fman_mac *dtsec, enum comm_mode mode); -int dtsec_init(struct fman_mac *dtsec); -int dtsec_free(struct fman_mac *dtsec); -int dtsec_accept_rx_pause_frames(struct fman_mac *dtsec, bool en); -int dtsec_set_tx_pause_frames(struct fman_mac *dtsec, u8 priority, - u16 pause_time, u16 thresh_time); -int dtsec_set_exception(struct fman_mac *dtsec, - enum fman_mac_exceptions exception, bool enable); -int dtsec_add_hash_mac_address(struct fman_mac *dtsec, enet_addr_t *eth_addr); -int dtsec_del_hash_mac_address(struct fman_mac *dtsec, enet_addr_t *eth_addr); -int dtsec_get_version(struct fman_mac *dtsec, u32 *mac_version); -int dtsec_set_allmulti(struct fman_mac *dtsec, bool enable); -int dtsec_set_tstamp(struct fman_mac *dtsec, bool enable); +struct mac_device; + +int dtsec_initialization(struct mac_device *mac_dev, + struct device_node *mac_node, + struct fman_mac_params *params); #endif /* __DTSEC_H */ diff --git a/drivers/net/ethernet/freescale/fman/fman_keygen.c b/drivers/net/ethernet/freescale/fman/fman_keygen.c index e1bdfed16134..e73f6ef3c6ee 100644 --- a/drivers/net/ethernet/freescale/fman/fman_keygen.c +++ b/drivers/net/ethernet/freescale/fman/fman_keygen.c @@ -1,33 +1,6 @@ +// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-or-later /* * Copyright 2017 NXP - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of NXP nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * - * ALTERNATIVELY, this software may be distributed under the terms of the - * GNU General Public License ("GPL") as published by the Free Software - * Foundation, either version 2 of that License or (at your option) any - * later version. - * - * THIS SOFTWARE IS PROVIDED BY NXP ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL NXP BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt diff --git a/drivers/net/ethernet/freescale/fman/fman_keygen.h b/drivers/net/ethernet/freescale/fman/fman_keygen.h index c4640de3f4cb..2cb0df453074 100644 --- a/drivers/net/ethernet/freescale/fman/fman_keygen.h +++ b/drivers/net/ethernet/freescale/fman/fman_keygen.h @@ -1,33 +1,6 @@ +/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-or-later */ /* * Copyright 2017 NXP - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of NXP nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * - * ALTERNATIVELY, this software may be distributed under the terms of the - * GNU General Public License ("GPL") as published by the Free Software - * Foundation, either version 2 of that License or (at your option) any - * later version. - * - * THIS SOFTWARE IS PROVIDED BY NXP ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL NXP BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef __KEYGEN_H diff --git a/drivers/net/ethernet/freescale/fman/fman_mac.h b/drivers/net/ethernet/freescale/fman/fman_mac.h index 19f327efdaff..65887a3160d7 100644 --- a/drivers/net/ethernet/freescale/fman/fman_mac.h +++ b/drivers/net/ethernet/freescale/fman/fman_mac.h @@ -41,6 +41,7 @@ #include <linux/if_ether.h> struct fman_mac; +struct mac_device; /* Ethernet Address */ typedef u8 enet_addr_t[ETH_ALEN]; @@ -75,16 +76,6 @@ typedef u8 enet_addr_t[ETH_ALEN]; #define ETH_HASH_ENTRY_OBJ(ptr) \ hlist_entry_safe(ptr, struct eth_hash_entry, node) -/* Enumeration (bit flags) of communication modes (Transmit, - * receive or both). - */ -enum comm_mode { - COMM_MODE_NONE = 0, /* No transmit/receive communication */ - COMM_MODE_RX = 1, /* Only receive communication */ - COMM_MODE_TX = 2, /* Only transmit communication */ - COMM_MODE_RX_AND_TX = 3 /* Both transmit and receive communication */ -}; - /* FM MAC Exceptions */ enum fman_mac_exceptions { FM_MAC_EX_10G_MDIO_SCAN_EVENT = 0 @@ -168,30 +159,23 @@ struct eth_hash_entry { struct list_head node; }; -typedef void (fman_mac_exception_cb)(void *dev_id, - enum fman_mac_exceptions exceptions); +typedef void (fman_mac_exception_cb)(struct mac_device *dev_id, + enum fman_mac_exceptions exceptions); /* FMan MAC config input */ struct fman_mac_params { - /* Base of memory mapped FM MAC registers */ - void __iomem *base_addr; - /* MAC address of device; First octet is sent first */ - enet_addr_t addr; /* MAC ID; numbering of dTSEC and 1G-mEMAC: * 0 - FM_MAX_NUM_OF_1G_MACS; * numbering of 10G-MAC (TGEC) and 10G-mEMAC: * 0 - FM_MAX_NUM_OF_10G_MACS */ u8 mac_id; - /* PHY interface */ - phy_interface_t phy_if; /* Note that the speed should indicate the maximum rate that * this MAC should support rather than the actual speed; */ u16 max_speed; /* A handle to the FM object this port related to */ void *fm; - void *dev_id; /* device cookie used by the exception cbs */ fman_mac_exception_cb *event_cb; /* MDIO Events Callback Routine */ fman_mac_exception_cb *exception_cb;/* Exception Callback Routine */ /* SGMII/QSGII interface with 1000BaseX auto-negotiation between MAC @@ -200,8 +184,6 @@ struct fman_mac_params { * synchronize with far-end phy at 10Mbps, 100Mbps or 1000Mbps */ bool basex_if; - /* Pointer to TBI/PCS PHY node, used for TBI/PCS PHY access */ - struct device_node *internal_phy_node; }; struct eth_hash_t { diff --git a/drivers/net/ethernet/freescale/fman/fman_memac.c b/drivers/net/ethernet/freescale/fman/fman_memac.c index 2216b7f51d26..32d26cf17843 100644 --- a/drivers/net/ethernet/freescale/fman/fman_memac.c +++ b/drivers/net/ethernet/freescale/fman/fman_memac.c @@ -1,39 +1,13 @@ +// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-or-later /* - * Copyright 2008-2015 Freescale Semiconductor Inc. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of Freescale Semiconductor nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * - * ALTERNATIVELY, this software may be distributed under the terms of the - * GNU General Public License ("GPL") as published by the Free Software - * Foundation, either version 2 of that License or (at your option) any - * later version. - * - * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Copyright 2008 - 2015 Freescale Semiconductor Inc. */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include "fman_memac.h" #include "fman.h" +#include "mac.h" #include <linux/slab.h> #include <linux/io.h> @@ -337,7 +311,7 @@ struct fman_mac { /* Ethernet physical interface */ phy_interface_t phy_if; u16 max_speed; - void *dev_id; /* device cookie used by the exception cbs */ + struct mac_device *dev_id; /* device cookie used by the exception cbs */ fman_mac_exception_cb *exception_cb; fman_mac_exception_cb *event_cb; /* Pointer to driver's global address hash table */ @@ -712,7 +686,7 @@ static bool is_init_done(struct memac_cfg *memac_drv_params) return false; } -int memac_enable(struct fman_mac *memac, enum comm_mode mode) +static int memac_enable(struct fman_mac *memac) { struct memac_regs __iomem *regs = memac->regs; u32 tmp; @@ -721,36 +695,26 @@ int memac_enable(struct fman_mac *memac, enum comm_mode mode) return -EINVAL; tmp = ioread32be(®s->command_config); - if (mode & COMM_MODE_RX) - tmp |= CMD_CFG_RX_EN; - if (mode & COMM_MODE_TX) - tmp |= CMD_CFG_TX_EN; - + tmp |= CMD_CFG_RX_EN | CMD_CFG_TX_EN; iowrite32be(tmp, ®s->command_config); return 0; } -int memac_disable(struct fman_mac *memac, enum comm_mode mode) +static void memac_disable(struct fman_mac *memac) + { struct memac_regs __iomem *regs = memac->regs; u32 tmp; - if (!is_init_done(memac->memac_drv_param)) - return -EINVAL; + WARN_ON_ONCE(!is_init_done(memac->memac_drv_param)); tmp = ioread32be(®s->command_config); - if (mode & COMM_MODE_RX) - tmp &= ~CMD_CFG_RX_EN; - if (mode & COMM_MODE_TX) - tmp &= ~CMD_CFG_TX_EN; - + tmp &= ~(CMD_CFG_RX_EN | CMD_CFG_TX_EN); iowrite32be(tmp, ®s->command_config); - - return 0; } -int memac_set_promiscuous(struct fman_mac *memac, bool new_val) +static int memac_set_promiscuous(struct fman_mac *memac, bool new_val) { struct memac_regs __iomem *regs = memac->regs; u32 tmp; @@ -769,7 +733,7 @@ int memac_set_promiscuous(struct fman_mac *memac, bool new_val) return 0; } -int memac_adjust_link(struct fman_mac *memac, u16 speed) +static int memac_adjust_link(struct fman_mac *memac, u16 speed) { struct memac_regs __iomem *regs = memac->regs; u32 tmp; @@ -809,39 +773,26 @@ int memac_adjust_link(struct fman_mac *memac, u16 speed) return 0; } -int memac_cfg_max_frame_len(struct fman_mac *memac, u16 new_val) -{ - if (is_init_done(memac->memac_drv_param)) - return -EINVAL; - - memac->memac_drv_param->max_frame_length = new_val; - - return 0; -} - -int memac_cfg_reset_on_init(struct fman_mac *memac, bool enable) -{ - if (is_init_done(memac->memac_drv_param)) - return -EINVAL; - - memac->memac_drv_param->reset_on_init = enable; - - return 0; -} - -int memac_cfg_fixed_link(struct fman_mac *memac, - struct fixed_phy_status *fixed_link) +static void adjust_link_memac(struct mac_device *mac_dev) { - if (is_init_done(memac->memac_drv_param)) - return -EINVAL; + struct phy_device *phy_dev = mac_dev->phy_dev; + struct fman_mac *fman_mac; + bool rx_pause, tx_pause; + int err; - memac->memac_drv_param->fixed_link = fixed_link; + fman_mac = mac_dev->fman_mac; + memac_adjust_link(fman_mac, phy_dev->speed); + mac_dev->update_speed(mac_dev, phy_dev->speed); - return 0; + fman_get_pause_cfg(mac_dev, &rx_pause, &tx_pause); + err = fman_set_mac_active_pause(mac_dev, rx_pause, tx_pause); + if (err < 0) + dev_err(mac_dev->dev, "fman_set_mac_active_pause() = %d\n", + err); } -int memac_set_tx_pause_frames(struct fman_mac *memac, u8 priority, - u16 pause_time, u16 thresh_time) +static int memac_set_tx_pause_frames(struct fman_mac *memac, u8 priority, + u16 pause_time, u16 thresh_time) { struct memac_regs __iomem *regs = memac->regs; u32 tmp; @@ -878,7 +829,7 @@ int memac_set_tx_pause_frames(struct fman_mac *memac, u8 priority, return 0; } -int memac_accept_rx_pause_frames(struct fman_mac *memac, bool en) +static int memac_accept_rx_pause_frames(struct fman_mac *memac, bool en) { struct memac_regs __iomem *regs = memac->regs; u32 tmp; @@ -897,7 +848,8 @@ int memac_accept_rx_pause_frames(struct fman_mac *memac, bool en) return 0; } -int memac_modify_mac_address(struct fman_mac *memac, const enet_addr_t *enet_addr) +static int memac_modify_mac_address(struct fman_mac *memac, + const enet_addr_t *enet_addr) { if (!is_init_done(memac->memac_drv_param)) return -EINVAL; @@ -907,7 +859,8 @@ int memac_modify_mac_address(struct fman_mac *memac, const enet_addr_t *enet_add return 0; } -int memac_add_hash_mac_address(struct fman_mac *memac, enet_addr_t *eth_addr) +static int memac_add_hash_mac_address(struct fman_mac *memac, + enet_addr_t *eth_addr) { struct memac_regs __iomem *regs = memac->regs; struct eth_hash_entry *hash_entry; @@ -940,7 +893,7 @@ int memac_add_hash_mac_address(struct fman_mac *memac, enet_addr_t *eth_addr) return 0; } -int memac_set_allmulti(struct fman_mac *memac, bool enable) +static int memac_set_allmulti(struct fman_mac *memac, bool enable) { u32 entry; struct memac_regs __iomem *regs = memac->regs; @@ -963,12 +916,13 @@ int memac_set_allmulti(struct fman_mac *memac, bool enable) return 0; } -int memac_set_tstamp(struct fman_mac *memac, bool enable) +static int memac_set_tstamp(struct fman_mac *memac, bool enable) { return 0; /* Always enabled. */ } -int memac_del_hash_mac_address(struct fman_mac *memac, enet_addr_t *eth_addr) +static int memac_del_hash_mac_address(struct fman_mac *memac, + enet_addr_t *eth_addr) { struct memac_regs __iomem *regs = memac->regs; struct eth_hash_entry *hash_entry = NULL; @@ -1001,8 +955,8 @@ int memac_del_hash_mac_address(struct fman_mac *memac, enet_addr_t *eth_addr) return 0; } -int memac_set_exception(struct fman_mac *memac, - enum fman_mac_exceptions exception, bool enable) +static int memac_set_exception(struct fman_mac *memac, + enum fman_mac_exceptions exception, bool enable) { u32 bit_mask = 0; @@ -1024,13 +978,13 @@ int memac_set_exception(struct fman_mac *memac, return 0; } -int memac_init(struct fman_mac *memac) +static int memac_init(struct fman_mac *memac) { struct memac_cfg *memac_drv_param; u8 i; enet_addr_t eth_addr; bool slow_10g_if = false; - struct fixed_phy_status *fixed_link; + struct fixed_phy_status *fixed_link = NULL; int err; u32 reg32 = 0; @@ -1141,7 +1095,7 @@ int memac_init(struct fman_mac *memac) return 0; } -int memac_free(struct fman_mac *memac) +static int memac_free(struct fman_mac *memac) { free_init_resources(memac); @@ -1154,13 +1108,12 @@ int memac_free(struct fman_mac *memac) return 0; } -struct fman_mac *memac_config(struct fman_mac_params *params) +static struct fman_mac *memac_config(struct mac_device *mac_dev, + struct fman_mac_params *params) { struct fman_mac *memac; struct memac_cfg *memac_drv_param; - void __iomem *base_addr; - base_addr = params->base_addr; /* allocate memory for the m_emac data structure */ memac = kzalloc(sizeof(*memac), GFP_KERNEL); if (!memac) @@ -1178,38 +1131,121 @@ struct fman_mac *memac_config(struct fman_mac_params *params) set_dflts(memac_drv_param); - memac->addr = ENET_ADDR_TO_UINT64(params->addr); + memac->addr = ENET_ADDR_TO_UINT64(mac_dev->addr); - memac->regs = base_addr; + memac->regs = mac_dev->vaddr; memac->max_speed = params->max_speed; - memac->phy_if = params->phy_if; + memac->phy_if = mac_dev->phy_if; memac->mac_id = params->mac_id; memac->exceptions = (MEMAC_IMASK_TSECC_ER | MEMAC_IMASK_TECC_ER | MEMAC_IMASK_RECC_ER | MEMAC_IMASK_MGI); memac->exception_cb = params->exception_cb; memac->event_cb = params->event_cb; - memac->dev_id = params->dev_id; + memac->dev_id = mac_dev; memac->fm = params->fm; memac->basex_if = params->basex_if; /* Save FMan revision */ fman_get_revision(memac->fm, &memac->fm_rev_info); + return memac; +} + +int memac_initialization(struct mac_device *mac_dev, + struct device_node *mac_node, + struct fman_mac_params *params) +{ + int err; + struct device_node *phy_node; + struct fixed_phy_status *fixed_link; + struct fman_mac *memac; + + mac_dev->set_promisc = memac_set_promiscuous; + mac_dev->change_addr = memac_modify_mac_address; + mac_dev->add_hash_mac_addr = memac_add_hash_mac_address; + mac_dev->remove_hash_mac_addr = memac_del_hash_mac_address; + mac_dev->set_tx_pause = memac_set_tx_pause_frames; + mac_dev->set_rx_pause = memac_accept_rx_pause_frames; + mac_dev->set_exception = memac_set_exception; + mac_dev->set_allmulti = memac_set_allmulti; + mac_dev->set_tstamp = memac_set_tstamp; + mac_dev->set_multi = fman_set_multi; + mac_dev->adjust_link = adjust_link_memac; + mac_dev->enable = memac_enable; + mac_dev->disable = memac_disable; + + if (params->max_speed == SPEED_10000) + mac_dev->phy_if = PHY_INTERFACE_MODE_XGMII; + + mac_dev->fman_mac = memac_config(mac_dev, params); + if (!mac_dev->fman_mac) { + err = -EINVAL; + goto _return; + } + + memac = mac_dev->fman_mac; + memac->memac_drv_param->max_frame_length = fman_get_max_frm(); + memac->memac_drv_param->reset_on_init = true; if (memac->phy_if == PHY_INTERFACE_MODE_SGMII || memac->phy_if == PHY_INTERFACE_MODE_QSGMII) { - if (!params->internal_phy_node) { + phy_node = of_parse_phandle(mac_node, "pcsphy-handle", 0); + if (!phy_node) { pr_err("PCS PHY node is not available\n"); - memac_free(memac); - return NULL; + err = -EINVAL; + goto _return_fm_mac_free; } - memac->pcsphy = of_phy_find_device(params->internal_phy_node); + memac->pcsphy = of_phy_find_device(phy_node); if (!memac->pcsphy) { pr_err("of_phy_find_device (PCS PHY) failed\n"); - memac_free(memac); - return NULL; + err = -EINVAL; + goto _return_fm_mac_free; } } - return memac; + if (!mac_dev->phy_node && of_phy_is_fixed_link(mac_node)) { + struct phy_device *phy; + + err = of_phy_register_fixed_link(mac_node); + if (err) + goto _return_fm_mac_free; + + fixed_link = kzalloc(sizeof(*fixed_link), GFP_KERNEL); + if (!fixed_link) { + err = -ENOMEM; + goto _return_fm_mac_free; + } + + mac_dev->phy_node = of_node_get(mac_node); + phy = of_phy_find_device(mac_dev->phy_node); + if (!phy) { + err = -EINVAL; + of_node_put(mac_dev->phy_node); + goto _return_fixed_link_free; + } + + fixed_link->link = phy->link; + fixed_link->speed = phy->speed; + fixed_link->duplex = phy->duplex; + fixed_link->pause = phy->pause; + fixed_link->asym_pause = phy->asym_pause; + + put_device(&phy->mdio.dev); + memac->memac_drv_param->fixed_link = fixed_link; + } + + err = memac_init(mac_dev->fman_mac); + if (err < 0) + goto _return_fixed_link_free; + + dev_info(mac_dev->dev, "FMan MEMAC\n"); + + goto _return; + +_return_fixed_link_free: + kfree(fixed_link); +_return_fm_mac_free: + memac_free(mac_dev->fman_mac); +_return: + return err; } diff --git a/drivers/net/ethernet/freescale/fman/fman_memac.h b/drivers/net/ethernet/freescale/fman/fman_memac.h index 3820f7a22983..5a3a14f9684f 100644 --- a/drivers/net/ethernet/freescale/fman/fman_memac.h +++ b/drivers/net/ethernet/freescale/fman/fman_memac.h @@ -1,33 +1,6 @@ +/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-or-later */ /* - * Copyright 2008-2015 Freescale Semiconductor Inc. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of Freescale Semiconductor nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * - * ALTERNATIVELY, this software may be distributed under the terms of the - * GNU General Public License ("GPL") as published by the Free Software - * Foundation, either version 2 of that License or (at your option) any - * later version. - * - * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Copyright 2008 - 2015 Freescale Semiconductor Inc. */ #ifndef __MEMAC_H @@ -38,26 +11,10 @@ #include <linux/netdevice.h> #include <linux/phy_fixed.h> -struct fman_mac *memac_config(struct fman_mac_params *params); -int memac_set_promiscuous(struct fman_mac *memac, bool new_val); -int memac_modify_mac_address(struct fman_mac *memac, const enet_addr_t *enet_addr); -int memac_adjust_link(struct fman_mac *memac, u16 speed); -int memac_cfg_max_frame_len(struct fman_mac *memac, u16 new_val); -int memac_cfg_reset_on_init(struct fman_mac *memac, bool enable); -int memac_cfg_fixed_link(struct fman_mac *memac, - struct fixed_phy_status *fixed_link); -int memac_enable(struct fman_mac *memac, enum comm_mode mode); -int memac_disable(struct fman_mac *memac, enum comm_mode mode); -int memac_init(struct fman_mac *memac); -int memac_free(struct fman_mac *memac); -int memac_accept_rx_pause_frames(struct fman_mac *memac, bool en); -int memac_set_tx_pause_frames(struct fman_mac *memac, u8 priority, - u16 pause_time, u16 thresh_time); -int memac_set_exception(struct fman_mac *memac, - enum fman_mac_exceptions exception, bool enable); -int memac_add_hash_mac_address(struct fman_mac *memac, enet_addr_t *eth_addr); -int memac_del_hash_mac_address(struct fman_mac *memac, enet_addr_t *eth_addr); -int memac_set_allmulti(struct fman_mac *memac, bool enable); -int memac_set_tstamp(struct fman_mac *memac, bool enable); +struct mac_device; + +int memac_initialization(struct mac_device *mac_dev, + struct device_node *mac_node, + struct fman_mac_params *params); #endif /* __MEMAC_H */ diff --git a/drivers/net/ethernet/freescale/fman/fman_muram.c b/drivers/net/ethernet/freescale/fman/fman_muram.c index 7ad317e622bc..f557d68e5b76 100644 --- a/drivers/net/ethernet/freescale/fman/fman_muram.c +++ b/drivers/net/ethernet/freescale/fman/fman_muram.c @@ -1,33 +1,6 @@ +// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-or-later /* - * Copyright 2008-2015 Freescale Semiconductor Inc. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of Freescale Semiconductor nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * - * ALTERNATIVELY, this software may be distributed under the terms of the - * GNU General Public License ("GPL") as published by the Free Software - * Foundation, either version 2 of that License or (at your option) any - * later version. - * - * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Copyright 2008 - 2015 Freescale Semiconductor Inc. */ #include "fman_muram.h" diff --git a/drivers/net/ethernet/freescale/fman/fman_muram.h b/drivers/net/ethernet/freescale/fman/fman_muram.h index 453bf849eee1..3643af61bae2 100644 --- a/drivers/net/ethernet/freescale/fman/fman_muram.h +++ b/drivers/net/ethernet/freescale/fman/fman_muram.h @@ -1,34 +1,8 @@ +/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-or-later */ /* - * Copyright 2008-2015 Freescale Semiconductor Inc. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of Freescale Semiconductor nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * - * ALTERNATIVELY, this software may be distributed under the terms of the - * GNU General Public License ("GPL") as published by the Free Software - * Foundation, either version 2 of that License or (at your option) any - * later version. - * - * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Copyright 2008 - 2015 Freescale Semiconductor Inc. */ + #ifndef __FM_MURAM_EXT #define __FM_MURAM_EXT diff --git a/drivers/net/ethernet/freescale/fman/fman_port.c b/drivers/net/ethernet/freescale/fman/fman_port.c index 4c9d05c45c03..ab90fe2bee5e 100644 --- a/drivers/net/ethernet/freescale/fman/fman_port.c +++ b/drivers/net/ethernet/freescale/fman/fman_port.c @@ -1,33 +1,6 @@ +// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-or-later /* * Copyright 2008 - 2015 Freescale Semiconductor Inc. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of Freescale Semiconductor nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * - * ALTERNATIVELY, this software may be distributed under the terms of the - * GNU General Public License ("GPL") as published by the Free Software - * Foundation, either version 2 of that License or (at your option) any - * later version. - * - * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt diff --git a/drivers/net/ethernet/freescale/fman/fman_port.h b/drivers/net/ethernet/freescale/fman/fman_port.h index 82f12661a46d..4917fe8f0617 100644 --- a/drivers/net/ethernet/freescale/fman/fman_port.h +++ b/drivers/net/ethernet/freescale/fman/fman_port.h @@ -1,33 +1,6 @@ +/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-or-later */ /* * Copyright 2008 - 2015 Freescale Semiconductor Inc. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of Freescale Semiconductor nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * - * ALTERNATIVELY, this software may be distributed under the terms of the - * GNU General Public License ("GPL") as published by the Free Software - * Foundation, either version 2 of that License or (at your option) any - * later version. - * - * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef __FMAN_PORT_H diff --git a/drivers/net/ethernet/freescale/fman/fman_sp.c b/drivers/net/ethernet/freescale/fman/fman_sp.c index 248f5bcca468..0fac60aa5283 100644 --- a/drivers/net/ethernet/freescale/fman/fman_sp.c +++ b/drivers/net/ethernet/freescale/fman/fman_sp.c @@ -1,33 +1,6 @@ +// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-or-later /* * Copyright 2008 - 2015 Freescale Semiconductor Inc. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of Freescale Semiconductor nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * - * ALTERNATIVELY, this software may be distributed under the terms of the - * GNU General Public License ("GPL") as published by the Free Software - * Foundation, either version 2 of that License or (at your option) any - * later version. - * - * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "fman_sp.h" diff --git a/drivers/net/ethernet/freescale/fman/fman_sp.h b/drivers/net/ethernet/freescale/fman/fman_sp.h index 820b7f63088f..a62dd21c81f1 100644 --- a/drivers/net/ethernet/freescale/fman/fman_sp.h +++ b/drivers/net/ethernet/freescale/fman/fman_sp.h @@ -1,32 +1,6 @@ +/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-or-later */ /* * Copyright 2008 - 2015 Freescale Semiconductor Inc. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of Freescale Semiconductor nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * ALTERNATIVELY, this software may be distributed under the terms of the - * GNU General Public License ("GPL") as published by the Free Software - * Foundation, either version 2 of that License or (at your option) any - * later version. - * - * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef __FM_SP_H diff --git a/drivers/net/ethernet/freescale/fman/fman_tgec.c b/drivers/net/ethernet/freescale/fman/fman_tgec.c index 311c1906e044..5a4be54ad459 100644 --- a/drivers/net/ethernet/freescale/fman/fman_tgec.c +++ b/drivers/net/ethernet/freescale/fman/fman_tgec.c @@ -1,39 +1,13 @@ +// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-or-later /* - * Copyright 2008-2015 Freescale Semiconductor Inc. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of Freescale Semiconductor nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * - * ALTERNATIVELY, this software may be distributed under the terms of the - * GNU General Public License ("GPL") as published by the Free Software - * Foundation, either version 2 of that License or (at your option) any - * later version. - * - * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Copyright 2008 - 2015 Freescale Semiconductor Inc. */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include "fman_tgec.h" #include "fman.h" +#include "mac.h" #include <linux/slab.h> #include <linux/bitrev.h> @@ -206,7 +180,7 @@ struct fman_mac { /* MAC address of device; */ u64 addr; u16 max_speed; - void *dev_id; /* device cookie used by the exception cbs */ + struct mac_device *dev_id; /* device cookie used by the exception cbs */ fman_mac_exception_cb *exception_cb; fman_mac_exception_cb *event_cb; /* pointer to driver's global address hash table */ @@ -419,7 +393,7 @@ static bool is_init_done(struct tgec_cfg *cfg) return false; } -int tgec_enable(struct fman_mac *tgec, enum comm_mode mode) +static int tgec_enable(struct fman_mac *tgec) { struct tgec_regs __iomem *regs = tgec->regs; u32 tmp; @@ -428,34 +402,25 @@ int tgec_enable(struct fman_mac *tgec, enum comm_mode mode) return -EINVAL; tmp = ioread32be(®s->command_config); - if (mode & COMM_MODE_RX) - tmp |= CMD_CFG_RX_EN; - if (mode & COMM_MODE_TX) - tmp |= CMD_CFG_TX_EN; + tmp |= CMD_CFG_RX_EN | CMD_CFG_TX_EN; iowrite32be(tmp, ®s->command_config); return 0; } -int tgec_disable(struct fman_mac *tgec, enum comm_mode mode) +static void tgec_disable(struct fman_mac *tgec) { struct tgec_regs __iomem *regs = tgec->regs; u32 tmp; - if (!is_init_done(tgec->cfg)) - return -EINVAL; + WARN_ON_ONCE(!is_init_done(tgec->cfg)); tmp = ioread32be(®s->command_config); - if (mode & COMM_MODE_RX) - tmp &= ~CMD_CFG_RX_EN; - if (mode & COMM_MODE_TX) - tmp &= ~CMD_CFG_TX_EN; + tmp &= ~(CMD_CFG_RX_EN | CMD_CFG_TX_EN); iowrite32be(tmp, ®s->command_config); - - return 0; } -int tgec_set_promiscuous(struct fman_mac *tgec, bool new_val) +static int tgec_set_promiscuous(struct fman_mac *tgec, bool new_val) { struct tgec_regs __iomem *regs = tgec->regs; u32 tmp; @@ -473,18 +438,9 @@ int tgec_set_promiscuous(struct fman_mac *tgec, bool new_val) return 0; } -int tgec_cfg_max_frame_len(struct fman_mac *tgec, u16 new_val) -{ - if (is_init_done(tgec->cfg)) - return -EINVAL; - - tgec->cfg->max_frame_length = new_val; - - return 0; -} - -int tgec_set_tx_pause_frames(struct fman_mac *tgec, u8 __maybe_unused priority, - u16 pause_time, u16 __maybe_unused thresh_time) +static int tgec_set_tx_pause_frames(struct fman_mac *tgec, + u8 __maybe_unused priority, u16 pause_time, + u16 __maybe_unused thresh_time) { struct tgec_regs __iomem *regs = tgec->regs; @@ -496,7 +452,7 @@ int tgec_set_tx_pause_frames(struct fman_mac *tgec, u8 __maybe_unused priority, return 0; } -int tgec_accept_rx_pause_frames(struct fman_mac *tgec, bool en) +static int tgec_accept_rx_pause_frames(struct fman_mac *tgec, bool en) { struct tgec_regs __iomem *regs = tgec->regs; u32 tmp; @@ -514,7 +470,8 @@ int tgec_accept_rx_pause_frames(struct fman_mac *tgec, bool en) return 0; } -int tgec_modify_mac_address(struct fman_mac *tgec, const enet_addr_t *p_enet_addr) +static int tgec_modify_mac_address(struct fman_mac *tgec, + const enet_addr_t *p_enet_addr) { if (!is_init_done(tgec->cfg)) return -EINVAL; @@ -525,7 +482,8 @@ int tgec_modify_mac_address(struct fman_mac *tgec, const enet_addr_t *p_enet_add return 0; } -int tgec_add_hash_mac_address(struct fman_mac *tgec, enet_addr_t *eth_addr) +static int tgec_add_hash_mac_address(struct fman_mac *tgec, + enet_addr_t *eth_addr) { struct tgec_regs __iomem *regs = tgec->regs; struct eth_hash_entry *hash_entry; @@ -562,7 +520,7 @@ int tgec_add_hash_mac_address(struct fman_mac *tgec, enet_addr_t *eth_addr) return 0; } -int tgec_set_allmulti(struct fman_mac *tgec, bool enable) +static int tgec_set_allmulti(struct fman_mac *tgec, bool enable) { u32 entry; struct tgec_regs __iomem *regs = tgec->regs; @@ -585,7 +543,7 @@ int tgec_set_allmulti(struct fman_mac *tgec, bool enable) return 0; } -int tgec_set_tstamp(struct fman_mac *tgec, bool enable) +static int tgec_set_tstamp(struct fman_mac *tgec, bool enable) { struct tgec_regs __iomem *regs = tgec->regs; u32 tmp; @@ -605,7 +563,8 @@ int tgec_set_tstamp(struct fman_mac *tgec, bool enable) return 0; } -int tgec_del_hash_mac_address(struct fman_mac *tgec, enet_addr_t *eth_addr) +static int tgec_del_hash_mac_address(struct fman_mac *tgec, + enet_addr_t *eth_addr) { struct tgec_regs __iomem *regs = tgec->regs; struct eth_hash_entry *hash_entry = NULL; @@ -642,20 +601,15 @@ int tgec_del_hash_mac_address(struct fman_mac *tgec, enet_addr_t *eth_addr) return 0; } -int tgec_get_version(struct fman_mac *tgec, u32 *mac_version) +static void tgec_adjust_link(struct mac_device *mac_dev) { - struct tgec_regs __iomem *regs = tgec->regs; - - if (!is_init_done(tgec->cfg)) - return -EINVAL; + struct phy_device *phy_dev = mac_dev->phy_dev; - *mac_version = ioread32be(®s->tgec_id); - - return 0; + mac_dev->update_speed(mac_dev, phy_dev->speed); } -int tgec_set_exception(struct fman_mac *tgec, - enum fman_mac_exceptions exception, bool enable) +static int tgec_set_exception(struct fman_mac *tgec, + enum fman_mac_exceptions exception, bool enable) { struct tgec_regs __iomem *regs = tgec->regs; u32 bit_mask = 0; @@ -681,7 +635,7 @@ int tgec_set_exception(struct fman_mac *tgec, return 0; } -int tgec_init(struct fman_mac *tgec) +static int tgec_init(struct fman_mac *tgec) { struct tgec_cfg *cfg; enet_addr_t eth_addr; @@ -764,7 +718,7 @@ int tgec_init(struct fman_mac *tgec) return 0; } -int tgec_free(struct fman_mac *tgec) +static int tgec_free(struct fman_mac *tgec) { free_init_resources(tgec); @@ -774,13 +728,12 @@ int tgec_free(struct fman_mac *tgec) return 0; } -struct fman_mac *tgec_config(struct fman_mac_params *params) +static struct fman_mac *tgec_config(struct mac_device *mac_dev, + struct fman_mac_params *params) { struct fman_mac *tgec; struct tgec_cfg *cfg; - void __iomem *base_addr; - base_addr = params->base_addr; /* allocate memory for the UCC GETH data structure. */ tgec = kzalloc(sizeof(*tgec), GFP_KERNEL); if (!tgec) @@ -798,8 +751,8 @@ struct fman_mac *tgec_config(struct fman_mac_params *params) set_dflts(cfg); - tgec->regs = base_addr; - tgec->addr = ENET_ADDR_TO_UINT64(params->addr); + tgec->regs = mac_dev->vaddr; + tgec->addr = ENET_ADDR_TO_UINT64(mac_dev->addr); tgec->max_speed = params->max_speed; tgec->mac_id = params->mac_id; tgec->exceptions = (TGEC_IMASK_MDIO_SCAN_EVENT | @@ -819,7 +772,7 @@ struct fman_mac *tgec_config(struct fman_mac_params *params) TGEC_IMASK_RX_ALIGN_ER); tgec->exception_cb = params->exception_cb; tgec->event_cb = params->event_cb; - tgec->dev_id = params->dev_id; + tgec->dev_id = mac_dev; tgec->fm = params->fm; /* Save FMan revision */ @@ -827,3 +780,52 @@ struct fman_mac *tgec_config(struct fman_mac_params *params) return tgec; } + +int tgec_initialization(struct mac_device *mac_dev, + struct device_node *mac_node, + struct fman_mac_params *params) +{ + int err; + struct fman_mac *tgec; + + mac_dev->set_promisc = tgec_set_promiscuous; + mac_dev->change_addr = tgec_modify_mac_address; + mac_dev->add_hash_mac_addr = tgec_add_hash_mac_address; + mac_dev->remove_hash_mac_addr = tgec_del_hash_mac_address; + mac_dev->set_tx_pause = tgec_set_tx_pause_frames; + mac_dev->set_rx_pause = tgec_accept_rx_pause_frames; + mac_dev->set_exception = tgec_set_exception; + mac_dev->set_allmulti = tgec_set_allmulti; + mac_dev->set_tstamp = tgec_set_tstamp; + mac_dev->set_multi = fman_set_multi; + mac_dev->adjust_link = tgec_adjust_link; + mac_dev->enable = tgec_enable; + mac_dev->disable = tgec_disable; + + mac_dev->fman_mac = tgec_config(mac_dev, params); + if (!mac_dev->fman_mac) { + err = -EINVAL; + goto _return; + } + + tgec = mac_dev->fman_mac; + tgec->cfg->max_frame_length = fman_get_max_frm(); + err = tgec_init(tgec); + if (err < 0) + goto _return_fm_mac_free; + + /* For 10G MAC, disable Tx ECC exception */ + err = tgec_set_exception(tgec, FM_MAC_EX_10G_TX_ECC_ER, false); + if (err < 0) + goto _return_fm_mac_free; + + pr_info("FMan XGEC version: 0x%08x\n", + ioread32be(&tgec->regs->tgec_id)); + goto _return; + +_return_fm_mac_free: + tgec_free(mac_dev->fman_mac); + +_return: + return err; +} diff --git a/drivers/net/ethernet/freescale/fman/fman_tgec.h b/drivers/net/ethernet/freescale/fman/fman_tgec.h index b28b20b26148..768b8d165e05 100644 --- a/drivers/net/ethernet/freescale/fman/fman_tgec.h +++ b/drivers/net/ethernet/freescale/fman/fman_tgec.h @@ -1,33 +1,6 @@ +/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-or-later */ /* - * Copyright 2008-2015 Freescale Semiconductor Inc. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of Freescale Semiconductor nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * - * ALTERNATIVELY, this software may be distributed under the terms of the - * GNU General Public License ("GPL") as published by the Free Software - * Foundation, either version 2 of that License or (at your option) any - * later version. - * - * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Copyright 2008 - 2015 Freescale Semiconductor Inc. */ #ifndef __TGEC_H @@ -35,23 +8,10 @@ #include "fman_mac.h" -struct fman_mac *tgec_config(struct fman_mac_params *params); -int tgec_set_promiscuous(struct fman_mac *tgec, bool new_val); -int tgec_modify_mac_address(struct fman_mac *tgec, const enet_addr_t *enet_addr); -int tgec_cfg_max_frame_len(struct fman_mac *tgec, u16 new_val); -int tgec_enable(struct fman_mac *tgec, enum comm_mode mode); -int tgec_disable(struct fman_mac *tgec, enum comm_mode mode); -int tgec_init(struct fman_mac *tgec); -int tgec_free(struct fman_mac *tgec); -int tgec_accept_rx_pause_frames(struct fman_mac *tgec, bool en); -int tgec_set_tx_pause_frames(struct fman_mac *tgec, u8 priority, - u16 pause_time, u16 thresh_time); -int tgec_set_exception(struct fman_mac *tgec, - enum fman_mac_exceptions exception, bool enable); -int tgec_add_hash_mac_address(struct fman_mac *tgec, enet_addr_t *eth_addr); -int tgec_del_hash_mac_address(struct fman_mac *tgec, enet_addr_t *eth_addr); -int tgec_get_version(struct fman_mac *tgec, u32 *mac_version); -int tgec_set_allmulti(struct fman_mac *tgec, bool enable); -int tgec_set_tstamp(struct fman_mac *tgec, bool enable); +struct mac_device; + +int tgec_initialization(struct mac_device *mac_dev, + struct device_node *mac_node, + struct fman_mac_params *params); #endif /* __TGEC_H */ diff --git a/drivers/net/ethernet/freescale/fman/mac.c b/drivers/net/ethernet/freescale/fman/mac.c index 39ae965cd4f6..7b7526fd7da3 100644 --- a/drivers/net/ethernet/freescale/fman/mac.c +++ b/drivers/net/ethernet/freescale/fman/mac.c @@ -1,32 +1,6 @@ -/* Copyright 2008-2015 Freescale Semiconductor, Inc. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of Freescale Semiconductor nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * - * ALTERNATIVELY, this software may be distributed under the terms of the - * GNU General Public License ("GPL") as published by the Free Software - * Foundation, either version 2 of that License or (at your option) any - * later version. - * - * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-or-later +/* + * Copyright 2008 - 2015 Freescale Semiconductor Inc. */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt @@ -54,20 +28,12 @@ MODULE_LICENSE("Dual BSD/GPL"); MODULE_DESCRIPTION("FSL FMan MAC API based driver"); struct mac_priv_s { - struct device *dev; - void __iomem *vaddr; u8 cell_index; struct fman *fman; - struct device_node *internal_phy_node; /* List of multicast addresses */ struct list_head mc_addr_list; struct platform_device *eth_dev; - struct fixed_phy_status *fixed_link; u16 speed; - u16 max_speed; - - int (*enable)(struct fman_mac *mac_dev, enum comm_mode mode); - int (*disable)(struct fman_mac *mac_dev, enum comm_mode mode); }; struct mac_address { @@ -75,222 +41,21 @@ struct mac_address { struct list_head list; }; -static void mac_exception(void *handle, enum fman_mac_exceptions ex) +static void mac_exception(struct mac_device *mac_dev, + enum fman_mac_exceptions ex) { - struct mac_device *mac_dev; - struct mac_priv_s *priv; - - mac_dev = handle; - priv = mac_dev->priv; - if (ex == FM_MAC_EX_10G_RX_FIFO_OVFL) { /* don't flag RX FIFO after the first */ mac_dev->set_exception(mac_dev->fman_mac, FM_MAC_EX_10G_RX_FIFO_OVFL, false); - dev_err(priv->dev, "10G MAC got RX FIFO Error = %x\n", ex); + dev_err(mac_dev->dev, "10G MAC got RX FIFO Error = %x\n", ex); } - dev_dbg(priv->dev, "%s:%s() -> %d\n", KBUILD_BASENAME ".c", + dev_dbg(mac_dev->dev, "%s:%s() -> %d\n", KBUILD_BASENAME ".c", __func__, ex); } -static int set_fman_mac_params(struct mac_device *mac_dev, - struct fman_mac_params *params) -{ - struct mac_priv_s *priv = mac_dev->priv; - - params->base_addr = (typeof(params->base_addr)) - devm_ioremap(priv->dev, mac_dev->res->start, - resource_size(mac_dev->res)); - if (!params->base_addr) - return -ENOMEM; - - memcpy(¶ms->addr, mac_dev->addr, sizeof(mac_dev->addr)); - params->max_speed = priv->max_speed; - params->phy_if = mac_dev->phy_if; - params->basex_if = false; - params->mac_id = priv->cell_index; - params->fm = (void *)priv->fman; - params->exception_cb = mac_exception; - params->event_cb = mac_exception; - params->dev_id = mac_dev; - params->internal_phy_node = priv->internal_phy_node; - - return 0; -} - -static int tgec_initialization(struct mac_device *mac_dev) -{ - int err; - struct mac_priv_s *priv; - struct fman_mac_params params; - u32 version; - - priv = mac_dev->priv; - - err = set_fman_mac_params(mac_dev, ¶ms); - if (err) - goto _return; - - mac_dev->fman_mac = tgec_config(¶ms); - if (!mac_dev->fman_mac) { - err = -EINVAL; - goto _return; - } - - err = tgec_cfg_max_frame_len(mac_dev->fman_mac, fman_get_max_frm()); - if (err < 0) - goto _return_fm_mac_free; - - err = tgec_init(mac_dev->fman_mac); - if (err < 0) - goto _return_fm_mac_free; - - /* For 10G MAC, disable Tx ECC exception */ - err = mac_dev->set_exception(mac_dev->fman_mac, - FM_MAC_EX_10G_TX_ECC_ER, false); - if (err < 0) - goto _return_fm_mac_free; - - err = tgec_get_version(mac_dev->fman_mac, &version); - if (err < 0) - goto _return_fm_mac_free; - - dev_info(priv->dev, "FMan XGEC version: 0x%08x\n", version); - - goto _return; - -_return_fm_mac_free: - tgec_free(mac_dev->fman_mac); - -_return: - return err; -} - -static int dtsec_initialization(struct mac_device *mac_dev) -{ - int err; - struct mac_priv_s *priv; - struct fman_mac_params params; - u32 version; - - priv = mac_dev->priv; - - err = set_fman_mac_params(mac_dev, ¶ms); - if (err) - goto _return; - - mac_dev->fman_mac = dtsec_config(¶ms); - if (!mac_dev->fman_mac) { - err = -EINVAL; - goto _return; - } - - err = dtsec_cfg_max_frame_len(mac_dev->fman_mac, fman_get_max_frm()); - if (err < 0) - goto _return_fm_mac_free; - - err = dtsec_cfg_pad_and_crc(mac_dev->fman_mac, true); - if (err < 0) - goto _return_fm_mac_free; - - err = dtsec_init(mac_dev->fman_mac); - if (err < 0) - goto _return_fm_mac_free; - - /* For 1G MAC, disable by default the MIB counters overflow interrupt */ - err = mac_dev->set_exception(mac_dev->fman_mac, - FM_MAC_EX_1G_RX_MIB_CNT_OVFL, false); - if (err < 0) - goto _return_fm_mac_free; - - err = dtsec_get_version(mac_dev->fman_mac, &version); - if (err < 0) - goto _return_fm_mac_free; - - dev_info(priv->dev, "FMan dTSEC version: 0x%08x\n", version); - - goto _return; - -_return_fm_mac_free: - dtsec_free(mac_dev->fman_mac); - -_return: - return err; -} - -static int memac_initialization(struct mac_device *mac_dev) -{ - int err; - struct mac_priv_s *priv; - struct fman_mac_params params; - - priv = mac_dev->priv; - - err = set_fman_mac_params(mac_dev, ¶ms); - if (err) - goto _return; - - if (priv->max_speed == SPEED_10000) - params.phy_if = PHY_INTERFACE_MODE_XGMII; - - mac_dev->fman_mac = memac_config(¶ms); - if (!mac_dev->fman_mac) { - err = -EINVAL; - goto _return; - } - - err = memac_cfg_max_frame_len(mac_dev->fman_mac, fman_get_max_frm()); - if (err < 0) - goto _return_fm_mac_free; - - err = memac_cfg_reset_on_init(mac_dev->fman_mac, true); - if (err < 0) - goto _return_fm_mac_free; - - err = memac_cfg_fixed_link(mac_dev->fman_mac, priv->fixed_link); - if (err < 0) - goto _return_fm_mac_free; - - err = memac_init(mac_dev->fman_mac); - if (err < 0) - goto _return_fm_mac_free; - - dev_info(priv->dev, "FMan MEMAC\n"); - - goto _return; - -_return_fm_mac_free: - memac_free(mac_dev->fman_mac); - -_return: - return err; -} - -static int start(struct mac_device *mac_dev) -{ - int err; - struct phy_device *phy_dev = mac_dev->phy_dev; - struct mac_priv_s *priv = mac_dev->priv; - - err = priv->enable(mac_dev->fman_mac, COMM_MODE_RX_AND_TX); - if (!err && phy_dev) - phy_start(phy_dev); - - return err; -} - -static int stop(struct mac_device *mac_dev) -{ - struct mac_priv_s *priv = mac_dev->priv; - - if (mac_dev->phy_dev) - phy_stop(mac_dev->phy_dev); - - return priv->disable(mac_dev->fman_mac, COMM_MODE_RX_AND_TX); -} - -static int set_multi(struct net_device *net_dev, struct mac_device *mac_dev) +int fman_set_multi(struct net_device *net_dev, struct mac_device *mac_dev) { struct mac_priv_s *priv; struct mac_address *old_addr, *tmp; @@ -424,109 +189,6 @@ void fman_get_pause_cfg(struct mac_device *mac_dev, bool *rx_pause, } EXPORT_SYMBOL(fman_get_pause_cfg); -static void adjust_link_void(struct mac_device *mac_dev) -{ -} - -static void adjust_link_dtsec(struct mac_device *mac_dev) -{ - struct phy_device *phy_dev = mac_dev->phy_dev; - struct fman_mac *fman_mac; - bool rx_pause, tx_pause; - int err; - - fman_mac = mac_dev->fman_mac; - if (!phy_dev->link) { - dtsec_restart_autoneg(fman_mac); - - return; - } - - dtsec_adjust_link(fman_mac, phy_dev->speed); - fman_get_pause_cfg(mac_dev, &rx_pause, &tx_pause); - err = fman_set_mac_active_pause(mac_dev, rx_pause, tx_pause); - if (err < 0) - dev_err(mac_dev->priv->dev, "fman_set_mac_active_pause() = %d\n", - err); -} - -static void adjust_link_memac(struct mac_device *mac_dev) -{ - struct phy_device *phy_dev = mac_dev->phy_dev; - struct fman_mac *fman_mac; - bool rx_pause, tx_pause; - int err; - - fman_mac = mac_dev->fman_mac; - memac_adjust_link(fman_mac, phy_dev->speed); - - fman_get_pause_cfg(mac_dev, &rx_pause, &tx_pause); - err = fman_set_mac_active_pause(mac_dev, rx_pause, tx_pause); - if (err < 0) - dev_err(mac_dev->priv->dev, "fman_set_mac_active_pause() = %d\n", - err); -} - -static void setup_dtsec(struct mac_device *mac_dev) -{ - mac_dev->init = dtsec_initialization; - mac_dev->set_promisc = dtsec_set_promiscuous; - mac_dev->change_addr = dtsec_modify_mac_address; - mac_dev->add_hash_mac_addr = dtsec_add_hash_mac_address; - mac_dev->remove_hash_mac_addr = dtsec_del_hash_mac_address; - mac_dev->set_tx_pause = dtsec_set_tx_pause_frames; - mac_dev->set_rx_pause = dtsec_accept_rx_pause_frames; - mac_dev->set_exception = dtsec_set_exception; - mac_dev->set_allmulti = dtsec_set_allmulti; - mac_dev->set_tstamp = dtsec_set_tstamp; - mac_dev->set_multi = set_multi; - mac_dev->start = start; - mac_dev->stop = stop; - mac_dev->adjust_link = adjust_link_dtsec; - mac_dev->priv->enable = dtsec_enable; - mac_dev->priv->disable = dtsec_disable; -} - -static void setup_tgec(struct mac_device *mac_dev) -{ - mac_dev->init = tgec_initialization; - mac_dev->set_promisc = tgec_set_promiscuous; - mac_dev->change_addr = tgec_modify_mac_address; - mac_dev->add_hash_mac_addr = tgec_add_hash_mac_address; - mac_dev->remove_hash_mac_addr = tgec_del_hash_mac_address; - mac_dev->set_tx_pause = tgec_set_tx_pause_frames; - mac_dev->set_rx_pause = tgec_accept_rx_pause_frames; - mac_dev->set_exception = tgec_set_exception; - mac_dev->set_allmulti = tgec_set_allmulti; - mac_dev->set_tstamp = tgec_set_tstamp; - mac_dev->set_multi = set_multi; - mac_dev->start = start; - mac_dev->stop = stop; - mac_dev->adjust_link = adjust_link_void; - mac_dev->priv->enable = tgec_enable; - mac_dev->priv->disable = tgec_disable; -} - -static void setup_memac(struct mac_device *mac_dev) -{ - mac_dev->init = memac_initialization; - mac_dev->set_promisc = memac_set_promiscuous; - mac_dev->change_addr = memac_modify_mac_address; - mac_dev->add_hash_mac_addr = memac_add_hash_mac_address; - mac_dev->remove_hash_mac_addr = memac_del_hash_mac_address; - mac_dev->set_tx_pause = memac_set_tx_pause_frames; - mac_dev->set_rx_pause = memac_accept_rx_pause_frames; - mac_dev->set_exception = memac_set_exception; - mac_dev->set_allmulti = memac_set_allmulti; - mac_dev->set_tstamp = memac_set_tstamp; - mac_dev->set_multi = set_multi; - mac_dev->start = start; - mac_dev->stop = stop; - mac_dev->adjust_link = adjust_link_memac; - mac_dev->priv->enable = memac_enable; - mac_dev->priv->disable = memac_disable; -} - #define DTSEC_SUPPORTED \ (SUPPORTED_10baseT_Half \ | SUPPORTED_10baseT_Full \ @@ -577,7 +239,7 @@ static struct platform_device *dpaa_eth_add_device(int fman_id, goto no_mem; } - pdev->dev.parent = priv->dev; + pdev->dev.parent = mac_dev->dev; ret = platform_device_add_data(pdev, &data, sizeof(data)); if (ret) @@ -601,9 +263,9 @@ no_mem: } static const struct of_device_id mac_match[] = { - { .compatible = "fsl,fman-dtsec" }, - { .compatible = "fsl,fman-xgec" }, - { .compatible = "fsl,fman-memac" }, + { .compatible = "fsl,fman-dtsec", .data = dtsec_initialization }, + { .compatible = "fsl,fman-xgec", .data = tgec_initialization }, + { .compatible = "fsl,fman-memac", .data = memac_initialization }, {} }; MODULE_DEVICE_TABLE(of, mac_match); @@ -611,50 +273,33 @@ MODULE_DEVICE_TABLE(of, mac_match); static int mac_probe(struct platform_device *_of_dev) { int err, i, nph; + int (*init)(struct mac_device *mac_dev, struct device_node *mac_node, + struct fman_mac_params *params); struct device *dev; struct device_node *mac_node, *dev_node; struct mac_device *mac_dev; struct platform_device *of_dev; - struct resource res; + struct resource *res; struct mac_priv_s *priv; + struct fman_mac_params params; u32 val; u8 fman_id; phy_interface_t phy_if; dev = &_of_dev->dev; mac_node = dev->of_node; + init = of_device_get_match_data(dev); mac_dev = devm_kzalloc(dev, sizeof(*mac_dev), GFP_KERNEL); - if (!mac_dev) { - err = -ENOMEM; - goto _return; - } + if (!mac_dev) + return -ENOMEM; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); - if (!priv) { - err = -ENOMEM; - goto _return; - } + if (!priv) + return -ENOMEM; /* Save private information */ mac_dev->priv = priv; - priv->dev = dev; - - if (of_device_is_compatible(mac_node, "fsl,fman-dtsec")) { - setup_dtsec(mac_dev); - priv->internal_phy_node = of_parse_phandle(mac_node, - "tbi-handle", 0); - } else if (of_device_is_compatible(mac_node, "fsl,fman-xgec")) { - setup_tgec(mac_dev); - } else if (of_device_is_compatible(mac_node, "fsl,fman-memac")) { - setup_memac(mac_dev); - priv->internal_phy_node = of_parse_phandle(mac_node, - "pcsphy-handle", 0); - } else { - dev_err(dev, "MAC node (%pOF) contains unsupported MAC\n", - mac_node); - err = -EINVAL; - goto _return; - } + mac_dev->dev = dev; INIT_LIST_HEAD(&priv->mc_addr_list); @@ -663,8 +308,7 @@ static int mac_probe(struct platform_device *_of_dev) if (!dev_node) { dev_err(dev, "of_get_parent(%pOF) failed\n", mac_node); - err = -EINVAL; - goto _return_of_get_parent; + return -EINVAL; } of_dev = of_find_device_by_node(dev_node); @@ -694,42 +338,33 @@ static int mac_probe(struct platform_device *_of_dev) of_node_put(dev_node); /* Get the address of the memory mapped registers */ - err = of_address_to_resource(mac_node, 0, &res); - if (err < 0) { - dev_err(dev, "of_address_to_resource(%pOF) = %d\n", - mac_node, err); - goto _return_of_get_parent; + res = platform_get_mem_or_io(_of_dev, 0); + if (!res) { + dev_err(dev, "could not get registers\n"); + return -EINVAL; } - mac_dev->res = __devm_request_region(dev, - fman_get_mem_region(priv->fman), - res.start, resource_size(&res), - "mac"); - if (!mac_dev->res) { - dev_err(dev, "__devm_request_mem_region(mac) failed\n"); - err = -EBUSY; - goto _return_of_get_parent; + err = devm_request_resource(dev, fman_get_mem_region(priv->fman), res); + if (err) { + dev_err_probe(dev, err, "could not request resource\n"); + return err; } - priv->vaddr = devm_ioremap(dev, mac_dev->res->start, - resource_size(mac_dev->res)); - if (!priv->vaddr) { + mac_dev->vaddr = devm_ioremap(dev, res->start, resource_size(res)); + if (!mac_dev->vaddr) { dev_err(dev, "devm_ioremap() failed\n"); - err = -EIO; - goto _return_of_get_parent; + return -EIO; } + mac_dev->vaddr_end = mac_dev->vaddr + resource_size(res); - if (!of_device_is_available(mac_node)) { - err = -ENODEV; - goto _return_of_get_parent; - } + if (!of_device_is_available(mac_node)) + return -ENODEV; /* Get the cell-index */ err = of_property_read_u32(mac_node, "cell-index", &val); if (err) { dev_err(dev, "failed to read cell-index for %pOF\n", mac_node); - err = -EINVAL; - goto _return_of_get_parent; + return -EINVAL; } priv->cell_index = (u8)val; @@ -743,15 +378,13 @@ static int mac_probe(struct platform_device *_of_dev) if (unlikely(nph < 0)) { dev_err(dev, "of_count_phandle_with_args(%pOF, fsl,fman-ports) failed\n", mac_node); - err = nph; - goto _return_of_get_parent; + return nph; } if (nph != ARRAY_SIZE(mac_dev->port)) { dev_err(dev, "Not supported number of fman-ports handles of mac node %pOF from device tree\n", mac_node); - err = -EINVAL; - goto _return_of_get_parent; + return -EINVAL; } for (i = 0; i < ARRAY_SIZE(mac_dev->port); i++) { @@ -760,8 +393,7 @@ static int mac_probe(struct platform_device *_of_dev) if (!dev_node) { dev_err(dev, "of_parse_phandle(%pOF, fsl,fman-ports) failed\n", mac_node); - err = -EINVAL; - goto _return_of_node_put; + return -EINVAL; } of_dev = of_find_device_by_node(dev_node); @@ -793,7 +425,7 @@ static int mac_probe(struct platform_device *_of_dev) mac_dev->phy_if = phy_if; priv->speed = phy2speed[mac_dev->phy_if]; - priv->max_speed = priv->speed; + params.max_speed = priv->speed; mac_dev->if_support = DTSEC_SUPPORTED; /* We don't support half-duplex in SGMII mode */ if (mac_dev->phy_if == PHY_INTERFACE_MODE_SGMII) @@ -801,7 +433,7 @@ static int mac_probe(struct platform_device *_of_dev) SUPPORTED_100baseT_Half); /* Gigabit support (no half-duplex) */ - if (priv->max_speed == 1000) + if (params.max_speed == 1000) mac_dev->if_support |= SUPPORTED_1000baseT_Full; /* The 10G interface only supports one mode */ @@ -810,42 +442,18 @@ static int mac_probe(struct platform_device *_of_dev) /* Get the rest of the PHY information */ mac_dev->phy_node = of_parse_phandle(mac_node, "phy-handle", 0); - if (!mac_dev->phy_node && of_phy_is_fixed_link(mac_node)) { - struct phy_device *phy; - - err = of_phy_register_fixed_link(mac_node); - if (err) - goto _return_of_get_parent; - - priv->fixed_link = kzalloc(sizeof(*priv->fixed_link), - GFP_KERNEL); - if (!priv->fixed_link) { - err = -ENOMEM; - goto _return_of_get_parent; - } - - mac_dev->phy_node = of_node_get(mac_node); - phy = of_phy_find_device(mac_dev->phy_node); - if (!phy) { - err = -EINVAL; - of_node_put(mac_dev->phy_node); - goto _return_of_get_parent; - } - priv->fixed_link->link = phy->link; - priv->fixed_link->speed = phy->speed; - priv->fixed_link->duplex = phy->duplex; - priv->fixed_link->pause = phy->pause; - priv->fixed_link->asym_pause = phy->asym_pause; + params.basex_if = false; + params.mac_id = priv->cell_index; + params.fm = (void *)priv->fman; + params.exception_cb = mac_exception; + params.event_cb = mac_exception; - put_device(&phy->mdio.dev); - } - - err = mac_dev->init(mac_dev); + err = init(mac_dev, mac_node, ¶ms); if (err < 0) { dev_err(dev, "mac_dev->init() = %d\n", err); of_node_put(mac_dev->phy_node); - goto _return_of_get_parent; + return err; } /* pause frame autonegotiation enabled */ @@ -872,13 +480,10 @@ static int mac_probe(struct platform_device *_of_dev) priv->eth_dev = NULL; } - goto _return; + return err; _return_of_node_put: of_node_put(dev_node); -_return_of_get_parent: - kfree(priv->fixed_link); -_return: return err; } diff --git a/drivers/net/ethernet/freescale/fman/mac.h b/drivers/net/ethernet/freescale/fman/mac.h index daa285a9b8b2..b95d384271bd 100644 --- a/drivers/net/ethernet/freescale/fman/mac.h +++ b/drivers/net/ethernet/freescale/fman/mac.h @@ -1,32 +1,6 @@ -/* Copyright 2008-2015 Freescale Semiconductor, Inc. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of Freescale Semiconductor nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * - * ALTERNATIVELY, this software may be distributed under the terms of the - * GNU General Public License ("GPL") as published by the Free Software - * Foundation, either version 2 of that License or (at your option) any - * later version. - * - * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-or-later */ +/* + * Copyright 2008 - 2015 Freescale Semiconductor Inc. */ #ifndef __MAC_H @@ -45,13 +19,16 @@ struct fman_mac; struct mac_priv_s; struct mac_device { - struct resource *res; + void __iomem *vaddr; + void __iomem *vaddr_end; + struct device *dev; u8 addr[ETH_ALEN]; struct fman_port *port[2]; u32 if_support; struct phy_device *phy_dev; phy_interface_t phy_if; struct device_node *phy_node; + struct net_device *net_dev; bool autoneg_pause; bool rx_pause_req; @@ -61,9 +38,8 @@ struct mac_device { bool promisc; bool allmulti; - int (*init)(struct mac_device *mac_dev); - int (*start)(struct mac_device *mac_dev); - int (*stop)(struct mac_device *mac_dev); + int (*enable)(struct fman_mac *mac_dev); + void (*disable)(struct fman_mac *mac_dev); void (*adjust_link)(struct mac_device *mac_dev); int (*set_promisc)(struct fman_mac *mac_dev, bool enable); int (*change_addr)(struct fman_mac *mac_dev, const enet_addr_t *enet_addr); @@ -81,6 +57,8 @@ struct mac_device { int (*remove_hash_mac_addr)(struct fman_mac *mac_dev, enet_addr_t *eth_addr); + void (*update_speed)(struct mac_device *mac_dev, int speed); + struct fman_mac *fman_mac; struct mac_priv_s *priv; }; @@ -97,5 +75,6 @@ int fman_set_mac_active_pause(struct mac_device *mac_dev, bool rx, bool tx); void fman_get_pause_cfg(struct mac_device *mac_dev, bool *rx_pause, bool *tx_pause); +int fman_set_multi(struct net_device *net_dev, struct mac_device *mac_dev); #endif /* __MAC_H */ diff --git a/drivers/net/ethernet/freescale/fs_enet/fs_enet-main.c b/drivers/net/ethernet/freescale/fs_enet/fs_enet-main.c index b3dae17e067e..8844a9a04fcf 100644 --- a/drivers/net/ethernet/freescale/fs_enet/fs_enet-main.c +++ b/drivers/net/ethernet/freescale/fs_enet/fs_enet-main.c @@ -791,7 +791,7 @@ static int fs_enet_close(struct net_device *dev) static void fs_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { - strlcpy(info->driver, DRV_MODULE_NAME, sizeof(info->driver)); + strscpy(info->driver, DRV_MODULE_NAME, sizeof(info->driver)); } static int fs_get_regs_len(struct net_device *dev) @@ -883,9 +883,6 @@ static const struct ethtool_ops fs_ethtool_ops = { .set_tunable = fs_set_tunable, }; -extern int fs_mii_connect(struct net_device *dev); -extern void fs_mii_disconnect(struct net_device *dev); - /**************************************************************************************/ #ifdef CONFIG_FS_ENET_HAS_FEC diff --git a/drivers/net/ethernet/freescale/fs_enet/mac-fec.c b/drivers/net/ethernet/freescale/fs_enet/mac-fec.c index 99fe2c210d0f..61f4b6e50d29 100644 --- a/drivers/net/ethernet/freescale/fs_enet/mac-fec.c +++ b/drivers/net/ethernet/freescale/fs_enet/mac-fec.c @@ -98,7 +98,7 @@ static int do_pd_setup(struct fs_enet_private *fep) return -EINVAL; fep->fec.fecp = of_iomap(ofdev->dev.of_node, 0); - if (!fep->fcc.fccp) + if (!fep->fec.fecp) return -EINVAL; return 0; diff --git a/drivers/net/ethernet/freescale/gianfar.c b/drivers/net/ethernet/freescale/gianfar.c index e7bf1524b68e..b2def295523a 100644 --- a/drivers/net/ethernet/freescale/gianfar.c +++ b/drivers/net/ethernet/freescale/gianfar.c @@ -3233,7 +3233,7 @@ static int gfar_probe(struct platform_device *ofdev) /* Register for napi ...We are registering NAPI for each grp */ for (i = 0; i < priv->num_grps; i++) { netif_napi_add(dev, &priv->gfargrp[i].napi_rx, - gfar_poll_rx_sq, NAPI_POLL_WEIGHT); + gfar_poll_rx_sq); netif_napi_add_tx_weight(dev, &priv->gfargrp[i].napi_tx, gfar_poll_tx_sq, 2); } diff --git a/drivers/net/ethernet/freescale/gianfar_ethtool.c b/drivers/net/ethernet/freescale/gianfar_ethtool.c index 81fb68730138..b2b0d3c26fcc 100644 --- a/drivers/net/ethernet/freescale/gianfar_ethtool.c +++ b/drivers/net/ethernet/freescale/gianfar_ethtool.c @@ -163,7 +163,7 @@ static int gfar_sset_count(struct net_device *dev, int sset) static void gfar_gdrvinfo(struct net_device *dev, struct ethtool_drvinfo *drvinfo) { - strlcpy(drvinfo->driver, DRV_NAME, sizeof(drvinfo->driver)); + strscpy(drvinfo->driver, DRV_NAME, sizeof(drvinfo->driver)); } /* Return the length of the register structure */ diff --git a/drivers/net/ethernet/freescale/ucc_geth.c b/drivers/net/ethernet/freescale/ucc_geth.c index 823221c912ab..7a4cb4f07c32 100644 --- a/drivers/net/ethernet/freescale/ucc_geth.c +++ b/drivers/net/ethernet/freescale/ucc_geth.c @@ -3712,7 +3712,7 @@ static int ucc_geth_probe(struct platform_device* ofdev) dev->netdev_ops = &ucc_geth_netdev_ops; dev->watchdog_timeo = TX_TIMEOUT; INIT_WORK(&ugeth->timeout_work, ucc_geth_timeout_work); - netif_napi_add(dev, &ugeth->napi, ucc_geth_poll, 64); + netif_napi_add(dev, &ugeth->napi, ucc_geth_poll); dev->mtu = 1500; dev->max_mtu = 1518; diff --git a/drivers/net/ethernet/freescale/ucc_geth_ethtool.c b/drivers/net/ethernet/freescale/ucc_geth_ethtool.c index 69b2b98b1525..601beb93d3b3 100644 --- a/drivers/net/ethernet/freescale/ucc_geth_ethtool.c +++ b/drivers/net/ethernet/freescale/ucc_geth_ethtool.c @@ -337,8 +337,8 @@ static void uec_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *drvinfo) { - strlcpy(drvinfo->driver, DRV_NAME, sizeof(drvinfo->driver)); - strlcpy(drvinfo->bus_info, "QUICC ENGINE", sizeof(drvinfo->bus_info)); + strscpy(drvinfo->driver, DRV_NAME, sizeof(drvinfo->driver)); + strscpy(drvinfo->bus_info, "QUICC ENGINE", sizeof(drvinfo->bus_info)); } #ifdef CONFIG_PM diff --git a/drivers/net/ethernet/freescale/xgmac_mdio.c b/drivers/net/ethernet/freescale/xgmac_mdio.c index ec90da1de030..d7d39a58cd80 100644 --- a/drivers/net/ethernet/freescale/xgmac_mdio.c +++ b/drivers/net/ethernet/freescale/xgmac_mdio.c @@ -355,7 +355,7 @@ static int xgmac_mdio_probe(struct platform_device *pdev) if (ret) return ret; - fwnode = pdev->dev.fwnode; + fwnode = dev_fwnode(&pdev->dev); if (is_of_node(fwnode)) ret = of_mdiobus_register(bus, to_of_node(fwnode)); else if (is_acpi_node(fwnode)) diff --git a/drivers/net/ethernet/fujitsu/fmvj18x_cs.c b/drivers/net/ethernet/fujitsu/fmvj18x_cs.c index b0d733e9a7c6..4859493471db 100644 --- a/drivers/net/ethernet/fujitsu/fmvj18x_cs.c +++ b/drivers/net/ethernet/fujitsu/fmvj18x_cs.c @@ -1046,8 +1046,8 @@ static void fjn_rx(struct net_device *dev) static void netdev_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { - strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); - strlcpy(info->version, DRV_VERSION, sizeof(info->version)); + strscpy(info->driver, DRV_NAME, sizeof(info->driver)); + strscpy(info->version, DRV_VERSION, sizeof(info->version)); snprintf(info->bus_info, sizeof(info->bus_info), "PCMCIA 0x%lx", dev->base_addr); } diff --git a/drivers/net/ethernet/fungible/funeth/funeth_main.c b/drivers/net/ethernet/fungible/funeth/funeth_main.c index f247b7ad3a88..095f51c4d9d9 100644 --- a/drivers/net/ethernet/fungible/funeth/funeth_main.c +++ b/drivers/net/ethernet/fungible/funeth/funeth_main.c @@ -339,8 +339,7 @@ static int fun_alloc_queue_irqs(struct net_device *dev, unsigned int ntx, return PTR_ERR(irq); fp->num_rx_irqs++; - netif_napi_add(dev, &irq->napi, fun_rxq_napi_poll, - NAPI_POLL_WEIGHT); + netif_napi_add(dev, &irq->napi, fun_rxq_napi_poll); } netif_info(fp, intr, dev, "Reserved %u/%u IRQs for Tx/Rx queues\n", @@ -1802,16 +1801,14 @@ static int fun_create_netdev(struct fun_ethdev *ed, unsigned int portid) if (rc) goto unreg_devlink; - if (fp->dl_port.devlink) - devlink_port_type_eth_set(&fp->dl_port, netdev); + devlink_port_type_eth_set(&fp->dl_port, netdev); return 0; unreg_devlink: ed->netdevs[portid] = NULL; fun_ktls_cleanup(fp); - if (fp->dl_port.devlink) - devlink_port_unregister(&fp->dl_port); + devlink_port_unregister(&fp->dl_port); free_stats: fun_free_stats_area(fp); free_rss: @@ -1830,11 +1827,9 @@ static void fun_destroy_netdev(struct net_device *netdev) struct funeth_priv *fp; fp = netdev_priv(netdev); - if (fp->dl_port.devlink) { - devlink_port_type_clear(&fp->dl_port); - devlink_port_unregister(&fp->dl_port); - } + devlink_port_type_clear(&fp->dl_port); unregister_netdev(netdev); + devlink_port_unregister(&fp->dl_port); fun_ktls_cleanup(fp); fun_free_stats_area(fp); fun_free_rss(fp); diff --git a/drivers/net/ethernet/google/gve/gve_main.c b/drivers/net/ethernet/google/gve/gve_main.c index 044db3ebb071..d3e3ac242bfc 100644 --- a/drivers/net/ethernet/google/gve/gve_main.c +++ b/drivers/net/ethernet/google/gve/gve_main.c @@ -526,8 +526,7 @@ static void gve_add_napi(struct gve_priv *priv, int ntfy_idx, { struct gve_notify_block *block = &priv->ntfy_blocks[ntfy_idx]; - netif_napi_add(priv->dev, &block->napi, gve_poll, - NAPI_POLL_WEIGHT); + netif_napi_add(priv->dev, &block->napi, gve_poll); } static void gve_remove_napi(struct gve_priv *priv, int ntfy_idx) diff --git a/drivers/net/ethernet/hisilicon/hip04_eth.c b/drivers/net/ethernet/hisilicon/hip04_eth.c index c84ef494bd60..50c3f5d6611f 100644 --- a/drivers/net/ethernet/hisilicon/hip04_eth.c +++ b/drivers/net/ethernet/hisilicon/hip04_eth.c @@ -830,8 +830,8 @@ static int hip04_set_coalesce(struct net_device *netdev, static void hip04_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *drvinfo) { - strlcpy(drvinfo->driver, DRV_NAME, sizeof(drvinfo->driver)); - strlcpy(drvinfo->version, DRV_VERSION, sizeof(drvinfo->version)); + strscpy(drvinfo->driver, DRV_NAME, sizeof(drvinfo->driver)); + strscpy(drvinfo->version, DRV_VERSION, sizeof(drvinfo->version)); } static const struct ethtool_ops hip04_ethtool_ops = { @@ -990,7 +990,7 @@ static int hip04_mac_probe(struct platform_device *pdev) ndev->watchdog_timeo = TX_TIMEOUT; ndev->priv_flags |= IFF_UNICAST_FLT; ndev->irq = irq; - netif_napi_add(ndev, &priv->napi, hip04_rx_poll, NAPI_POLL_WEIGHT); + netif_napi_add(ndev, &priv->napi, hip04_rx_poll); hip04_reset_dreq(priv); hip04_reset_ppe(priv); diff --git a/drivers/net/ethernet/hisilicon/hix5hd2_gmac.c b/drivers/net/ethernet/hisilicon/hix5hd2_gmac.c index d7e62eca050f..ffcf797dfa90 100644 --- a/drivers/net/ethernet/hisilicon/hix5hd2_gmac.c +++ b/drivers/net/ethernet/hisilicon/hix5hd2_gmac.c @@ -1243,7 +1243,7 @@ static int hix5hd2_dev_probe(struct platform_device *pdev) if (ret) goto out_phy_node; - netif_napi_add(ndev, &priv->napi, hix5hd2_poll, NAPI_POLL_WEIGHT); + netif_napi_add(ndev, &priv->napi, hix5hd2_poll); if (HAS_CAP_TSO(priv->hw_cap)) { ret = hix5hd2_init_sg_desc_queue(priv); diff --git a/drivers/net/ethernet/hisilicon/hns/hns_enet.c b/drivers/net/ethernet/hisilicon/hns/hns_enet.c index d94cc8c6681f..7cf10d1e2b31 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_enet.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_enet.c @@ -2109,8 +2109,7 @@ static int hns_nic_init_ring_data(struct hns_nic_priv *priv) rd->fini_process = is_ver1 ? hns_nic_tx_fini_pro : hns_nic_tx_fini_pro_v2; - netif_napi_add(priv->netdev, &rd->napi, - hns_nic_common_poll, NAPI_POLL_WEIGHT); + netif_napi_add(priv->netdev, &rd->napi, hns_nic_common_poll); rd->ring->irq_init_flag = RCB_IRQ_NOT_INITED; } for (i = h->q_num; i < h->q_num * 2; i++) { @@ -2122,8 +2121,7 @@ static int hns_nic_init_ring_data(struct hns_nic_priv *priv) rd->fini_process = is_ver1 ? hns_nic_rx_fini_pro : hns_nic_rx_fini_pro_v2; - netif_napi_add(priv->netdev, &rd->napi, - hns_nic_common_poll, NAPI_POLL_WEIGHT); + netif_napi_add(priv->netdev, &rd->napi, hns_nic_common_poll); rd->ring->irq_init_flag = RCB_IRQ_NOT_INITED; } diff --git a/drivers/net/ethernet/hisilicon/hns3/hclge_mbx.h b/drivers/net/ethernet/hisilicon/hns3/hclge_mbx.h index 7d4ae467f3ad..abcd7877f7d2 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hclge_mbx.h +++ b/drivers/net/ethernet/hisilicon/hns3/hclge_mbx.h @@ -233,6 +233,17 @@ struct hclgevf_mbx_arq_ring { __le16 msg_q[HCLGE_MBX_MAX_ARQ_MSG_NUM][HCLGE_MBX_MAX_ARQ_MSG_SIZE]; }; +struct hclge_dev; + +#define HCLGE_MBX_OPCODE_MAX 256 +struct hclge_mbx_ops_param { + struct hclge_vport *vport; + struct hclge_mbx_vf_to_pf_cmd *req; + struct hclge_respond_to_vf_msg *resp_msg; +}; + +typedef int (*hclge_mbx_ops_fn)(struct hclge_mbx_ops_param *param); + #define hclge_mbx_ring_ptr_move_crq(crq) \ (crq->next_to_use = (crq->next_to_use + 1) % crq->desc_num) #define hclge_mbx_tail_ptr_move_arq(arq) \ diff --git a/drivers/net/ethernet/hisilicon/hns3/hnae3.h b/drivers/net/ethernet/hisilicon/hns3/hnae3.h index 94f80e1c4020..0179fc288f5f 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hnae3.h +++ b/drivers/net/ethernet/hisilicon/hns3/hnae3.h @@ -97,13 +97,15 @@ enum HNAE3_DEV_CAP_BITS { HNAE3_DEV_SUPPORT_VLAN_FLTR_MDF_B, HNAE3_DEV_SUPPORT_MC_MAC_MNG_B, HNAE3_DEV_SUPPORT_CQ_B, + HNAE3_DEV_SUPPORT_FEC_STATS_B, + HNAE3_DEV_SUPPORT_LANE_NUM_B, }; -#define hnae3_dev_fd_supported(hdev) \ - test_bit(HNAE3_DEV_SUPPORT_FD_B, (hdev)->ae_dev->caps) +#define hnae3_ae_dev_fd_supported(ae_dev) \ + test_bit(HNAE3_DEV_SUPPORT_FD_B, (ae_dev)->caps) -#define hnae3_dev_gro_supported(hdev) \ - test_bit(HNAE3_DEV_SUPPORT_GRO_B, (hdev)->ae_dev->caps) +#define hnae3_ae_dev_gro_supported(ae_dev) \ + test_bit(HNAE3_DEV_SUPPORT_GRO_B, (ae_dev)->caps) #define hnae3_dev_fec_supported(hdev) \ test_bit(HNAE3_DEV_SUPPORT_FEC_B, (hdev)->ae_dev->caps) @@ -159,6 +161,12 @@ enum HNAE3_DEV_CAP_BITS { #define hnae3_ae_dev_cq_supported(ae_dev) \ test_bit(HNAE3_DEV_SUPPORT_CQ_B, (ae_dev)->caps) +#define hnae3_ae_dev_fec_stats_supported(ae_dev) \ + test_bit(HNAE3_DEV_SUPPORT_FEC_STATS_B, (ae_dev)->caps) + +#define hnae3_ae_dev_lane_num_supported(ae_dev) \ + test_bit(HNAE3_DEV_SUPPORT_LANE_NUM_B, (ae_dev)->caps) + enum HNAE3_PF_CAP_BITS { HNAE3_PF_SUPPORT_VLAN_FLTR_MDF_B = 0, }; @@ -187,6 +195,7 @@ struct hns3_mac_stats { /* hnae3 loop mode */ enum hnae3_loop { + HNAE3_LOOP_EXTERNAL, HNAE3_LOOP_APP, HNAE3_LOOP_SERIAL_SERDES, HNAE3_LOOP_PARALLEL_SERDES, @@ -223,6 +232,8 @@ enum hnae3_fec_mode { HNAE3_FEC_AUTO = 0, HNAE3_FEC_BASER, HNAE3_FEC_RS, + HNAE3_FEC_LLRS, + HNAE3_FEC_NONE, HNAE3_FEC_USER_DEF, }; @@ -270,6 +281,7 @@ enum hnae3_dbg_cmd { HNAE3_DBG_CMD_TC_SCH_INFO, HNAE3_DBG_CMD_QOS_PAUSE_CFG, HNAE3_DBG_CMD_QOS_PRI_MAP, + HNAE3_DBG_CMD_QOS_DSCP_MAP, HNAE3_DBG_CMD_QOS_BUF_CFG, HNAE3_DBG_CMD_DEV_INFO, HNAE3_DBG_CMD_TX_BD, @@ -308,6 +320,11 @@ enum hnae3_dbg_cmd { HNAE3_DBG_CMD_UNKNOWN, }; +enum hnae3_tc_map_mode { + HNAE3_TC_MAP_MODE_PRIO, + HNAE3_TC_MAP_MODE_DSCP, +}; + struct hnae3_vector_info { u8 __iomem *io_addr; int vector; @@ -560,14 +577,17 @@ struct hnae3_ae_ops { void (*client_stop)(struct hnae3_handle *handle); int (*get_status)(struct hnae3_handle *handle); void (*get_ksettings_an_result)(struct hnae3_handle *handle, - u8 *auto_neg, u32 *speed, u8 *duplex); + u8 *auto_neg, u32 *speed, u8 *duplex, + u32 *lane_num); int (*cfg_mac_speed_dup_h)(struct hnae3_handle *handle, int speed, - u8 duplex); + u8 duplex, u8 lane_num); void (*get_media_type)(struct hnae3_handle *handle, u8 *media_type, u8 *module_type); int (*check_port_speed)(struct hnae3_handle *handle, u32 speed); + void (*get_fec_stats)(struct hnae3_handle *handle, + struct ethtool_fec_stats *fec_stats); void (*get_fec)(struct hnae3_handle *handle, u8 *fec_ability, u8 *fec_mode); int (*set_fec)(struct hnae3_handle *handle, u32 fec_mode); @@ -737,6 +757,8 @@ struct hnae3_ae_ops { int (*get_link_diagnosis_info)(struct hnae3_handle *handle, u32 *status_code); void (*clean_vf_config)(struct hnae3_ae_dev *ae_dev, int num_vfs); + int (*get_dscp_prio)(struct hnae3_handle *handle, u8 dscp, + u8 *tc_map_mode, u8 *priority); }; struct hnae3_dcb_ops { @@ -745,6 +767,8 @@ struct hnae3_dcb_ops { int (*ieee_setets)(struct hnae3_handle *, struct ieee_ets *); int (*ieee_getpfc)(struct hnae3_handle *, struct ieee_pfc *); int (*ieee_setpfc)(struct hnae3_handle *, struct ieee_pfc *); + int (*ieee_setapp)(struct hnae3_handle *h, struct dcb_app *app); + int (*ieee_delapp)(struct hnae3_handle *h, struct dcb_app *app); /* DCBX configuration */ u8 (*getdcbx)(struct hnae3_handle *); @@ -774,6 +798,8 @@ struct hnae3_tc_info { bool mqprio_active; }; +#define HNAE3_MAX_DSCP 64 +#define HNAE3_PRIO_ID_INVALID 0xff struct hnae3_knic_private_info { struct net_device *netdev; /* Set by KNIC client when init instance */ u16 rss_size; /* Allocated RSS queues */ @@ -784,6 +810,9 @@ struct hnae3_knic_private_info { u32 tx_spare_buf_size; struct hnae3_tc_info tc_info; + u8 tc_map_mode; + u8 dscp_app_cnt; + u8 dscp_prio[HNAE3_MAX_DSCP]; u16 num_tqps; /* total number of TQPs in this handle */ struct hnae3_queue **tqp; /* array base of all TQPs in this instance */ @@ -815,6 +844,7 @@ struct hnae3_roce_private_info { #define HNAE3_SUPPORT_SERDES_SERIAL_LOOPBACK BIT(2) #define HNAE3_SUPPORT_VF BIT(3) #define HNAE3_SUPPORT_SERDES_PARALLEL_LOOPBACK BIT(4) +#define HNAE3_SUPPORT_EXTERNAL_LOOPBACK BIT(5) #define HNAE3_USER_UPE BIT(0) /* unicast promisc enabled by user */ #define HNAE3_USER_MPE BIT(1) /* mulitcast promisc enabled by user */ diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_common/hclge_comm_cmd.c b/drivers/net/ethernet/hisilicon/hns3/hns3_common/hclge_comm_cmd.c index c8b151d29f53..f671a63cecde 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3_common/hclge_comm_cmd.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3_common/hclge_comm_cmd.c @@ -52,9 +52,9 @@ void hclge_comm_cmd_reuse_desc(struct hclge_desc *desc, bool is_read) static void hclge_comm_set_default_capability(struct hnae3_ae_dev *ae_dev, bool is_pf) { - set_bit(HNAE3_DEV_SUPPORT_FD_B, ae_dev->caps); set_bit(HNAE3_DEV_SUPPORT_GRO_B, ae_dev->caps); - if (is_pf && ae_dev->dev_version == HNAE3_DEVICE_VERSION_V2) { + if (is_pf) { + set_bit(HNAE3_DEV_SUPPORT_FD_B, ae_dev->caps); set_bit(HNAE3_DEV_SUPPORT_FEC_B, ae_dev->caps); set_bit(HNAE3_DEV_SUPPORT_PAUSE_B, ae_dev->caps); } @@ -91,6 +91,7 @@ int hclge_comm_firmware_compat_config(struct hnae3_ae_dev *ae_dev, hnae3_set_bit(compat, HCLGE_COMM_PHY_IMP_EN_B, 1); hnae3_set_bit(compat, HCLGE_COMM_MAC_STATS_EXT_EN_B, 1); hnae3_set_bit(compat, HCLGE_COMM_SYNC_RX_RING_HEAD_EN_B, 1); + hnae3_set_bit(compat, HCLGE_COMM_LLRS_FEC_EN_B, 1); req->compat = cpu_to_le32(compat); } @@ -150,6 +151,10 @@ static const struct hclge_comm_caps_bit_map hclge_pf_cmd_caps[] = { HNAE3_DEV_SUPPORT_PORT_VLAN_BYPASS_B}, {HCLGE_COMM_CAP_PORT_VLAN_BYPASS_B, HNAE3_DEV_SUPPORT_VLAN_FLTR_MDF_B}, {HCLGE_COMM_CAP_CQ_B, HNAE3_DEV_SUPPORT_CQ_B}, + {HCLGE_COMM_CAP_GRO_B, HNAE3_DEV_SUPPORT_GRO_B}, + {HCLGE_COMM_CAP_FD_B, HNAE3_DEV_SUPPORT_FD_B}, + {HCLGE_COMM_CAP_FEC_STATS_B, HNAE3_DEV_SUPPORT_FEC_STATS_B}, + {HCLGE_COMM_CAP_LANE_NUM_B, HNAE3_DEV_SUPPORT_LANE_NUM_B}, }; static const struct hclge_comm_caps_bit_map hclge_vf_cmd_caps[] = { @@ -162,6 +167,7 @@ static const struct hclge_comm_caps_bit_map hclge_vf_cmd_caps[] = { {HCLGE_COMM_CAP_TX_PUSH_B, HNAE3_DEV_SUPPORT_TX_PUSH_B}, {HCLGE_COMM_CAP_RXD_ADV_LAYOUT_B, HNAE3_DEV_SUPPORT_RXD_ADV_LAYOUT_B}, {HCLGE_COMM_CAP_CQ_B, HNAE3_DEV_SUPPORT_CQ_B}, + {HCLGE_COMM_CAP_GRO_B, HNAE3_DEV_SUPPORT_GRO_B}, }; static void @@ -220,8 +226,10 @@ int hclge_comm_cmd_query_version_and_capability(struct hnae3_ae_dev *ae_dev, HNAE3_PCI_REVISION_BIT_SIZE; ae_dev->dev_version |= ae_dev->pdev->revision; - if (ae_dev->dev_version >= HNAE3_DEVICE_VERSION_V2) + if (ae_dev->dev_version == HNAE3_DEVICE_VERSION_V2) { hclge_comm_set_default_capability(ae_dev, is_pf); + return 0; + } hclge_comm_parse_capability(ae_dev, is_pf, resp); diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_common/hclge_comm_cmd.h b/drivers/net/ethernet/hisilicon/hns3/hns3_common/hclge_comm_cmd.h index 7a7d4cf9bf35..b1f9383b418f 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3_common/hclge_comm_cmd.h +++ b/drivers/net/ethernet/hisilicon/hns3/hns3_common/hclge_comm_cmd.h @@ -20,6 +20,7 @@ #define HCLGE_COMM_PHY_IMP_EN_B 2 #define HCLGE_COMM_MAC_STATS_EXT_EN_B 3 #define HCLGE_COMM_SYNC_RX_RING_HEAD_EN_B 4 +#define HCLGE_COMM_LLRS_FEC_EN_B 5 #define hclge_comm_dev_phy_imp_supported(ae_dev) \ test_bit(HNAE3_DEV_SUPPORT_PHY_IMP_B, (ae_dev)->caps) @@ -102,6 +103,7 @@ enum hclge_opcode_type { HCLGE_OPC_MAC_TNL_INT_EN = 0x0311, HCLGE_OPC_CLEAR_MAC_TNL_INT = 0x0312, HCLGE_OPC_COMMON_LOOPBACK = 0x0315, + HCLGE_OPC_QUERY_FEC_STATS = 0x0316, HCLGE_OPC_CONFIG_FEC_MODE = 0x031A, HCLGE_OPC_QUERY_ROH_TYPE_INFO = 0x0389, @@ -339,6 +341,10 @@ enum HCLGE_COMM_CAP_BITS { HCLGE_COMM_CAP_RXD_ADV_LAYOUT_B = 15, HCLGE_COMM_CAP_PORT_VLAN_BYPASS_B = 17, HCLGE_COMM_CAP_CQ_B = 18, + HCLGE_COMM_CAP_GRO_B = 20, + HCLGE_COMM_CAP_FD_B = 21, + HCLGE_COMM_CAP_FEC_STATS_B = 25, + HCLGE_COMM_CAP_LANE_NUM_B = 27, }; enum HCLGE_COMM_API_CAP_BITS { diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_dcbnl.c b/drivers/net/ethernet/hisilicon/hns3/hns3_dcbnl.c index d2ec4c573bf8..3b6dbf158b98 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3_dcbnl.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3_dcbnl.c @@ -56,6 +56,32 @@ static int hns3_dcbnl_ieee_setpfc(struct net_device *ndev, struct ieee_pfc *pfc) return -EOPNOTSUPP; } +static int hns3_dcbnl_ieee_setapp(struct net_device *ndev, struct dcb_app *app) +{ + struct hnae3_handle *h = hns3_get_handle(ndev); + + if (hns3_nic_resetting(ndev)) + return -EBUSY; + + if (h->kinfo.dcb_ops->ieee_setapp) + return h->kinfo.dcb_ops->ieee_setapp(h, app); + + return -EOPNOTSUPP; +} + +static int hns3_dcbnl_ieee_delapp(struct net_device *ndev, struct dcb_app *app) +{ + struct hnae3_handle *h = hns3_get_handle(ndev); + + if (hns3_nic_resetting(ndev)) + return -EBUSY; + + if (h->kinfo.dcb_ops->ieee_setapp) + return h->kinfo.dcb_ops->ieee_delapp(h, app); + + return -EOPNOTSUPP; +} + /* DCBX configuration */ static u8 hns3_dcbnl_getdcbx(struct net_device *ndev) { @@ -83,6 +109,8 @@ static const struct dcbnl_rtnl_ops hns3_dcbnl_ops = { .ieee_setets = hns3_dcbnl_ieee_setets, .ieee_getpfc = hns3_dcbnl_ieee_getpfc, .ieee_setpfc = hns3_dcbnl_ieee_setpfc, + .ieee_setapp = hns3_dcbnl_ieee_setapp, + .ieee_delapp = hns3_dcbnl_ieee_delapp, .getdcbx = hns3_dcbnl_getdcbx, .setdcbx = hns3_dcbnl_setdcbx, }; diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_debugfs.c b/drivers/net/ethernet/hisilicon/hns3/hns3_debugfs.c index 93aeb615191d..66feb23f7b7b 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3_debugfs.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3_debugfs.c @@ -106,6 +106,13 @@ static struct hns3_dbg_cmd_info hns3_dbg_cmd[] = { .init = hns3_dbg_common_file_init, }, { + .name = "qos_dscp_map", + .cmd = HNAE3_DBG_CMD_QOS_DSCP_MAP, + .dentry = HNS3_DBG_DENTRY_TM, + .buf_len = HNS3_DBG_READ_LEN, + .init = hns3_dbg_common_file_init, + }, + { .name = "qos_buf_cfg", .cmd = HNAE3_DBG_CMD_QOS_BUF_CFG, .dentry = HNS3_DBG_DENTRY_TM, @@ -395,6 +402,12 @@ static struct hns3_dbg_cap_info hns3_dbg_cap[] = { }, { .name = "support modify vlan filter state", .cap_bit = HNAE3_DEV_SUPPORT_VLAN_FLTR_MDF_B, + }, { + .name = "support FEC statistics", + .cap_bit = HNAE3_DEV_SUPPORT_FEC_STATS_B, + }, { + .name = "support lane num", + .cap_bit = HNAE3_DEV_SUPPORT_LANE_NUM_B, } }; diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c index 35d70041b9e8..4cb2421e71a7 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c @@ -2963,6 +2963,48 @@ static int hns3_nic_set_vf_mac(struct net_device *netdev, int vf_id, u8 *mac) return h->ae_algo->ops->set_vf_mac(h, vf_id, mac); } +#define HNS3_INVALID_DSCP 0xff +#define HNS3_DSCP_SHIFT 2 + +static u8 hns3_get_skb_dscp(struct sk_buff *skb) +{ + __be16 protocol = skb->protocol; + u8 dscp = HNS3_INVALID_DSCP; + + if (protocol == htons(ETH_P_8021Q)) + protocol = vlan_get_protocol(skb); + + if (protocol == htons(ETH_P_IP)) + dscp = ipv4_get_dsfield(ip_hdr(skb)) >> HNS3_DSCP_SHIFT; + else if (protocol == htons(ETH_P_IPV6)) + dscp = ipv6_get_dsfield(ipv6_hdr(skb)) >> HNS3_DSCP_SHIFT; + + return dscp; +} + +static u16 hns3_nic_select_queue(struct net_device *netdev, + struct sk_buff *skb, + struct net_device *sb_dev) +{ + struct hnae3_handle *h = hns3_get_handle(netdev); + u8 dscp; + + if (h->kinfo.tc_map_mode != HNAE3_TC_MAP_MODE_DSCP || + !h->ae_algo->ops->get_dscp_prio) + goto out; + + dscp = hns3_get_skb_dscp(skb); + if (unlikely(dscp >= HNAE3_MAX_DSCP)) + goto out; + + skb->priority = h->kinfo.dscp_prio[dscp]; + if (skb->priority == HNAE3_PRIO_ID_INVALID) + skb->priority = 0; + +out: + return netdev_pick_tx(netdev, skb, sb_dev); +} + static const struct net_device_ops hns3_nic_netdev_ops = { .ndo_open = hns3_nic_net_open, .ndo_stop = hns3_nic_net_stop, @@ -2988,6 +3030,7 @@ static const struct net_device_ops hns3_nic_netdev_ops = { .ndo_set_vf_link_state = hns3_nic_set_vf_link_state, .ndo_set_vf_rate = hns3_nic_set_vf_rate, .ndo_set_vf_mac = hns3_nic_set_vf_mac, + .ndo_select_queue = hns3_nic_select_queue, }; bool hns3_is_phys_func(struct pci_dev *pdev) @@ -3271,12 +3314,11 @@ static void hns3_set_default_feature(struct net_device *netdev) NETIF_F_GSO_GRE_CSUM | NETIF_F_GSO_UDP_TUNNEL | NETIF_F_SCTP_CRC | NETIF_F_FRAGLIST; - if (ae_dev->dev_version >= HNAE3_DEVICE_VERSION_V2) { + if (hnae3_ae_dev_gro_supported(ae_dev)) netdev->features |= NETIF_F_GRO_HW; - if (!(h->flags & HNAE3_SUPPORT_VF)) - netdev->features |= NETIF_F_NTUPLE; - } + if (hnae3_ae_dev_fd_supported(ae_dev)) + netdev->features |= NETIF_F_NTUPLE; if (test_bit(HNAE3_DEV_SUPPORT_UDP_GSO_B, ae_dev->caps)) netdev->features |= NETIF_F_GSO_UDP_L4; @@ -4650,7 +4692,7 @@ static int hns3_nic_init_vector_data(struct hns3_nic_priv *priv) goto map_ring_fail; netif_napi_add(priv->netdev, &tqp_vector->napi, - hns3_nic_common_poll, NAPI_POLL_WEIGHT); + hns3_nic_common_poll); } return 0; @@ -5782,6 +5824,57 @@ int hns3_set_channels(struct net_device *netdev, return 0; } +void hns3_external_lb_prepare(struct net_device *ndev, bool if_running) +{ + struct hns3_nic_priv *priv = netdev_priv(ndev); + struct hnae3_handle *h = priv->ae_handle; + int i; + + if (!if_running) + return; + + netif_carrier_off(ndev); + netif_tx_disable(ndev); + + for (i = 0; i < priv->vector_num; i++) + hns3_vector_disable(&priv->tqp_vector[i]); + + for (i = 0; i < h->kinfo.num_tqps; i++) + hns3_tqp_disable(h->kinfo.tqp[i]); + + /* delay ring buffer clearing to hns3_reset_notify_uninit_enet + * during reset process, because driver may not be able + * to disable the ring through firmware when downing the netdev. + */ + if (!hns3_nic_resetting(ndev)) + hns3_nic_reset_all_ring(priv->ae_handle); + + hns3_reset_tx_queue(priv->ae_handle); +} + +void hns3_external_lb_restore(struct net_device *ndev, bool if_running) +{ + struct hns3_nic_priv *priv = netdev_priv(ndev); + struct hnae3_handle *h = priv->ae_handle; + int i; + + if (!if_running) + return; + + hns3_nic_reset_all_ring(priv->ae_handle); + + for (i = 0; i < priv->vector_num; i++) + hns3_vector_enable(&priv->tqp_vector[i]); + + for (i = 0; i < h->kinfo.num_tqps; i++) + hns3_tqp_enable(h->kinfo.tqp[i]); + + netif_tx_wake_all_queues(ndev); + + if (h->ae_algo->ops->get_status(h)) + netif_carrier_on(ndev); +} + static const struct hns3_hw_error_info hns3_hw_err[] = { { .type = HNAE3_PPU_POISON_ERROR, .msg = "PPU poison" }, diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.h b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.h index 4a3253692dcc..133a054af6b7 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.h +++ b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.h @@ -744,4 +744,7 @@ u16 hns3_get_max_available_channels(struct hnae3_handle *h); void hns3_cq_period_mode_init(struct hns3_nic_priv *priv, enum dim_cq_period_mode tx_mode, enum dim_cq_period_mode rx_mode); + +void hns3_external_lb_prepare(struct net_device *ndev, bool if_running); +void hns3_external_lb_restore(struct net_device *ndev, bool if_running); #endif diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_ethtool.c b/drivers/net/ethernet/hisilicon/hns3/hns3_ethtool.c index 4c7988e308a2..cdf76fb58d45 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3_ethtool.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3_ethtool.c @@ -69,7 +69,6 @@ static const struct hns3_stats hns3_rxq_stats[] = { #define HNS3_TQP_STATS_COUNT (HNS3_TXQ_STATS_COUNT + HNS3_RXQ_STATS_COUNT) -#define HNS3_SELF_TEST_TYPE_NUM 4 #define HNS3_NIC_LB_TEST_PKT_NUM 1 #define HNS3_NIC_LB_TEST_RING_ID 0 #define HNS3_NIC_LB_TEST_PACKET_SIZE 128 @@ -95,6 +94,7 @@ static int hns3_lp_setup(struct net_device *ndev, enum hnae3_loop loop, bool en) case HNAE3_LOOP_PARALLEL_SERDES: case HNAE3_LOOP_APP: case HNAE3_LOOP_PHY: + case HNAE3_LOOP_EXTERNAL: ret = h->ae_algo->ops->set_loopback(h, loop, en); break; default: @@ -304,6 +304,10 @@ out: static void hns3_set_selftest_param(struct hnae3_handle *h, int (*st_param)[2]) { + st_param[HNAE3_LOOP_EXTERNAL][0] = HNAE3_LOOP_EXTERNAL; + st_param[HNAE3_LOOP_EXTERNAL][1] = + h->flags & HNAE3_SUPPORT_EXTERNAL_LOOPBACK; + st_param[HNAE3_LOOP_APP][0] = HNAE3_LOOP_APP; st_param[HNAE3_LOOP_APP][1] = h->flags & HNAE3_SUPPORT_APP_LOOPBACK; @@ -322,17 +326,11 @@ static void hns3_set_selftest_param(struct hnae3_handle *h, int (*st_param)[2]) h->flags & HNAE3_SUPPORT_PHY_LOOPBACK; } -static void hns3_selftest_prepare(struct net_device *ndev, - bool if_running, int (*st_param)[2]) +static void hns3_selftest_prepare(struct net_device *ndev, bool if_running) { struct hns3_nic_priv *priv = netdev_priv(ndev); struct hnae3_handle *h = priv->ae_handle; - if (netif_msg_ifdown(h)) - netdev_info(ndev, "self test start\n"); - - hns3_set_selftest_param(h, st_param); - if (if_running) ndev->netdev_ops->ndo_stop(ndev); @@ -371,18 +369,15 @@ static void hns3_selftest_restore(struct net_device *ndev, bool if_running) if (if_running) ndev->netdev_ops->ndo_open(ndev); - - if (netif_msg_ifdown(h)) - netdev_info(ndev, "self test end\n"); } static void hns3_do_selftest(struct net_device *ndev, int (*st_param)[2], struct ethtool_test *eth_test, u64 *data) { - int test_index = 0; + int test_index = HNAE3_LOOP_APP; u32 i; - for (i = 0; i < HNS3_SELF_TEST_TYPE_NUM; i++) { + for (i = HNAE3_LOOP_APP; i < HNAE3_LOOP_NONE; i++) { enum hnae3_loop loop_type = (enum hnae3_loop)st_param[i][0]; if (!st_param[i][1]) @@ -401,6 +396,20 @@ static void hns3_do_selftest(struct net_device *ndev, int (*st_param)[2], } } +static void hns3_do_external_lb(struct net_device *ndev, + struct ethtool_test *eth_test, u64 *data) +{ + data[HNAE3_LOOP_EXTERNAL] = hns3_lp_up(ndev, HNAE3_LOOP_EXTERNAL); + if (!data[HNAE3_LOOP_EXTERNAL]) + data[HNAE3_LOOP_EXTERNAL] = hns3_lp_run_test(ndev, HNAE3_LOOP_EXTERNAL); + hns3_lp_down(ndev, HNAE3_LOOP_EXTERNAL); + + if (data[HNAE3_LOOP_EXTERNAL]) + eth_test->flags |= ETH_TEST_FL_FAILED; + + eth_test->flags |= ETH_TEST_FL_EXTERNAL_LB_DONE; +} + /** * hns3_self_test - self test * @ndev: net device @@ -410,7 +419,9 @@ static void hns3_do_selftest(struct net_device *ndev, int (*st_param)[2], static void hns3_self_test(struct net_device *ndev, struct ethtool_test *eth_test, u64 *data) { - int st_param[HNS3_SELF_TEST_TYPE_NUM][2]; + struct hns3_nic_priv *priv = netdev_priv(ndev); + struct hnae3_handle *h = priv->ae_handle; + int st_param[HNAE3_LOOP_NONE][2]; bool if_running = netif_running(ndev); if (hns3_nic_resetting(ndev)) { @@ -418,13 +429,29 @@ static void hns3_self_test(struct net_device *ndev, return; } - /* Only do offline selftest, or pass by default */ - if (eth_test->flags != ETH_TEST_FL_OFFLINE) + if (!(eth_test->flags & ETH_TEST_FL_OFFLINE)) return; - hns3_selftest_prepare(ndev, if_running, st_param); + if (netif_msg_ifdown(h)) + netdev_info(ndev, "self test start\n"); + + hns3_set_selftest_param(h, st_param); + + /* external loopback test requires that the link is up and the duplex is + * full, do external test first to reduce the whole test time + */ + if (eth_test->flags & ETH_TEST_FL_EXTERNAL_LB) { + hns3_external_lb_prepare(ndev, if_running); + hns3_do_external_lb(ndev, eth_test, data); + hns3_external_lb_restore(ndev, if_running); + } + + hns3_selftest_prepare(ndev, if_running); hns3_do_selftest(ndev, st_param, eth_test, data); hns3_selftest_restore(ndev, if_running); + + if (netif_msg_ifdown(h)) + netdev_info(ndev, "self test end\n"); } static void hns3_update_limit_promisc_mode(struct net_device *netdev, @@ -712,7 +739,8 @@ static void hns3_get_ksettings(struct hnae3_handle *h, ops->get_ksettings_an_result(h, &cmd->base.autoneg, &cmd->base.speed, - &cmd->base.duplex); + &cmd->base.duplex, + &cmd->lanes); /* 2.get link mode */ if (ops->get_link_mode) @@ -794,6 +822,7 @@ static int hns3_check_ksettings_param(const struct net_device *netdev, const struct hnae3_ae_ops *ops = handle->ae_algo->ops; u8 module_type = HNAE3_MODULE_TYPE_UNKNOWN; u8 media_type = HNAE3_MEDIA_TYPE_UNKNOWN; + u32 lane_num; u8 autoneg; u32 speed; u8 duplex; @@ -806,9 +835,9 @@ static int hns3_check_ksettings_param(const struct net_device *netdev, return 0; if (ops->get_ksettings_an_result) { - ops->get_ksettings_an_result(handle, &autoneg, &speed, &duplex); + ops->get_ksettings_an_result(handle, &autoneg, &speed, &duplex, &lane_num); if (cmd->base.autoneg == autoneg && cmd->base.speed == speed && - cmd->base.duplex == duplex) + cmd->base.duplex == duplex && cmd->lanes == lane_num) return 0; } @@ -845,10 +874,14 @@ static int hns3_set_link_ksettings(struct net_device *netdev, if (cmd->base.speed == SPEED_1000 && cmd->base.duplex == DUPLEX_HALF) return -EINVAL; + if (cmd->lanes && !hnae3_ae_dev_lane_num_supported(ae_dev)) + return -EOPNOTSUPP; + netif_dbg(handle, drv, netdev, - "set link(%s): autoneg=%u, speed=%u, duplex=%u\n", + "set link(%s): autoneg=%u, speed=%u, duplex=%u, lanes=%u\n", netdev->phydev ? "phy" : "mac", - cmd->base.autoneg, cmd->base.speed, cmd->base.duplex); + cmd->base.autoneg, cmd->base.speed, cmd->base.duplex, + cmd->lanes); /* Only support ksettings_set for netdev with phy attached for now */ if (netdev->phydev) { @@ -886,7 +919,7 @@ static int hns3_set_link_ksettings(struct net_device *netdev, if (ops->cfg_mac_speed_dup_h) ret = ops->cfg_mac_speed_dup_h(handle, cmd->base.speed, - cmd->base.duplex); + cmd->base.duplex, (u8)(cmd->lanes)); return ret; } @@ -1612,6 +1645,19 @@ static void hns3_set_msglevel(struct net_device *netdev, u32 msg_level) h->msg_enable = msg_level; } +static void hns3_get_fec_stats(struct net_device *netdev, + struct ethtool_fec_stats *fec_stats) +{ + struct hnae3_handle *handle = hns3_get_handle(netdev); + struct hnae3_ae_dev *ae_dev = pci_get_drvdata(handle->pdev); + const struct hnae3_ae_ops *ops = handle->ae_algo->ops; + + if (!hnae3_ae_dev_fec_stats_supported(ae_dev) || !ops->get_fec_stats) + return; + + ops->get_fec_stats(handle, fec_stats); +} + /* Translate local fec value into ethtool value. */ static unsigned int loc_to_eth_fec(u8 loc_fec) { @@ -1621,12 +1667,12 @@ static unsigned int loc_to_eth_fec(u8 loc_fec) eth_fec |= ETHTOOL_FEC_AUTO; if (loc_fec & BIT(HNAE3_FEC_RS)) eth_fec |= ETHTOOL_FEC_RS; + if (loc_fec & BIT(HNAE3_FEC_LLRS)) + eth_fec |= ETHTOOL_FEC_LLRS; if (loc_fec & BIT(HNAE3_FEC_BASER)) eth_fec |= ETHTOOL_FEC_BASER; - - /* if nothing is set, then FEC is off */ - if (!eth_fec) - eth_fec = ETHTOOL_FEC_OFF; + if (loc_fec & BIT(HNAE3_FEC_NONE)) + eth_fec |= ETHTOOL_FEC_OFF; return eth_fec; } @@ -1637,12 +1683,13 @@ static unsigned int eth_to_loc_fec(unsigned int eth_fec) u32 loc_fec = 0; if (eth_fec & ETHTOOL_FEC_OFF) - return loc_fec; - + loc_fec |= BIT(HNAE3_FEC_NONE); if (eth_fec & ETHTOOL_FEC_AUTO) loc_fec |= BIT(HNAE3_FEC_AUTO); if (eth_fec & ETHTOOL_FEC_RS) loc_fec |= BIT(HNAE3_FEC_RS); + if (eth_fec & ETHTOOL_FEC_LLRS) + loc_fec |= BIT(HNAE3_FEC_LLRS); if (eth_fec & ETHTOOL_FEC_BASER) loc_fec |= BIT(HNAE3_FEC_BASER); @@ -1668,6 +1715,8 @@ static int hns3_get_fecparam(struct net_device *netdev, fec->fec = loc_to_eth_fec(fec_ability); fec->active_fec = loc_to_eth_fec(fec_mode); + if (!fec->active_fec) + fec->active_fec = ETHTOOL_FEC_OFF; return 0; } @@ -2051,6 +2100,7 @@ static const struct ethtool_ops hns3vf_ethtool_ops = { static const struct ethtool_ops hns3_ethtool_ops = { .supported_coalesce_params = HNS3_ETHTOOL_COALESCE, .supported_ring_params = HNS3_ETHTOOL_RING, + .cap_link_lanes_supported = true, .self_test = hns3_self_test, .get_drvinfo = hns3_get_drvinfo, .get_link = hns3_get_link, @@ -2081,6 +2131,7 @@ static const struct ethtool_ops hns3_ethtool_ops = { .set_msglevel = hns3_set_msglevel, .get_fecparam = hns3_get_fecparam, .set_fecparam = hns3_set_fecparam, + .get_fec_stats = hns3_get_fec_stats, .get_module_info = hns3_get_module_info, .get_module_eeprom = hns3_get_module_eeprom, .get_priv_flags = hns3_get_priv_flags, diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.h b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.h index f9d89511eb32..43cada51d8cb 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.h +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.h @@ -321,7 +321,9 @@ struct hclge_config_mac_speed_dup_cmd { #define HCLGE_CFG_MAC_SPEED_CHANGE_EN_B 0 u8 mac_change_fec_en; - u8 rsv[22]; + u8 rsv[4]; + u8 lane_num; + u8 rsv1[17]; }; #define HCLGE_TQP_ENABLE_B 0 @@ -347,7 +349,9 @@ struct hclge_sfp_info_cmd { u8 autoneg_ability; /* whether support autoneg */ __le32 speed_ability; /* speed ability for current media */ __le32 module_type; - u8 rsv[8]; + u8 fec_ability; + u8 lane_num; + u8 rsv[6]; }; #define HCLGE_MAC_CFG_FEC_AUTO_EN_B 0 @@ -359,12 +363,27 @@ struct hclge_sfp_info_cmd { #define HCLGE_MAC_FEC_OFF 0 #define HCLGE_MAC_FEC_BASER 1 #define HCLGE_MAC_FEC_RS 2 +#define HCLGE_MAC_FEC_LLRS 3 struct hclge_config_fec_cmd { u8 fec_mode; u8 default_config; u8 rsv[22]; }; +#define HCLGE_FEC_STATS_CMD_NUM 4 + +struct hclge_query_fec_stats_cmd { + /* fec rs mode total stats */ + __le32 rs_fec_corr_blocks; + __le32 rs_fec_uncorr_blocks; + __le32 rs_fec_error_blocks; + /* fec base-r mode per lanes stats */ + u8 base_r_lane_num; + u8 rsv[3]; + __le32 base_r_fec_corr_blocks; + __le32 base_r_fec_uncorr_blocks; +}; + #define HCLGE_MAC_UPLINK_PORT 0x100 struct hclge_config_max_frm_size_cmd { diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_dcb.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_dcb.c index 69b8673436ca..c4aded65e848 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_dcb.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_dcb.c @@ -359,6 +359,93 @@ static int hclge_ieee_setpfc(struct hnae3_handle *h, struct ieee_pfc *pfc) return hclge_notify_client(hdev, HNAE3_UP_CLIENT); } +static int hclge_ieee_setapp(struct hnae3_handle *h, struct dcb_app *app) +{ + struct hclge_vport *vport = hclge_get_vport(h); + struct net_device *netdev = h->kinfo.netdev; + struct hclge_dev *hdev = vport->back; + struct dcb_app old_app; + int ret; + + if (app->selector != IEEE_8021QAZ_APP_SEL_DSCP || + app->protocol >= HNAE3_MAX_DSCP || + app->priority >= HNAE3_MAX_USER_PRIO) + return -EINVAL; + + dev_info(&hdev->pdev->dev, "setapp dscp=%u priority=%u\n", + app->protocol, app->priority); + + if (app->priority == h->kinfo.dscp_prio[app->protocol]) + return 0; + + ret = dcb_ieee_setapp(netdev, app); + if (ret) + return ret; + + old_app.selector = IEEE_8021QAZ_APP_SEL_DSCP; + old_app.protocol = app->protocol; + old_app.priority = h->kinfo.dscp_prio[app->protocol]; + + h->kinfo.dscp_prio[app->protocol] = app->priority; + ret = hclge_dscp_to_tc_map(hdev); + if (ret) { + dev_err(&hdev->pdev->dev, + "failed to set dscp to tc map, ret = %d\n", ret); + h->kinfo.dscp_prio[app->protocol] = old_app.priority; + (void)dcb_ieee_delapp(netdev, app); + return ret; + } + + vport->nic.kinfo.tc_map_mode = HNAE3_TC_MAP_MODE_DSCP; + if (old_app.priority == HNAE3_PRIO_ID_INVALID) + h->kinfo.dscp_app_cnt++; + else + ret = dcb_ieee_delapp(netdev, &old_app); + + return ret; +} + +static int hclge_ieee_delapp(struct hnae3_handle *h, struct dcb_app *app) +{ + struct hclge_vport *vport = hclge_get_vport(h); + struct net_device *netdev = h->kinfo.netdev; + struct hclge_dev *hdev = vport->back; + int ret; + + if (app->selector != IEEE_8021QAZ_APP_SEL_DSCP || + app->protocol >= HNAE3_MAX_DSCP || + app->priority >= HNAE3_MAX_USER_PRIO || + app->priority != h->kinfo.dscp_prio[app->protocol]) + return -EINVAL; + + dev_info(&hdev->pdev->dev, "delapp dscp=%u priority=%u\n", + app->protocol, app->priority); + + ret = dcb_ieee_delapp(netdev, app); + if (ret) + return ret; + + h->kinfo.dscp_prio[app->protocol] = HNAE3_PRIO_ID_INVALID; + ret = hclge_dscp_to_tc_map(hdev); + if (ret) { + dev_err(&hdev->pdev->dev, + "failed to del dscp to tc map, ret = %d\n", ret); + h->kinfo.dscp_prio[app->protocol] = app->priority; + (void)dcb_ieee_setapp(netdev, app); + return ret; + } + + if (h->kinfo.dscp_app_cnt) + h->kinfo.dscp_app_cnt--; + + if (!h->kinfo.dscp_app_cnt) { + vport->nic.kinfo.tc_map_mode = HNAE3_TC_MAP_MODE_PRIO; + ret = hclge_up_to_tc_map(hdev); + } + + return ret; +} + /* DCBX configuration */ static u8 hclge_getdcbx(struct hnae3_handle *h) { @@ -543,6 +630,8 @@ static const struct hnae3_dcb_ops hns3_dcb_ops = { .ieee_setets = hclge_ieee_setets, .ieee_getpfc = hclge_ieee_getpfc, .ieee_setpfc = hclge_ieee_setpfc, + .ieee_setapp = hclge_ieee_setapp, + .ieee_delapp = hclge_ieee_delapp, .getdcbx = hclge_getdcbx, .setdcbx = hclge_setdcbx, .setup_tc = hclge_setup_tc, diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_debugfs.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_debugfs.c index 9b870e79c290..142415c84c6b 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_debugfs.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_debugfs.c @@ -14,6 +14,8 @@ static const char * const hclge_mac_state_str[] = { "TO_ADD", "TO_DEL", "ACTIVE" }; +static const char * const tc_map_mode_str[] = { "PRIO", "DSCP" }; + static const struct hclge_dbg_reg_type_info hclge_dbg_reg_info[] = { { .cmd = HNAE3_DBG_CMD_REG_BIOS_COMMON, .dfx_msg = &hclge_dbg_bios_common_reg[0], @@ -1115,10 +1117,11 @@ static int hclge_dbg_dump_qos_pause_cfg(struct hclge_dev *hdev, char *buf, return 0; } +#define HCLGE_DBG_TC_MASK 0x0F + static int hclge_dbg_dump_qos_pri_map(struct hclge_dev *hdev, char *buf, int len) { -#define HCLGE_DBG_TC_MASK 0x0F #define HCLGE_DBG_TC_BIT_WIDTH 4 struct hclge_qos_pri_map_cmd *pri_map; @@ -1152,6 +1155,58 @@ static int hclge_dbg_dump_qos_pri_map(struct hclge_dev *hdev, char *buf, return 0; } +static int hclge_dbg_dump_qos_dscp_map(struct hclge_dev *hdev, char *buf, + int len) +{ + struct hnae3_knic_private_info *kinfo = &hdev->vport[0].nic.kinfo; + struct hclge_desc desc[HCLGE_DSCP_MAP_TC_BD_NUM]; + u8 *req0 = (u8 *)desc[0].data; + u8 *req1 = (u8 *)desc[1].data; + u8 dscp_tc[HNAE3_MAX_DSCP]; + int pos, ret; + u8 i, j; + + pos = scnprintf(buf, len, "tc map mode: %s\n", + tc_map_mode_str[kinfo->tc_map_mode]); + + if (kinfo->tc_map_mode != HNAE3_TC_MAP_MODE_DSCP) + return 0; + + hclge_cmd_setup_basic_desc(&desc[0], HCLGE_OPC_QOS_MAP, true); + desc[0].flag |= cpu_to_le16(HCLGE_COMM_CMD_FLAG_NEXT); + hclge_cmd_setup_basic_desc(&desc[1], HCLGE_OPC_QOS_MAP, true); + ret = hclge_cmd_send(&hdev->hw, desc, HCLGE_DSCP_MAP_TC_BD_NUM); + if (ret) { + dev_err(&hdev->pdev->dev, + "failed to dump qos dscp map, ret = %d\n", ret); + return ret; + } + + pos += scnprintf(buf + pos, len - pos, "\nDSCP PRIO TC\n"); + + /* The low 32 dscp setting use bd0, high 32 dscp setting use bd1 */ + for (i = 0; i < HNAE3_MAX_DSCP / HCLGE_DSCP_MAP_TC_BD_NUM; i++) { + j = i + HNAE3_MAX_DSCP / HCLGE_DSCP_MAP_TC_BD_NUM; + /* Each dscp setting has 4 bits, so each byte saves two dscp + * setting + */ + dscp_tc[i] = req0[i >> 1] >> HCLGE_DSCP_TC_SHIFT(i); + dscp_tc[j] = req1[i >> 1] >> HCLGE_DSCP_TC_SHIFT(i); + dscp_tc[i] &= HCLGE_DBG_TC_MASK; + dscp_tc[j] &= HCLGE_DBG_TC_MASK; + } + + for (i = 0; i < HNAE3_MAX_DSCP; i++) { + if (kinfo->dscp_prio[i] == HNAE3_PRIO_ID_INVALID) + continue; + + pos += scnprintf(buf + pos, len - pos, " %2u %u %u\n", + i, kinfo->dscp_prio[i], dscp_tc[i]); + } + + return 0; +} + static int hclge_dbg_dump_tx_buf_cfg(struct hclge_dev *hdev, char *buf, int len) { struct hclge_tx_buff_alloc_cmd *tx_buf_cmd; @@ -1517,7 +1572,7 @@ static int hclge_dbg_dump_fd_tcam(struct hclge_dev *hdev, char *buf, int len) char *tcam_buf; int pos = 0; - if (!hnae3_dev_fd_supported(hdev)) { + if (!hnae3_ae_dev_fd_supported(hdev->ae_dev)) { dev_err(&hdev->pdev->dev, "Only FD-supported dev supports dump fd tcam\n"); return -EOPNOTSUPP; @@ -1585,6 +1640,9 @@ static int hclge_dbg_dump_fd_counter(struct hclge_dev *hdev, char *buf, int len) u64 cnt; u8 i; + if (!hnae3_ae_dev_fd_supported(hdev->ae_dev)) + return -EOPNOTSUPP; + pos += scnprintf(buf + pos, len - pos, "func_id\thit_times\n"); @@ -2374,6 +2432,10 @@ static const struct hclge_dbg_func hclge_dbg_cmd_func[] = { .dbg_dump = hclge_dbg_dump_qos_pri_map, }, { + .cmd = HNAE3_DBG_CMD_QOS_DSCP_MAP, + .dbg_dump = hclge_dbg_dump_qos_dscp_map, + }, + { .cmd = HNAE3_DBG_CMD_QOS_BUF_CFG, .dbg_dump = hclge_dbg_dump_qos_buf_cfg, }, diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c index fae79764dc44..6962a9d69cf8 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c @@ -71,6 +71,7 @@ static void hclge_sync_mac_table(struct hclge_dev *hdev); static void hclge_restore_hw_table(struct hclge_dev *hdev); static void hclge_sync_promisc_mode(struct hclge_dev *hdev); static void hclge_sync_fd_table(struct hclge_dev *hdev); +static void hclge_update_fec_stats(struct hclge_dev *hdev); static struct hnae3_ae_algo ae_algo; @@ -148,10 +149,11 @@ static const u32 tqp_intr_reg_addr_list[] = {HCLGE_TQP_INTR_CTRL_REG, HCLGE_TQP_INTR_RL_REG}; static const char hns3_nic_test_strs[][ETH_GSTRING_LEN] = { - "App Loopback test", - "Serdes serial Loopback test", - "Serdes parallel Loopback test", - "Phy Loopback test" + "External Loopback test", + "App Loopback test", + "Serdes serial Loopback test", + "Serdes parallel Loopback test", + "Phy Loopback test" }; static const struct hclge_comm_stats_str g_mac_stats_string[] = { @@ -679,6 +681,8 @@ static void hclge_update_stats_for_all(struct hclge_dev *hdev) } } + hclge_update_fec_stats(hdev); + status = hclge_mac_update_stats(hdev); if (status) dev_err(&hdev->pdev->dev, @@ -715,7 +719,8 @@ static int hclge_get_sset_count(struct hnae3_handle *handle, int stringset) #define HCLGE_LOOPBACK_TEST_FLAGS (HNAE3_SUPPORT_APP_LOOPBACK | \ HNAE3_SUPPORT_PHY_LOOPBACK | \ HNAE3_SUPPORT_SERDES_SERIAL_LOOPBACK | \ - HNAE3_SUPPORT_SERDES_PARALLEL_LOOPBACK) + HNAE3_SUPPORT_SERDES_PARALLEL_LOOPBACK | \ + HNAE3_SUPPORT_EXTERNAL_LOOPBACK) struct hclge_vport *vport = hclge_get_vport(handle); struct hclge_dev *hdev = vport->back; @@ -737,9 +742,12 @@ static int hclge_get_sset_count(struct hnae3_handle *handle, int stringset) handle->flags |= HNAE3_SUPPORT_APP_LOOPBACK; } - count += 2; + count += 1; handle->flags |= HNAE3_SUPPORT_SERDES_SERIAL_LOOPBACK; + count += 1; handle->flags |= HNAE3_SUPPORT_SERDES_PARALLEL_LOOPBACK; + count += 1; + handle->flags |= HNAE3_SUPPORT_EXTERNAL_LOOPBACK; if ((hdev->hw.mac.phydev && hdev->hw.mac.phydev->drv && hdev->hw.mac.phydev->drv->set_loopback) || @@ -770,6 +778,11 @@ static void hclge_get_strings(struct hnae3_handle *handle, u32 stringset, size, p); p = hclge_comm_tqps_get_strings(handle, p); } else if (stringset == ETH_SS_TEST) { + if (handle->flags & HNAE3_SUPPORT_EXTERNAL_LOOPBACK) { + memcpy(p, hns3_nic_test_strs[HNAE3_LOOP_EXTERNAL], + ETH_GSTRING_LEN); + p += ETH_GSTRING_LEN; + } if (handle->flags & HNAE3_SUPPORT_APP_LOOPBACK) { memcpy(p, hns3_nic_test_strs[HNAE3_LOOP_APP], ETH_GSTRING_LEN); @@ -1003,6 +1016,27 @@ static int hclge_check_port_speed(struct hnae3_handle *handle, u32 speed) return -EINVAL; } +static void hclge_update_fec_support(struct hclge_mac *mac) +{ + linkmode_clear_bit(ETHTOOL_LINK_MODE_FEC_BASER_BIT, mac->supported); + linkmode_clear_bit(ETHTOOL_LINK_MODE_FEC_RS_BIT, mac->supported); + linkmode_clear_bit(ETHTOOL_LINK_MODE_FEC_LLRS_BIT, mac->supported); + linkmode_clear_bit(ETHTOOL_LINK_MODE_FEC_NONE_BIT, mac->supported); + + if (mac->fec_ability & BIT(HNAE3_FEC_BASER)) + linkmode_set_bit(ETHTOOL_LINK_MODE_FEC_BASER_BIT, + mac->supported); + if (mac->fec_ability & BIT(HNAE3_FEC_RS)) + linkmode_set_bit(ETHTOOL_LINK_MODE_FEC_RS_BIT, + mac->supported); + if (mac->fec_ability & BIT(HNAE3_FEC_LLRS)) + linkmode_set_bit(ETHTOOL_LINK_MODE_FEC_LLRS_BIT, + mac->supported); + if (mac->fec_ability & BIT(HNAE3_FEC_NONE)) + linkmode_set_bit(ETHTOOL_LINK_MODE_FEC_NONE_BIT, + mac->supported); +} + static void hclge_convert_setting_sr(u16 speed_ability, unsigned long *link_mode) { @@ -1101,34 +1135,36 @@ static void hclge_convert_setting_kr(u16 speed_ability, static void hclge_convert_setting_fec(struct hclge_mac *mac) { - linkmode_clear_bit(ETHTOOL_LINK_MODE_FEC_BASER_BIT, mac->supported); - linkmode_clear_bit(ETHTOOL_LINK_MODE_FEC_RS_BIT, mac->supported); + /* If firmware has reported fec_ability, don't need to convert by speed */ + if (mac->fec_ability) + goto out; switch (mac->speed) { case HCLGE_MAC_SPEED_10G: case HCLGE_MAC_SPEED_40G: - linkmode_set_bit(ETHTOOL_LINK_MODE_FEC_BASER_BIT, - mac->supported); - mac->fec_ability = - BIT(HNAE3_FEC_BASER) | BIT(HNAE3_FEC_AUTO); + mac->fec_ability = BIT(HNAE3_FEC_BASER) | BIT(HNAE3_FEC_AUTO) | + BIT(HNAE3_FEC_NONE); break; case HCLGE_MAC_SPEED_25G: case HCLGE_MAC_SPEED_50G: - linkmode_set_bit(ETHTOOL_LINK_MODE_FEC_RS_BIT, - mac->supported); - mac->fec_ability = - BIT(HNAE3_FEC_BASER) | BIT(HNAE3_FEC_RS) | - BIT(HNAE3_FEC_AUTO); + mac->fec_ability = BIT(HNAE3_FEC_BASER) | BIT(HNAE3_FEC_RS) | + BIT(HNAE3_FEC_AUTO) | BIT(HNAE3_FEC_NONE); break; case HCLGE_MAC_SPEED_100G: + mac->fec_ability = BIT(HNAE3_FEC_RS) | BIT(HNAE3_FEC_AUTO) | + BIT(HNAE3_FEC_NONE); + break; case HCLGE_MAC_SPEED_200G: - linkmode_set_bit(ETHTOOL_LINK_MODE_FEC_RS_BIT, mac->supported); - mac->fec_ability = BIT(HNAE3_FEC_RS) | BIT(HNAE3_FEC_AUTO); + mac->fec_ability = BIT(HNAE3_FEC_RS) | BIT(HNAE3_FEC_AUTO) | + BIT(HNAE3_FEC_LLRS); break; default: mac->fec_ability = 0; break; } + +out: + hclge_update_fec_support(mac); } static void hclge_parse_fiber_link_mode(struct hclge_dev *hdev, @@ -1574,7 +1610,7 @@ static int hclge_configure(struct hclge_dev *hdev) if (cfg.vlan_fliter_cap == HCLGE_VLAN_FLTR_CAN_MDF) set_bit(HNAE3_DEV_SUPPORT_VLAN_FLTR_MDF_B, ae_dev->caps); - if (hnae3_dev_fd_supported(hdev)) { + if (hnae3_ae_dev_fd_supported(hdev->ae_dev)) { hdev->fd_en = true; hdev->fd_active_type = HCLGE_FD_RULE_NONE; } @@ -1617,7 +1653,7 @@ static int hclge_config_gro(struct hclge_dev *hdev) struct hclge_desc desc; int ret; - if (!hnae3_dev_gro_supported(hdev)) + if (!hnae3_ae_dev_gro_supported(hdev->ae_dev)) return 0; hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_GRO_GENERIC_CONFIG, false); @@ -2589,7 +2625,7 @@ static int hclge_convert_to_fw_speed(u32 speed_drv, u32 *speed_fw) } static int hclge_cfg_mac_speed_dup_hw(struct hclge_dev *hdev, int speed, - u8 duplex) + u8 duplex, u8 lane_num) { struct hclge_config_mac_speed_dup_cmd *req; struct hclge_desc desc; @@ -2613,6 +2649,7 @@ static int hclge_cfg_mac_speed_dup_hw(struct hclge_dev *hdev, int speed, speed_fw); hnae3_set_bit(req->mac_change_fec_en, HCLGE_CFG_MAC_SPEED_CHANGE_EN_B, 1); + req->lane_num = lane_num; ret = hclge_cmd_send(&hdev->hw, &desc, 1); if (ret) { @@ -2624,33 +2661,35 @@ static int hclge_cfg_mac_speed_dup_hw(struct hclge_dev *hdev, int speed, return 0; } -int hclge_cfg_mac_speed_dup(struct hclge_dev *hdev, int speed, u8 duplex) +int hclge_cfg_mac_speed_dup(struct hclge_dev *hdev, int speed, u8 duplex, u8 lane_num) { struct hclge_mac *mac = &hdev->hw.mac; int ret; duplex = hclge_check_speed_dup(duplex, speed); if (!mac->support_autoneg && mac->speed == speed && - mac->duplex == duplex) + mac->duplex == duplex && (mac->lane_num == lane_num || lane_num == 0)) return 0; - ret = hclge_cfg_mac_speed_dup_hw(hdev, speed, duplex); + ret = hclge_cfg_mac_speed_dup_hw(hdev, speed, duplex, lane_num); if (ret) return ret; hdev->hw.mac.speed = speed; hdev->hw.mac.duplex = duplex; + if (!lane_num) + hdev->hw.mac.lane_num = lane_num; return 0; } static int hclge_cfg_mac_speed_dup_h(struct hnae3_handle *handle, int speed, - u8 duplex) + u8 duplex, u8 lane_num) { struct hclge_vport *vport = hclge_get_vport(handle); struct hclge_dev *hdev = vport->back; - return hclge_cfg_mac_speed_dup(hdev, speed, duplex); + return hclge_cfg_mac_speed_dup(hdev, speed, duplex, lane_num); } static int hclge_set_autoneg_en(struct hclge_dev *hdev, bool enable) @@ -2730,6 +2769,157 @@ static int hclge_halt_autoneg(struct hnae3_handle *handle, bool halt) return 0; } +static void hclge_parse_fec_stats_lanes(struct hclge_dev *hdev, + struct hclge_desc *desc, u32 desc_len) +{ + u32 lane_size = HCLGE_FEC_STATS_MAX_LANES * 2; + u32 desc_index = 0; + u32 data_index = 0; + u32 i; + + for (i = 0; i < lane_size; i++) { + if (data_index >= HCLGE_DESC_DATA_LEN) { + desc_index++; + data_index = 0; + } + + if (desc_index >= desc_len) + return; + + hdev->fec_stats.per_lanes[i] += + le32_to_cpu(desc[desc_index].data[data_index]); + data_index++; + } +} + +static void hclge_parse_fec_stats(struct hclge_dev *hdev, + struct hclge_desc *desc, u32 desc_len) +{ + struct hclge_query_fec_stats_cmd *req; + + req = (struct hclge_query_fec_stats_cmd *)desc[0].data; + + hdev->fec_stats.base_r_lane_num = req->base_r_lane_num; + hdev->fec_stats.rs_corr_blocks += + le32_to_cpu(req->rs_fec_corr_blocks); + hdev->fec_stats.rs_uncorr_blocks += + le32_to_cpu(req->rs_fec_uncorr_blocks); + hdev->fec_stats.rs_error_blocks += + le32_to_cpu(req->rs_fec_error_blocks); + hdev->fec_stats.base_r_corr_blocks += + le32_to_cpu(req->base_r_fec_corr_blocks); + hdev->fec_stats.base_r_uncorr_blocks += + le32_to_cpu(req->base_r_fec_uncorr_blocks); + + hclge_parse_fec_stats_lanes(hdev, &desc[1], desc_len - 1); +} + +static int hclge_update_fec_stats_hw(struct hclge_dev *hdev) +{ + struct hclge_desc desc[HCLGE_FEC_STATS_CMD_NUM]; + int ret; + u32 i; + + for (i = 0; i < HCLGE_FEC_STATS_CMD_NUM; i++) { + hclge_cmd_setup_basic_desc(&desc[i], HCLGE_OPC_QUERY_FEC_STATS, + true); + if (i != (HCLGE_FEC_STATS_CMD_NUM - 1)) + desc[i].flag |= cpu_to_le16(HCLGE_COMM_CMD_FLAG_NEXT); + } + + ret = hclge_cmd_send(&hdev->hw, desc, HCLGE_FEC_STATS_CMD_NUM); + if (ret) + return ret; + + hclge_parse_fec_stats(hdev, desc, HCLGE_FEC_STATS_CMD_NUM); + + return 0; +} + +static void hclge_update_fec_stats(struct hclge_dev *hdev) +{ + struct hnae3_ae_dev *ae_dev = pci_get_drvdata(hdev->pdev); + int ret; + + if (!hnae3_ae_dev_fec_stats_supported(ae_dev) || + test_and_set_bit(HCLGE_STATE_FEC_STATS_UPDATING, &hdev->state)) + return; + + ret = hclge_update_fec_stats_hw(hdev); + if (ret) + dev_err(&hdev->pdev->dev, + "failed to update fec stats, ret = %d\n", ret); + + clear_bit(HCLGE_STATE_FEC_STATS_UPDATING, &hdev->state); +} + +static void hclge_get_fec_stats_total(struct hclge_dev *hdev, + struct ethtool_fec_stats *fec_stats) +{ + fec_stats->corrected_blocks.total = hdev->fec_stats.rs_corr_blocks; + fec_stats->uncorrectable_blocks.total = + hdev->fec_stats.rs_uncorr_blocks; +} + +static void hclge_get_fec_stats_lanes(struct hclge_dev *hdev, + struct ethtool_fec_stats *fec_stats) +{ + u32 i; + + if (hdev->fec_stats.base_r_lane_num == 0 || + hdev->fec_stats.base_r_lane_num > HCLGE_FEC_STATS_MAX_LANES) { + dev_err(&hdev->pdev->dev, + "fec stats lane number(%llu) is invalid\n", + hdev->fec_stats.base_r_lane_num); + return; + } + + for (i = 0; i < hdev->fec_stats.base_r_lane_num; i++) { + fec_stats->corrected_blocks.lanes[i] = + hdev->fec_stats.base_r_corr_per_lanes[i]; + fec_stats->uncorrectable_blocks.lanes[i] = + hdev->fec_stats.base_r_uncorr_per_lanes[i]; + } +} + +static void hclge_comm_get_fec_stats(struct hclge_dev *hdev, + struct ethtool_fec_stats *fec_stats) +{ + u32 fec_mode = hdev->hw.mac.fec_mode; + + switch (fec_mode) { + case BIT(HNAE3_FEC_RS): + case BIT(HNAE3_FEC_LLRS): + hclge_get_fec_stats_total(hdev, fec_stats); + break; + case BIT(HNAE3_FEC_BASER): + hclge_get_fec_stats_lanes(hdev, fec_stats); + break; + default: + dev_err(&hdev->pdev->dev, + "fec stats is not supported by current fec mode(0x%x)\n", + fec_mode); + break; + } +} + +static void hclge_get_fec_stats(struct hnae3_handle *handle, + struct ethtool_fec_stats *fec_stats) +{ + struct hclge_vport *vport = hclge_get_vport(handle); + struct hclge_dev *hdev = vport->back; + u32 fec_mode = hdev->hw.mac.fec_mode; + + if (fec_mode == BIT(HNAE3_FEC_NONE) || + fec_mode == BIT(HNAE3_FEC_AUTO) || + fec_mode == BIT(HNAE3_FEC_USER_DEF)) + return; + + hclge_update_fec_stats(hdev); + + hclge_comm_get_fec_stats(hdev, fec_stats); +} + static int hclge_set_fec_hw(struct hclge_dev *hdev, u32 fec_mode) { struct hclge_config_fec_cmd *req; @@ -2744,6 +2934,9 @@ static int hclge_set_fec_hw(struct hclge_dev *hdev, u32 fec_mode) if (fec_mode & BIT(HNAE3_FEC_RS)) hnae3_set_field(req->fec_mode, HCLGE_MAC_CFG_FEC_MODE_M, HCLGE_MAC_CFG_FEC_MODE_S, HCLGE_MAC_FEC_RS); + if (fec_mode & BIT(HNAE3_FEC_LLRS)) + hnae3_set_field(req->fec_mode, HCLGE_MAC_CFG_FEC_MODE_M, + HCLGE_MAC_CFG_FEC_MODE_S, HCLGE_MAC_FEC_LLRS); if (fec_mode & BIT(HNAE3_FEC_BASER)) hnae3_set_field(req->fec_mode, HCLGE_MAC_CFG_FEC_MODE_M, HCLGE_MAC_CFG_FEC_MODE_S, HCLGE_MAC_FEC_BASER); @@ -2796,7 +2989,7 @@ static int hclge_mac_init(struct hclge_dev *hdev) hdev->support_sfp_query = true; hdev->hw.mac.duplex = HCLGE_MAC_FULL; ret = hclge_cfg_mac_speed_dup_hw(hdev, hdev->hw.mac.speed, - hdev->hw.mac.duplex); + hdev->hw.mac.duplex, hdev->hw.mac.lane_num); if (ret) return ret; @@ -2988,6 +3181,9 @@ static void hclge_update_fec_advertising(struct hclge_mac *mac) if (mac->fec_mode & BIT(HNAE3_FEC_RS)) linkmode_set_bit(ETHTOOL_LINK_MODE_FEC_RS_BIT, mac->advertising); + else if (mac->fec_mode & BIT(HNAE3_FEC_LLRS)) + linkmode_set_bit(ETHTOOL_LINK_MODE_FEC_LLRS_BIT, + mac->advertising); else if (mac->fec_mode & BIT(HNAE3_FEC_BASER)) linkmode_set_bit(ETHTOOL_LINK_MODE_FEC_BASER_BIT, mac->advertising); @@ -3037,7 +3233,6 @@ static void hclge_update_port_capability(struct hclge_dev *hdev, struct hclge_mac *mac) { if (hnae3_dev_fec_supported(hdev)) - /* update fec ability by speed */ hclge_convert_setting_fec(mac); /* firmware can not identify back plane type, the media type @@ -3119,10 +3314,12 @@ static int hclge_get_sfp_info(struct hclge_dev *hdev, struct hclge_mac *mac) mac->autoneg = resp->autoneg; mac->support_autoneg = resp->autoneg_ability; mac->speed_type = QUERY_ACTIVE_SPEED; + mac->lane_num = resp->lane_num; if (!resp->active_fec) mac->fec_mode = 0; else mac->fec_mode = BIT(resp->active_fec); + mac->fec_ability = resp->fec_ability; } else { mac->speed_type = QUERY_SFP_SPEED; } @@ -3302,13 +3499,13 @@ static int hclge_update_port_info(struct hclge_dev *hdev) return 0; } return hclge_cfg_mac_speed_dup(hdev, mac->speed, - HCLGE_MAC_FULL); + HCLGE_MAC_FULL, mac->lane_num); } else { if (speed == HCLGE_MAC_SPEED_UNKNOWN) return 0; /* do nothing if no SFP */ /* must config full duplex for SFP */ - return hclge_cfg_mac_speed_dup(hdev, speed, HCLGE_MAC_FULL); + return hclge_cfg_mac_speed_dup(hdev, speed, HCLGE_MAC_FULL, 0); } } @@ -5334,7 +5531,7 @@ static int hclge_init_fd_config(struct hclge_dev *hdev) struct hclge_fd_key_cfg *key_cfg; int ret; - if (!hnae3_dev_fd_supported(hdev)) + if (!hnae3_ae_dev_fd_supported(hdev->ae_dev)) return 0; ret = hclge_get_fd_mode(hdev, &hdev->fd_cfg.fd_mode); @@ -6339,7 +6536,7 @@ static int hclge_add_fd_entry(struct hnae3_handle *handle, u8 action; int ret; - if (!hnae3_dev_fd_supported(hdev)) { + if (!hnae3_ae_dev_fd_supported(hdev->ae_dev)) { dev_err(&hdev->pdev->dev, "flow table director is not supported\n"); return -EOPNOTSUPP; @@ -6395,7 +6592,7 @@ static int hclge_del_fd_entry(struct hnae3_handle *handle, struct ethtool_rx_flow_spec *fs; int ret; - if (!hnae3_dev_fd_supported(hdev)) + if (!hnae3_ae_dev_fd_supported(hdev->ae_dev)) return -EOPNOTSUPP; fs = (struct ethtool_rx_flow_spec *)&cmd->fs; @@ -6431,9 +6628,6 @@ static void hclge_clear_fd_rules_in_list(struct hclge_dev *hdev, struct hlist_node *node; u16 location; - if (!hnae3_dev_fd_supported(hdev)) - return; - spin_lock_bh(&hdev->fd_rule_lock); for_each_set_bit(location, hdev->fd_bmap, @@ -6458,6 +6652,9 @@ static void hclge_clear_fd_rules_in_list(struct hclge_dev *hdev, static void hclge_del_all_fd_entries(struct hclge_dev *hdev) { + if (!hnae3_ae_dev_fd_supported(hdev->ae_dev)) + return; + hclge_clear_fd_rules_in_list(hdev, true); hclge_fd_disable_user_def(hdev); } @@ -6473,7 +6670,7 @@ static int hclge_restore_fd_entries(struct hnae3_handle *handle) * return value. If error is returned here, the reset process will * fail. */ - if (!hnae3_dev_fd_supported(hdev)) + if (!hnae3_ae_dev_fd_supported(hdev->ae_dev)) return 0; /* if fd is disabled, should not restore it when reset */ @@ -6497,7 +6694,7 @@ static int hclge_get_fd_rule_cnt(struct hnae3_handle *handle, struct hclge_vport *vport = hclge_get_vport(handle); struct hclge_dev *hdev = vport->back; - if (!hnae3_dev_fd_supported(hdev) || hclge_is_cls_flower_active(handle)) + if (!hnae3_ae_dev_fd_supported(hdev->ae_dev) || hclge_is_cls_flower_active(handle)) return -EOPNOTSUPP; cmd->rule_cnt = hdev->hclge_fd_rule_num; @@ -6715,7 +6912,7 @@ static int hclge_get_fd_rule_info(struct hnae3_handle *handle, struct hclge_dev *hdev = vport->back; struct ethtool_rx_flow_spec *fs; - if (!hnae3_dev_fd_supported(hdev)) + if (!hnae3_ae_dev_fd_supported(hdev->ae_dev)) return -EOPNOTSUPP; fs = (struct ethtool_rx_flow_spec *)&cmd->fs; @@ -6778,7 +6975,7 @@ static int hclge_get_all_rules(struct hnae3_handle *handle, struct hlist_node *node2; int cnt = 0; - if (!hnae3_dev_fd_supported(hdev)) + if (!hnae3_ae_dev_fd_supported(hdev->ae_dev)) return -EOPNOTSUPP; cmd->data = hdev->fd_cfg.rule_num[HCLGE_FD_STAGE_1]; @@ -6878,7 +7075,7 @@ static int hclge_add_fd_entry_by_arfs(struct hnae3_handle *handle, u16 queue_id, struct hclge_fd_rule *rule; u16 bit_id; - if (!hnae3_dev_fd_supported(hdev)) + if (!hnae3_ae_dev_fd_supported(hdev->ae_dev)) return -EOPNOTSUPP; /* when there is already fd rule existed add by user, @@ -7167,6 +7364,12 @@ static int hclge_add_cls_flower(struct hnae3_handle *handle, struct hclge_fd_rule *rule; int ret; + if (!hnae3_ae_dev_fd_supported(hdev->ae_dev)) { + dev_err(&hdev->pdev->dev, + "cls flower is not supported\n"); + return -EOPNOTSUPP; + } + ret = hclge_check_cls_flower(hdev, cls_flower, tc); if (ret) { dev_err(&hdev->pdev->dev, @@ -7220,6 +7423,9 @@ static int hclge_del_cls_flower(struct hnae3_handle *handle, struct hclge_fd_rule *rule; int ret; + if (!hnae3_ae_dev_fd_supported(hdev->ae_dev)) + return -EOPNOTSUPP; + spin_lock_bh(&hdev->fd_rule_lock); rule = hclge_find_cls_flower(hdev, cls_flower->cookie); @@ -7282,6 +7488,9 @@ out: static void hclge_sync_fd_table(struct hclge_dev *hdev) { + if (!hnae3_ae_dev_fd_supported(hdev->ae_dev)) + return; + if (test_and_clear_bit(HCLGE_STATE_FD_CLEAR_ALL, &hdev->state)) { bool clear_list = hdev->fd_active_type == HCLGE_FD_ARFS_ACTIVE; @@ -7705,7 +7914,7 @@ static int hclge_set_loopback(struct hnae3_handle *handle, { struct hclge_vport *vport = hclge_get_vport(handle); struct hclge_dev *hdev = vport->back; - int ret; + int ret = 0; /* Loopback can be enabled in three places: SSU, MAC, and serdes. By * default, SSU loopback is enabled, so if the SMAC and the DMAC are @@ -7732,6 +7941,8 @@ static int hclge_set_loopback(struct hnae3_handle *handle, case HNAE3_LOOP_PHY: ret = hclge_set_phy_loopback(hdev, en); break; + case HNAE3_LOOP_EXTERNAL: + break; default: ret = -ENOTSUPP; dev_err(&hdev->pdev->dev, @@ -10793,7 +11004,7 @@ static int hclge_set_pauseparam(struct hnae3_handle *handle, u32 auto_neg, } static void hclge_get_ksettings_an_result(struct hnae3_handle *handle, - u8 *auto_neg, u32 *speed, u8 *duplex) + u8 *auto_neg, u32 *speed, u8 *duplex, u32 *lane_num) { struct hclge_vport *vport = hclge_get_vport(handle); struct hclge_dev *hdev = vport->back; @@ -10804,6 +11015,8 @@ static void hclge_get_ksettings_an_result(struct hnae3_handle *handle, *duplex = hdev->hw.mac.duplex; if (auto_neg) *auto_neg = hdev->hw.mac.autoneg; + if (lane_num) + *lane_num = hdev->hw.mac.lane_num; } static void hclge_get_media_type(struct hnae3_handle *handle, u8 *media_type, @@ -11443,6 +11656,10 @@ static int hclge_init_ae_dev(struct hnae3_ae_dev *ae_dev) if (ret) goto err_mdiobus_unreg; + ret = hclge_update_port_info(hdev); + if (ret) + goto err_mdiobus_unreg; + INIT_KFIFO(hdev->mac_tnl_log); hclge_dcb_ops_set(hdev); @@ -11510,6 +11727,7 @@ out: static void hclge_stats_clear(struct hclge_dev *hdev) { memset(&hdev->mac_stats, 0, sizeof(hdev->mac_stats)); + memset(&hdev->fec_stats, 0, sizeof(hdev->fec_stats)); } static int hclge_set_mac_spoofchk(struct hclge_dev *hdev, int vf, bool enable) @@ -12763,6 +12981,21 @@ static void hclge_clean_vport_config(struct hnae3_ae_dev *ae_dev, int num_vfs) } } +static int hclge_get_dscp_prio(struct hnae3_handle *h, u8 dscp, u8 *tc_mode, + u8 *priority) +{ + if (dscp >= HNAE3_MAX_DSCP) + return -EINVAL; + + if (tc_mode) + *tc_mode = h->kinfo.tc_map_mode; + if (priority) + *priority = h->kinfo.dscp_prio[dscp] == HNAE3_PRIO_ID_INVALID ? 0 : + h->kinfo.dscp_prio[dscp]; + + return 0; +} + static const struct hnae3_ae_ops hclge_ops = { .init_ae_dev = hclge_init_ae_dev, .uninit_ae_dev = hclge_uninit_ae_dev, @@ -12786,6 +13019,7 @@ static const struct hnae3_ae_ops hclge_ops = { .cfg_mac_speed_dup_h = hclge_cfg_mac_speed_dup_h, .get_media_type = hclge_get_media_type, .check_port_speed = hclge_check_port_speed, + .get_fec_stats = hclge_get_fec_stats, .get_fec = hclge_get_fec, .set_fec = hclge_set_fec, .get_rss_key_size = hclge_comm_get_rss_key_size, @@ -12865,6 +13099,7 @@ static const struct hnae3_ae_ops hclge_ops = { .get_ts_info = hclge_ptp_get_ts_info, .get_link_diagnosis_info = hclge_get_link_diagnosis_info, .clean_vf_config = hclge_clean_vport_config, + .get_dscp_prio = hclge_get_dscp_prio, }; static struct hnae3_ae_algo ae_algo = { @@ -12872,7 +13107,7 @@ static struct hnae3_ae_algo ae_algo = { .pdev_id_table = ae_algo_pci_tbl, }; -static int hclge_init(void) +static int __init hclge_init(void) { pr_info("%s is initializing\n", HCLGE_NAME); @@ -12887,7 +13122,7 @@ static int hclge_init(void) return 0; } -static void hclge_exit(void) +static void __exit hclge_exit(void) { hnae3_unregister_ae_algo_prepare(&ae_algo); hnae3_unregister_ae_algo(&ae_algo); diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h index 18caddd541f8..495b639b0dc2 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h @@ -216,6 +216,7 @@ enum HCLGE_DEV_STATE { HCLGE_STATE_FD_USER_DEF_CHANGED, HCLGE_STATE_PTP_EN, HCLGE_STATE_PTP_TX_HANDLING, + HCLGE_STATE_FEC_STATS_UPDATING, HCLGE_STATE_MAX }; @@ -258,6 +259,7 @@ struct hclge_mac { u8 duplex; u8 support_autoneg; u8 speed_type; /* 0: sfp speed, 1: active speed */ + u8 lane_num; u32 speed; u32 max_speed; u32 speed_ability; /* speed ability supported by current media */ @@ -488,6 +490,26 @@ struct hclge_mac_stats { #define HCLGE_STATS_TIMER_INTERVAL 300UL +/* fec stats ,opcode id: 0x0316 */ +#define HCLGE_FEC_STATS_MAX_LANES 8 +struct hclge_fec_stats { + /* fec rs mode total stats */ + u64 rs_corr_blocks; + u64 rs_uncorr_blocks; + u64 rs_error_blocks; + /* fec base-r mode per lanes stats */ + u64 base_r_lane_num; + u64 base_r_corr_blocks; + u64 base_r_uncorr_blocks; + union { + struct { + u64 base_r_corr_per_lanes[HCLGE_FEC_STATS_MAX_LANES]; + u64 base_r_uncorr_per_lanes[HCLGE_FEC_STATS_MAX_LANES]; + }; + u64 per_lanes[HCLGE_FEC_STATS_MAX_LANES * 2]; + }; +}; + struct hclge_vlan_type_cfg { u16 rx_ot_fst_vlan_type; u16 rx_ot_sec_vlan_type; @@ -826,6 +848,7 @@ struct hclge_dev { struct hclge_hw hw; struct hclge_misc_vector misc_vector; struct hclge_mac_stats mac_stats; + struct hclge_fec_stats fec_stats; unsigned long state; unsigned long flr_state; unsigned long last_reset_time; @@ -1070,7 +1093,7 @@ static inline int hclge_get_queue_id(struct hnae3_queue *queue) } int hclge_inform_reset_assert_to_vf(struct hclge_vport *vport); -int hclge_cfg_mac_speed_dup(struct hclge_dev *hdev, int speed, u8 duplex); +int hclge_cfg_mac_speed_dup(struct hclge_dev *hdev, int speed, u8 duplex, u8 lane_num); int hclge_set_vlan_filter(struct hnae3_handle *handle, __be16 proto, u16 vlan_id, bool is_kill); int hclge_en_hw_strip_rxvtag(struct hnae3_handle *handle, bool enable); diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_mbx.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_mbx.c index e1012f7f9b73..a7b06c63143c 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_mbx.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_mbx.c @@ -779,17 +779,284 @@ static void hclge_handle_vf_tbl(struct hclge_vport *vport, } } +static int +hclge_mbx_map_ring_to_vector_handler(struct hclge_mbx_ops_param *param) +{ + return hclge_map_unmap_ring_to_vf_vector(param->vport, true, + param->req); +} + +static int +hclge_mbx_unmap_ring_to_vector_handler(struct hclge_mbx_ops_param *param) +{ + return hclge_map_unmap_ring_to_vf_vector(param->vport, false, + param->req); +} + +static int +hclge_mbx_get_ring_vector_map_handler(struct hclge_mbx_ops_param *param) +{ + int ret; + + ret = hclge_get_vf_ring_vector_map(param->vport, param->req, + param->resp_msg); + if (ret) + dev_err(¶m->vport->back->pdev->dev, + "PF fail(%d) to get VF ring vector map\n", + ret); + return ret; +} + +static int hclge_mbx_set_promisc_mode_handler(struct hclge_mbx_ops_param *param) +{ + hclge_set_vf_promisc_mode(param->vport, param->req); + return 0; +} + +static int hclge_mbx_set_unicast_handler(struct hclge_mbx_ops_param *param) +{ + int ret; + + ret = hclge_set_vf_uc_mac_addr(param->vport, param->req); + if (ret) + dev_err(¶m->vport->back->pdev->dev, + "PF fail(%d) to set VF UC MAC Addr\n", + ret); + return ret; +} + +static int hclge_mbx_set_multicast_handler(struct hclge_mbx_ops_param *param) +{ + int ret; + + ret = hclge_set_vf_mc_mac_addr(param->vport, param->req); + if (ret) + dev_err(¶m->vport->back->pdev->dev, + "PF fail(%d) to set VF MC MAC Addr\n", + ret); + return ret; +} + +static int hclge_mbx_set_vlan_handler(struct hclge_mbx_ops_param *param) +{ + int ret; + + ret = hclge_set_vf_vlan_cfg(param->vport, param->req, param->resp_msg); + if (ret) + dev_err(¶m->vport->back->pdev->dev, + "PF failed(%d) to config VF's VLAN\n", + ret); + return ret; +} + +static int hclge_mbx_set_alive_handler(struct hclge_mbx_ops_param *param) +{ + int ret; + + ret = hclge_set_vf_alive(param->vport, param->req); + if (ret) + dev_err(¶m->vport->back->pdev->dev, + "PF failed(%d) to set VF's ALIVE\n", + ret); + return ret; +} + +static int hclge_mbx_get_qinfo_handler(struct hclge_mbx_ops_param *param) +{ + hclge_get_vf_queue_info(param->vport, param->resp_msg); + return 0; +} + +static int hclge_mbx_get_qdepth_handler(struct hclge_mbx_ops_param *param) +{ + hclge_get_vf_queue_depth(param->vport, param->resp_msg); + return 0; +} + +static int hclge_mbx_get_basic_info_handler(struct hclge_mbx_ops_param *param) +{ + hclge_get_basic_info(param->vport, param->resp_msg); + return 0; +} + +static int hclge_mbx_get_link_status_handler(struct hclge_mbx_ops_param *param) +{ + int ret; + + ret = hclge_push_vf_link_status(param->vport); + if (ret) + dev_err(¶m->vport->back->pdev->dev, + "failed to inform link stat to VF, ret = %d\n", + ret); + return ret; +} + +static int hclge_mbx_queue_reset_handler(struct hclge_mbx_ops_param *param) +{ + return hclge_mbx_reset_vf_queue(param->vport, param->req, + param->resp_msg); +} + +static int hclge_mbx_reset_handler(struct hclge_mbx_ops_param *param) +{ + return hclge_reset_vf(param->vport); +} + +static int hclge_mbx_keep_alive_handler(struct hclge_mbx_ops_param *param) +{ + hclge_vf_keep_alive(param->vport); + return 0; +} + +static int hclge_mbx_set_mtu_handler(struct hclge_mbx_ops_param *param) +{ + int ret; + + ret = hclge_set_vf_mtu(param->vport, param->req); + if (ret) + dev_err(¶m->vport->back->pdev->dev, + "VF fail(%d) to set mtu\n", ret); + return ret; +} + +static int hclge_mbx_get_qid_in_pf_handler(struct hclge_mbx_ops_param *param) +{ + return hclge_get_queue_id_in_pf(param->vport, param->req, + param->resp_msg); +} + +static int hclge_mbx_get_rss_key_handler(struct hclge_mbx_ops_param *param) +{ + return hclge_get_rss_key(param->vport, param->req, param->resp_msg); +} + +static int hclge_mbx_get_link_mode_handler(struct hclge_mbx_ops_param *param) +{ + hclge_get_link_mode(param->vport, param->req); + return 0; +} + +static int +hclge_mbx_get_vf_flr_status_handler(struct hclge_mbx_ops_param *param) +{ + hclge_rm_vport_all_mac_table(param->vport, false, + HCLGE_MAC_ADDR_UC); + hclge_rm_vport_all_mac_table(param->vport, false, + HCLGE_MAC_ADDR_MC); + hclge_rm_vport_all_vlan_table(param->vport, false); + return 0; +} + +static int hclge_mbx_vf_uninit_handler(struct hclge_mbx_ops_param *param) +{ + hclge_rm_vport_all_mac_table(param->vport, true, + HCLGE_MAC_ADDR_UC); + hclge_rm_vport_all_mac_table(param->vport, true, + HCLGE_MAC_ADDR_MC); + hclge_rm_vport_all_vlan_table(param->vport, true); + return 0; +} + +static int hclge_mbx_get_media_type_handler(struct hclge_mbx_ops_param *param) +{ + hclge_get_vf_media_type(param->vport, param->resp_msg); + return 0; +} + +static int hclge_mbx_push_link_status_handler(struct hclge_mbx_ops_param *param) +{ + hclge_handle_link_change_event(param->vport->back, param->req); + return 0; +} + +static int hclge_mbx_get_mac_addr_handler(struct hclge_mbx_ops_param *param) +{ + hclge_get_vf_mac_addr(param->vport, param->resp_msg); + return 0; +} + +static int hclge_mbx_ncsi_error_handler(struct hclge_mbx_ops_param *param) +{ + hclge_handle_ncsi_error(param->vport->back); + return 0; +} + +static int hclge_mbx_handle_vf_tbl_handler(struct hclge_mbx_ops_param *param) +{ + hclge_handle_vf_tbl(param->vport, param->req); + return 0; +} + +static const hclge_mbx_ops_fn hclge_mbx_ops_list[HCLGE_MBX_OPCODE_MAX] = { + [HCLGE_MBX_RESET] = hclge_mbx_reset_handler, + [HCLGE_MBX_SET_UNICAST] = hclge_mbx_set_unicast_handler, + [HCLGE_MBX_SET_MULTICAST] = hclge_mbx_set_multicast_handler, + [HCLGE_MBX_SET_VLAN] = hclge_mbx_set_vlan_handler, + [HCLGE_MBX_MAP_RING_TO_VECTOR] = hclge_mbx_map_ring_to_vector_handler, + [HCLGE_MBX_UNMAP_RING_TO_VECTOR] = hclge_mbx_unmap_ring_to_vector_handler, + [HCLGE_MBX_SET_PROMISC_MODE] = hclge_mbx_set_promisc_mode_handler, + [HCLGE_MBX_GET_QINFO] = hclge_mbx_get_qinfo_handler, + [HCLGE_MBX_GET_QDEPTH] = hclge_mbx_get_qdepth_handler, + [HCLGE_MBX_GET_BASIC_INFO] = hclge_mbx_get_basic_info_handler, + [HCLGE_MBX_GET_RSS_KEY] = hclge_mbx_get_rss_key_handler, + [HCLGE_MBX_GET_MAC_ADDR] = hclge_mbx_get_mac_addr_handler, + [HCLGE_MBX_GET_LINK_STATUS] = hclge_mbx_get_link_status_handler, + [HCLGE_MBX_QUEUE_RESET] = hclge_mbx_queue_reset_handler, + [HCLGE_MBX_KEEP_ALIVE] = hclge_mbx_keep_alive_handler, + [HCLGE_MBX_SET_ALIVE] = hclge_mbx_set_alive_handler, + [HCLGE_MBX_SET_MTU] = hclge_mbx_set_mtu_handler, + [HCLGE_MBX_GET_QID_IN_PF] = hclge_mbx_get_qid_in_pf_handler, + [HCLGE_MBX_GET_LINK_MODE] = hclge_mbx_get_link_mode_handler, + [HCLGE_MBX_GET_MEDIA_TYPE] = hclge_mbx_get_media_type_handler, + [HCLGE_MBX_VF_UNINIT] = hclge_mbx_vf_uninit_handler, + [HCLGE_MBX_HANDLE_VF_TBL] = hclge_mbx_handle_vf_tbl_handler, + [HCLGE_MBX_GET_RING_VECTOR_MAP] = hclge_mbx_get_ring_vector_map_handler, + [HCLGE_MBX_GET_VF_FLR_STATUS] = hclge_mbx_get_vf_flr_status_handler, + [HCLGE_MBX_PUSH_LINK_STATUS] = hclge_mbx_push_link_status_handler, + [HCLGE_MBX_NCSI_ERROR] = hclge_mbx_ncsi_error_handler, +}; + +static void hclge_mbx_request_handling(struct hclge_mbx_ops_param *param) +{ + hclge_mbx_ops_fn cmd_func = NULL; + struct hclge_dev *hdev; + int ret = 0; + + hdev = param->vport->back; + cmd_func = hclge_mbx_ops_list[param->req->msg.code]; + if (cmd_func) + ret = cmd_func(param); + else + dev_err(&hdev->pdev->dev, + "un-supported mailbox message, code = %u\n", + param->req->msg.code); + + /* PF driver should not reply IMP */ + if (hnae3_get_bit(param->req->mbx_need_resp, HCLGE_MBX_NEED_RESP_B) && + param->req->msg.code < HCLGE_MBX_GET_VF_FLR_STATUS) { + param->resp_msg->status = ret; + if (time_is_before_jiffies(hdev->last_mbx_scheduled + + HCLGE_MBX_SCHED_TIMEOUT)) + dev_warn(&hdev->pdev->dev, + "resp vport%u mbx(%u,%u) late\n", + param->req->mbx_src_vfid, + param->req->msg.code, + param->req->msg.subcode); + + hclge_gen_resp_to_vf(param->vport, param->req, param->resp_msg); + } +} + void hclge_mbx_handler(struct hclge_dev *hdev) { struct hclge_comm_cmq_ring *crq = &hdev->hw.hw.cmq.crq; struct hclge_respond_to_vf_msg resp_msg; struct hclge_mbx_vf_to_pf_cmd *req; - struct hclge_vport *vport; + struct hclge_mbx_ops_param param; struct hclge_desc *desc; - bool is_del = false; unsigned int flag; - int ret = 0; + param.resp_msg = &resp_msg; /* handle all the mailbox requests in the queue */ while (!hclge_cmd_crq_empty(&hdev->hw)) { if (test_bit(HCLGE_COMM_STATE_CMD_DISABLE, @@ -814,152 +1081,16 @@ void hclge_mbx_handler(struct hclge_dev *hdev) continue; } - vport = &hdev->vport[req->mbx_src_vfid]; - trace_hclge_pf_mbx_get(hdev, req); /* clear the resp_msg before processing every mailbox message */ memset(&resp_msg, 0, sizeof(resp_msg)); - - switch (req->msg.code) { - case HCLGE_MBX_MAP_RING_TO_VECTOR: - ret = hclge_map_unmap_ring_to_vf_vector(vport, true, - req); - break; - case HCLGE_MBX_UNMAP_RING_TO_VECTOR: - ret = hclge_map_unmap_ring_to_vf_vector(vport, false, - req); - break; - case HCLGE_MBX_GET_RING_VECTOR_MAP: - ret = hclge_get_vf_ring_vector_map(vport, req, - &resp_msg); - if (ret) - dev_err(&hdev->pdev->dev, - "PF fail(%d) to get VF ring vector map\n", - ret); - break; - case HCLGE_MBX_SET_PROMISC_MODE: - hclge_set_vf_promisc_mode(vport, req); - break; - case HCLGE_MBX_SET_UNICAST: - ret = hclge_set_vf_uc_mac_addr(vport, req); - if (ret) - dev_err(&hdev->pdev->dev, - "PF fail(%d) to set VF UC MAC Addr\n", - ret); - break; - case HCLGE_MBX_SET_MULTICAST: - ret = hclge_set_vf_mc_mac_addr(vport, req); - if (ret) - dev_err(&hdev->pdev->dev, - "PF fail(%d) to set VF MC MAC Addr\n", - ret); - break; - case HCLGE_MBX_SET_VLAN: - ret = hclge_set_vf_vlan_cfg(vport, req, &resp_msg); - if (ret) - dev_err(&hdev->pdev->dev, - "PF failed(%d) to config VF's VLAN\n", - ret); - break; - case HCLGE_MBX_SET_ALIVE: - ret = hclge_set_vf_alive(vport, req); - if (ret) - dev_err(&hdev->pdev->dev, - "PF failed(%d) to set VF's ALIVE\n", - ret); - break; - case HCLGE_MBX_GET_QINFO: - hclge_get_vf_queue_info(vport, &resp_msg); - break; - case HCLGE_MBX_GET_QDEPTH: - hclge_get_vf_queue_depth(vport, &resp_msg); - break; - case HCLGE_MBX_GET_BASIC_INFO: - hclge_get_basic_info(vport, &resp_msg); - break; - case HCLGE_MBX_GET_LINK_STATUS: - ret = hclge_push_vf_link_status(vport); - if (ret) - dev_err(&hdev->pdev->dev, - "failed to inform link stat to VF, ret = %d\n", - ret); - break; - case HCLGE_MBX_QUEUE_RESET: - ret = hclge_mbx_reset_vf_queue(vport, req, &resp_msg); - break; - case HCLGE_MBX_RESET: - ret = hclge_reset_vf(vport); - break; - case HCLGE_MBX_KEEP_ALIVE: - hclge_vf_keep_alive(vport); - break; - case HCLGE_MBX_SET_MTU: - ret = hclge_set_vf_mtu(vport, req); - if (ret) - dev_err(&hdev->pdev->dev, - "VF fail(%d) to set mtu\n", ret); - break; - case HCLGE_MBX_GET_QID_IN_PF: - ret = hclge_get_queue_id_in_pf(vport, req, &resp_msg); - break; - case HCLGE_MBX_GET_RSS_KEY: - ret = hclge_get_rss_key(vport, req, &resp_msg); - break; - case HCLGE_MBX_GET_LINK_MODE: - hclge_get_link_mode(vport, req); - break; - case HCLGE_MBX_GET_VF_FLR_STATUS: - case HCLGE_MBX_VF_UNINIT: - is_del = req->msg.code == HCLGE_MBX_VF_UNINIT; - hclge_rm_vport_all_mac_table(vport, is_del, - HCLGE_MAC_ADDR_UC); - hclge_rm_vport_all_mac_table(vport, is_del, - HCLGE_MAC_ADDR_MC); - hclge_rm_vport_all_vlan_table(vport, is_del); - break; - case HCLGE_MBX_GET_MEDIA_TYPE: - hclge_get_vf_media_type(vport, &resp_msg); - break; - case HCLGE_MBX_PUSH_LINK_STATUS: - hclge_handle_link_change_event(hdev, req); - break; - case HCLGE_MBX_GET_MAC_ADDR: - hclge_get_vf_mac_addr(vport, &resp_msg); - break; - case HCLGE_MBX_NCSI_ERROR: - hclge_handle_ncsi_error(hdev); - break; - case HCLGE_MBX_HANDLE_VF_TBL: - hclge_handle_vf_tbl(vport, req); - break; - default: - dev_err(&hdev->pdev->dev, - "un-supported mailbox message, code = %u\n", - req->msg.code); - break; - } - - /* PF driver should not reply IMP */ - if (hnae3_get_bit(req->mbx_need_resp, HCLGE_MBX_NEED_RESP_B) && - req->msg.code < HCLGE_MBX_GET_VF_FLR_STATUS) { - resp_msg.status = ret; - if (time_is_before_jiffies(hdev->last_mbx_scheduled + - HCLGE_MBX_SCHED_TIMEOUT)) - dev_warn(&hdev->pdev->dev, - "resp vport%u mbx(%u,%u) late\n", - req->mbx_src_vfid, - req->msg.code, - req->msg.subcode); - - hclge_gen_resp_to_vf(vport, req, &resp_msg); - } + param.vport = &hdev->vport[req->mbx_src_vfid]; + param.req = req; + hclge_mbx_request_handling(¶m); crq->desc[crq->next_to_use].flag = 0; hclge_mbx_ring_ptr_move_crq(crq); - - /* reinitialize ret after complete the mbx message processing */ - ret = 0; } /* Write back CMDQ_RQ header pointer, M7 need this pointer */ diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_mdio.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_mdio.c index 03d63b6a9b2b..85fb11de43a1 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_mdio.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_mdio.c @@ -187,7 +187,7 @@ static void hclge_mac_adjust_link(struct net_device *netdev) speed = netdev->phydev->speed; duplex = netdev->phydev->duplex; - ret = hclge_cfg_mac_speed_dup(hdev, speed, duplex); + ret = hclge_cfg_mac_speed_dup(hdev, speed, duplex, 0); if (ret) netdev_err(netdev, "failed to adjust link.\n"); diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_tm.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_tm.c index 2f33b036a47a..4a33f65190e2 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_tm.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_tm.c @@ -248,7 +248,7 @@ static int hclge_fill_pri_array(struct hclge_dev *hdev, u8 *pri, u8 pri_id) return 0; } -static int hclge_up_to_tc_map(struct hclge_dev *hdev) +int hclge_up_to_tc_map(struct hclge_dev *hdev) { struct hclge_desc desc; u8 *pri = (u8 *)desc.data; @@ -266,6 +266,47 @@ static int hclge_up_to_tc_map(struct hclge_dev *hdev) return hclge_cmd_send(&hdev->hw, &desc, 1); } +static void hclge_dscp_to_prio_map_init(struct hclge_dev *hdev) +{ + u8 i; + + hdev->vport[0].nic.kinfo.tc_map_mode = HNAE3_TC_MAP_MODE_PRIO; + hdev->vport[0].nic.kinfo.dscp_app_cnt = 0; + for (i = 0; i < HNAE3_MAX_DSCP; i++) + hdev->vport[0].nic.kinfo.dscp_prio[i] = HNAE3_PRIO_ID_INVALID; +} + +int hclge_dscp_to_tc_map(struct hclge_dev *hdev) +{ + struct hclge_desc desc[HCLGE_DSCP_MAP_TC_BD_NUM]; + u8 *req0 = (u8 *)desc[0].data; + u8 *req1 = (u8 *)desc[1].data; + u8 pri_id, tc_id, i, j; + + hclge_cmd_setup_basic_desc(&desc[0], HCLGE_OPC_QOS_MAP, false); + desc[0].flag |= cpu_to_le16(HCLGE_COMM_CMD_FLAG_NEXT); + hclge_cmd_setup_basic_desc(&desc[1], HCLGE_OPC_QOS_MAP, false); + + /* The low 32 dscp setting use bd0, high 32 dscp setting use bd1 */ + for (i = 0; i < HNAE3_MAX_DSCP / HCLGE_DSCP_MAP_TC_BD_NUM; i++) { + pri_id = hdev->vport[0].nic.kinfo.dscp_prio[i]; + pri_id = pri_id == HNAE3_PRIO_ID_INVALID ? 0 : pri_id; + tc_id = hdev->tm_info.prio_tc[pri_id]; + /* Each dscp setting has 4 bits, so each byte saves two dscp + * setting + */ + req0[i >> 1] |= tc_id << HCLGE_DSCP_TC_SHIFT(i); + + j = i + HNAE3_MAX_DSCP / HCLGE_DSCP_MAP_TC_BD_NUM; + pri_id = hdev->vport[0].nic.kinfo.dscp_prio[j]; + pri_id = pri_id == HNAE3_PRIO_ID_INVALID ? 0 : pri_id; + tc_id = hdev->tm_info.prio_tc[pri_id]; + req1[i >> 1] |= tc_id << HCLGE_DSCP_TC_SHIFT(i); + } + + return hclge_cmd_send(&hdev->hw, desc, HCLGE_DSCP_MAP_TC_BD_NUM); +} + static int hclge_tm_pg_to_pri_map_cfg(struct hclge_dev *hdev, u8 pg_id, u8 pri_bit_map) { @@ -1275,6 +1316,12 @@ static int hclge_tm_map_cfg(struct hclge_dev *hdev) if (ret) return ret; + if (hdev->vport[0].nic.kinfo.tc_map_mode == HNAE3_TC_MAP_MODE_DSCP) { + ret = hclge_dscp_to_tc_map(hdev); + if (ret) + return ret; + } + ret = hclge_tm_pg_to_pri_map(hdev); if (ret) return ret; @@ -1646,6 +1693,7 @@ int hclge_tm_schd_init(struct hclge_dev *hdev) return -EINVAL; hclge_tm_schd_info_init(hdev); + hclge_dscp_to_prio_map_init(hdev); return hclge_tm_init_hw(hdev, true); } diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_tm.h b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_tm.h index d943943912f7..68f28a98e380 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_tm.h +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_tm.h @@ -30,6 +30,9 @@ enum hclge_opcode_type; #define HCLGE_TM_PF_MAX_PRI_NUM 8 #define HCLGE_TM_PF_MAX_QSET_NUM 8 +#define HCLGE_DSCP_MAP_TC_BD_NUM 2 +#define HCLGE_DSCP_TC_SHIFT(n) (((n) & 1) * 4) + struct hclge_pg_to_pri_link_cmd { u8 pg_id; u8 rsvd1[3]; @@ -262,4 +265,6 @@ int hclge_tm_get_pg_shaper(struct hclge_dev *hdev, u8 pg_id, struct hclge_tm_shaper_para *para); int hclge_tm_get_port_shaper(struct hclge_dev *hdev, struct hclge_tm_shaper_para *para); +int hclge_up_to_tc_map(struct hclge_dev *hdev); +int hclge_dscp_to_tc_map(struct hclge_dev *hdev); #endif diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c index 26f87330173e..db6f7cdba958 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c @@ -2125,7 +2125,7 @@ static int hclgevf_config_gro(struct hclgevf_dev *hdev) struct hclge_desc desc; int ret; - if (!hnae3_dev_gro_supported(hdev)) + if (!hnae3_ae_dev_gro_supported(hdev->ae_dev)) return 0; hclgevf_cmd_setup_basic_desc(&desc, HCLGE_OPC_GRO_GENERIC_CONFIG, @@ -3177,7 +3177,7 @@ static int hclgevf_get_status(struct hnae3_handle *handle) static void hclgevf_get_ksettings_an_result(struct hnae3_handle *handle, u8 *auto_neg, u32 *speed, - u8 *duplex) + u8 *duplex, u32 *lane_num) { struct hclgevf_dev *hdev = hclgevf_ae_get_hdev(handle); @@ -3429,7 +3429,7 @@ static struct hnae3_ae_algo ae_algovf = { .pdev_id_table = ae_algovf_pci_tbl, }; -static int hclgevf_init(void) +static int __init hclgevf_init(void) { pr_info("%s is initializing\n", HCLGEVF_NAME); @@ -3444,7 +3444,7 @@ static int hclgevf_init(void) return 0; } -static void hclgevf_exit(void) +static void __exit hclgevf_exit(void) { hnae3_unregister_ae_algo(&ae_algovf); destroy_workqueue(hclgevf_wq); diff --git a/drivers/net/ethernet/huawei/hinic/hinic_debugfs.h b/drivers/net/ethernet/huawei/hinic/hinic_debugfs.h index e9e00cfa1329..e10f739d8339 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_debugfs.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_debugfs.h @@ -12,7 +12,6 @@ #define TBL_ID_FUNC_CFG_SM_INST 1 #define HINIC_FUNCTION_CONFIGURE_TABLE_SIZE 64 -#define HINIC_FUNCTION_CONFIGURE_TABLE 1 struct hinic_cmd_lt_rd { u8 status; diff --git a/drivers/net/ethernet/huawei/hinic/hinic_ethtool.c b/drivers/net/ethernet/huawei/hinic/hinic_ethtool.c index 93192f58ac88..f4b680286911 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_ethtool.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_ethtool.c @@ -55,7 +55,6 @@ #define COALESCE_ALL_QUEUE 0xFFFF #define COALESCE_MAX_PENDING_LIMIT (255 * COALESCE_PENDING_LIMIT_UNIT) #define COALESCE_MAX_TIMER_CFG (255 * COALESCE_TIMER_CFG_UNIT) -#define OBJ_STR_MAX_LEN 32 struct hw2ethtool_link_mode { enum ethtool_link_mode_bit_indices link_mode_bit; diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_cmdq.c b/drivers/net/ethernet/huawei/hinic/hinic_hw_cmdq.c index a627237f694b..78190e88cd75 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_cmdq.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_cmdq.c @@ -82,11 +82,6 @@ struct hinic_func_to_io, \ cmdqs) -enum cmdq_wqe_type { - WQE_LCMD_TYPE = 0, - WQE_SCMD_TYPE = 1, -}; - enum completion_format { COMPLETE_DIRECT = 0, COMPLETE_SGE = 1, @@ -509,8 +504,8 @@ int hinic_cmdq_direct_resp(struct hinic_cmdqs *cmdqs, * * Return 0 - Success, negative - Failure **/ -int hinic_set_arm_bit(struct hinic_cmdqs *cmdqs, - enum hinic_set_arm_qtype q_type, u32 q_id) +static int hinic_set_arm_bit(struct hinic_cmdqs *cmdqs, + enum hinic_set_arm_qtype q_type, u32 q_id) { struct hinic_cmdq *cmdq = &cmdqs->cmdq[HINIC_CMDQ_SYNC]; struct hinic_hwif *hwif = cmdqs->hwif; diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_cmdq.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_cmdq.h index 9c413e963a04..ff09cf0ed52b 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_cmdq.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_cmdq.h @@ -177,9 +177,6 @@ int hinic_cmdq_direct_resp(struct hinic_cmdqs *cmdqs, enum hinic_mod_type mod, u8 cmd, struct hinic_cmdq_buf *buf_in, u64 *out_param); -int hinic_set_arm_bit(struct hinic_cmdqs *cmdqs, - enum hinic_set_arm_qtype q_type, u32 q_id); - int hinic_init_cmdqs(struct hinic_cmdqs *cmdqs, struct hinic_hwif *hwif, void __iomem **db_area); diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_csr.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_csr.h index 7e84e4e33fff..d56e7413ace0 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_csr.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_csr.h @@ -22,7 +22,6 @@ (HINIC_DMA_ATTR_BASE + (idx) * HINIC_DMA_ATTR_STRIDE) #define HINIC_PPF_ELECTION_STRIDE 0x4 -#define HINIC_CSR_MAX_PORTS 4 #define HINIC_CSR_PPF_ELECTION_ADDR(idx) \ (HINIC_ELECTION_BASE + (idx) * HINIC_PPF_ELECTION_STRIDE) diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.c b/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.c index 2127a48749a8..94f470556295 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.c @@ -29,7 +29,6 @@ #include "hinic_hw_io.h" #include "hinic_hw_dev.h" -#define IO_STATUS_TIMEOUT 100 #define OUTBOUND_STATE_TIMEOUT 100 #define DB_STATE_TIMEOUT 100 @@ -42,11 +41,6 @@ enum intr_type { INTR_MSIX_TYPE, }; -enum io_status { - IO_STOPPED = 0, - IO_RUNNING = 1, -}; - /** * parse_capability - convert device capabilities to NIC capabilities * @hwdev: the HW device to set and convert device capabilities for @@ -837,8 +831,8 @@ static int hinic_l2nic_reset(struct hinic_hwdev *hwdev) return 0; } -int hinic_get_interrupt_cfg(struct hinic_hwdev *hwdev, - struct hinic_msix_config *interrupt_info) +static int hinic_get_interrupt_cfg(struct hinic_hwdev *hwdev, + struct hinic_msix_config *interrupt_info) { u16 out_size = sizeof(*interrupt_info); struct hinic_pfhwdev *pfhwdev; @@ -1041,13 +1035,6 @@ void hinic_free_hwdev(struct hinic_hwdev *hwdev) hinic_free_hwif(hwdev->hwif); } -int hinic_hwdev_max_num_qps(struct hinic_hwdev *hwdev) -{ - struct hinic_cap *nic_cap = &hwdev->nic_cap; - - return nic_cap->max_qps; -} - /** * hinic_hwdev_num_qps - return the number QPs available for use * @hwdev: the NIC HW device diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.h index 416492e48274..d2d89b0a5ef0 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.h @@ -566,8 +566,6 @@ struct hinic_hwdev *hinic_init_hwdev(struct pci_dev *pdev, struct devlink *devli void hinic_free_hwdev(struct hinic_hwdev *hwdev); -int hinic_hwdev_max_num_qps(struct hinic_hwdev *hwdev); - int hinic_hwdev_num_qps(struct hinic_hwdev *hwdev); struct hinic_sq *hinic_hwdev_get_sq(struct hinic_hwdev *hwdev, int i); @@ -587,9 +585,6 @@ int hinic_hwdev_hw_ci_addr_set(struct hinic_hwdev *hwdev, struct hinic_sq *sq, void hinic_hwdev_set_msix_state(struct hinic_hwdev *hwdev, u16 msix_index, enum hinic_msix_state flag); -int hinic_get_interrupt_cfg(struct hinic_hwdev *hwdev, - struct hinic_msix_config *interrupt_info); - int hinic_set_interrupt_cfg(struct hinic_hwdev *hwdev, struct hinic_msix_config *interrupt_info); diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_if.c b/drivers/net/ethernet/huawei/hinic/hinic_hw_if.c index 0428faa68e80..88567305d06e 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_if.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_if.c @@ -58,39 +58,6 @@ int hinic_msix_attr_set(struct hinic_hwif *hwif, u16 msix_index, } /** - * hinic_msix_attr_get - get message attribute of msix entry - * @hwif: the HW interface of a pci function device - * @msix_index: msix_index - * @pending_limit: the maximum pending interrupt events (unit 8) - * @coalesc_timer: coalesc period for interrupt (unit 8 us) - * @lli_timer: replenishing period for low latency credit (unit 8 us) - * @lli_credit_limit: maximum credits for low latency msix messages (unit 8) - * @resend_timer: maximum wait for resending msix (unit coalesc period) - * - * Return 0 - Success, negative - Failure - **/ -int hinic_msix_attr_get(struct hinic_hwif *hwif, u16 msix_index, - u8 *pending_limit, u8 *coalesc_timer, - u8 *lli_timer, u8 *lli_credit_limit, - u8 *resend_timer) -{ - u32 addr, val; - - if (!VALID_MSIX_IDX(&hwif->attr, msix_index)) - return -EINVAL; - - addr = HINIC_CSR_MSIX_CTRL_ADDR(msix_index); - val = hinic_hwif_read_reg(hwif, addr); - - *pending_limit = HINIC_MSIX_ATTR_GET(val, PENDING_LIMIT); - *coalesc_timer = HINIC_MSIX_ATTR_GET(val, COALESC_TIMER); - *lli_timer = HINIC_MSIX_ATTR_GET(val, LLI_TIMER); - *lli_credit_limit = HINIC_MSIX_ATTR_GET(val, LLI_CREDIT); - *resend_timer = HINIC_MSIX_ATTR_GET(val, RESEND_TIMER); - return 0; -} - -/** * hinic_msix_attr_cnt_clear - clear message attribute counters for msix entry * @hwif: the HW interface of a pci function device * @msix_index: msix_index @@ -115,8 +82,6 @@ int hinic_msix_attr_cnt_clear(struct hinic_hwif *hwif, u16 msix_index) * hinic_set_pf_action - set action on pf channel * @hwif: the HW interface of a pci function device * @action: action on pf channel - * - * Return 0 - Success, negative - Failure **/ void hinic_set_pf_action(struct hinic_hwif *hwif, enum hinic_pf_action action) { diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_if.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_if.h index c06f2253151e..3d588896a367 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_if.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_if.h @@ -131,10 +131,6 @@ (((u32)(val) & HINIC_MSIX_##member##_MASK) << \ HINIC_MSIX_##member##_SHIFT) -#define HINIC_MSIX_ATTR_GET(val, member) \ - (((val) >> HINIC_MSIX_##member##_SHIFT) & \ - HINIC_MSIX_##member##_MASK) - #define HINIC_MSIX_CNT_RESEND_TIMER_SHIFT 29 #define HINIC_MSIX_CNT_RESEND_TIMER_MASK 0x1 @@ -269,11 +265,6 @@ int hinic_msix_attr_set(struct hinic_hwif *hwif, u16 msix_index, u8 lli_timer_cfg, u8 lli_credit_limit, u8 resend_timer); -int hinic_msix_attr_get(struct hinic_hwif *hwif, u16 msix_index, - u8 *pending_limit, u8 *coalesc_timer_cfg, - u8 *lli_timer, u8 *lli_credit_limit, - u8 *resend_timer); - void hinic_set_msix_state(struct hinic_hwif *hwif, u16 msix_idx, enum hinic_msix_state flag); diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_mbox.c b/drivers/net/ethernet/huawei/hinic/hinic_hw_mbox.c index 5078c0c73863..3f9c31d29215 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_mbox.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_mbox.c @@ -117,7 +117,6 @@ enum hinic_mbox_tx_status { #define MBOX_WB_STATUS_MASK 0xFF #define MBOX_WB_ERROR_CODE_MASK 0xFF00 #define MBOX_WB_STATUS_FINISHED_SUCCESS 0xFF -#define MBOX_WB_STATUS_FINISHED_WITH_ERR 0xFE #define MBOX_WB_STATUS_NOT_FINISHED 0x00 #define MBOX_STATUS_FINISHED(wb) \ @@ -130,11 +129,8 @@ enum hinic_mbox_tx_status { #define SEQ_ID_START_VAL 0 #define SEQ_ID_MAX_VAL 42 -#define DST_AEQ_IDX_DEFAULT_VAL 0 -#define SRC_AEQ_IDX_DEFAULT_VAL 0 #define NO_DMA_ATTRIBUTE_VAL 0 -#define HINIC_MGMT_RSP_AEQN 0 #define HINIC_MBOX_RSP_AEQN 2 #define HINIC_MBOX_RECV_AEQN 0 @@ -146,7 +142,6 @@ enum hinic_mbox_tx_status { #define IS_PF_OR_PPF_SRC(src_func_idx) ((src_func_idx) < HINIC_MAX_PF_FUNCS) -#define MBOX_RESPONSE_ERROR 0x1 #define MBOX_MSG_ID_MASK 0xFF #define MBOX_MSG_ID(func_to_func) ((func_to_func)->send_msg_id) #define MBOX_MSG_ID_INC(func_to_func_mbox) (MBOX_MSG_ID(func_to_func_mbox) = \ @@ -621,7 +616,7 @@ static bool check_vf_mbox_random_id(struct hinic_mbox_func_to_func *func_to_func return false; } -void hinic_mbox_func_aeqe_handler(void *handle, void *header, u8 size) +static void hinic_mbox_func_aeqe_handler(void *handle, void *header, u8 size) { struct hinic_mbox_func_to_func *func_to_func; u64 mbox_header = *((u64 *)header); @@ -649,7 +644,7 @@ void hinic_mbox_func_aeqe_handler(void *handle, void *header, u8 size) recv_mbox_handler(func_to_func, (u64 *)header, recv_mbox); } -void hinic_mbox_self_aeqe_handler(void *handle, void *header, u8 size) +static void hinic_mbox_self_aeqe_handler(void *handle, void *header, u8 size) { struct hinic_mbox_func_to_func *func_to_func; struct hinic_send_mbox *send_mbox; diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_mbox.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_mbox.h index 46953190d29e..33ac7814d3b3 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_mbox.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_mbox.h @@ -150,10 +150,6 @@ void hinic_unregister_pf_mbox_cb(struct hinic_hwdev *hwdev, void hinic_unregister_vf_mbox_cb(struct hinic_hwdev *hwdev, enum hinic_mod_type mod); -void hinic_mbox_func_aeqe_handler(void *handle, void *header, u8 size); - -void hinic_mbox_self_aeqe_handler(void *handle, void *header, u8 size); - int hinic_func_to_func_init(struct hinic_hwdev *hwdev); void hinic_func_to_func_free(struct hinic_hwdev *hwdev); diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_qp.c b/drivers/net/ethernet/huawei/hinic/hinic_hw_qp.c index 336248aa2e48..537a8098bc4e 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_qp.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_qp.c @@ -472,8 +472,7 @@ int hinic_get_rq_free_wqebbs(struct hinic_rq *rq) return atomic_read(&wq->delta) - 1; } -static void sq_prepare_ctrl(struct hinic_sq_ctrl *ctrl, u16 prod_idx, - int nr_descs) +static void sq_prepare_ctrl(struct hinic_sq_ctrl *ctrl, int nr_descs) { u32 ctrl_size, task_size, bufdesc_size; @@ -588,18 +587,16 @@ void hinic_set_tso_inner_l4(struct hinic_sq_task *task, u32 *queue_info, /** * hinic_sq_prepare_wqe - prepare wqe before insert to the queue * @sq: send queue - * @prod_idx: pi value * @sq_wqe: wqe to prepare * @sges: sges for use by the wqe for send for buf addresses * @nr_sges: number of sges **/ -void hinic_sq_prepare_wqe(struct hinic_sq *sq, u16 prod_idx, - struct hinic_sq_wqe *sq_wqe, struct hinic_sge *sges, - int nr_sges) +void hinic_sq_prepare_wqe(struct hinic_sq *sq, struct hinic_sq_wqe *sq_wqe, + struct hinic_sge *sges, int nr_sges) { int i; - sq_prepare_ctrl(&sq_wqe->ctrl, prod_idx, nr_sges); + sq_prepare_ctrl(&sq_wqe->ctrl, nr_sges); sq_prepare_task(&sq_wqe->task); diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_qp.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_qp.h index 0dfa51ad5855..178dcc874370 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_qp.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_qp.h @@ -175,9 +175,8 @@ void hinic_set_tso_inner_l4(struct hinic_sq_task *task, u32 l4_len, u32 offset, u32 ip_ident, u32 mss); -void hinic_sq_prepare_wqe(struct hinic_sq *sq, u16 prod_idx, - struct hinic_sq_wqe *wqe, struct hinic_sge *sges, - int nr_sges); +void hinic_sq_prepare_wqe(struct hinic_sq *sq, struct hinic_sq_wqe *wqe, + struct hinic_sge *sges, int nr_sges); void hinic_sq_write_db(struct hinic_sq *sq, u16 prod_idx, unsigned int wqe_size, unsigned int cos); diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_wq.c b/drivers/net/ethernet/huawei/hinic/hinic_hw_wq.c index 4daf6bf291ec..e1a1735c00c1 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_wq.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_wq.c @@ -175,8 +175,6 @@ static int cmdq_allocate_page(struct hinic_cmdq_pages *cmdq_pages) /** * cmdq_free_page - free page from cmdq * @cmdq_pages: the pages of the cmdq queue struct that hold the page - * - * Return 0 - Success, negative - Failure **/ static void cmdq_free_page(struct hinic_cmdq_pages *cmdq_pages) { diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_wqe.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_wqe.h index f4b6d2c1061f..c6bdeed5606e 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_wqe.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_wqe.h @@ -261,23 +261,6 @@ #define HINIC_RSS_TYPE_GET(val, member) \ (((u32)(val) >> HINIC_RSS_TYPE_##member##_SHIFT) & 0x1) -enum hinic_l4offload_type { - HINIC_L4_OFF_DISABLE = 0, - HINIC_TCP_OFFLOAD_ENABLE = 1, - HINIC_SCTP_OFFLOAD_ENABLE = 2, - HINIC_UDP_OFFLOAD_ENABLE = 3, -}; - -enum hinic_vlan_offload { - HINIC_VLAN_OFF_DISABLE = 0, - HINIC_VLAN_OFF_ENABLE = 1, -}; - -enum hinic_pkt_parsed { - HINIC_PKT_NOT_PARSED = 0, - HINIC_PKT_PARSED = 1, -}; - enum hinic_l3_offload_type { L3TYPE_UNKNOWN = 0, IPV6_PKT = 1, @@ -305,18 +288,10 @@ enum hinic_outer_l3type { HINIC_OUTER_L3TYPE_IPV4_CHKSUM = 3, }; -enum hinic_media_type { - HINIC_MEDIA_UNKNOWN = 0, -}; - enum hinic_l2type { HINIC_L2TYPE_ETH = 0, }; -enum hinc_tunnel_l4type { - HINIC_TUNNEL_L4TYPE_UNKNOWN = 0, -}; - struct hinic_cmdq_header { u32 header_info; u32 saved_data; diff --git a/drivers/net/ethernet/huawei/hinic/hinic_main.c b/drivers/net/ethernet/huawei/hinic/hinic_main.c index c23ee2ddbce3..e1f54a2f28b2 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_main.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_main.c @@ -960,8 +960,6 @@ static void hinic_refresh_nic_cfg(struct hinic_dev *nic_dev) * @in_size: input size * @buf_out: output buffer * @out_size: returned output size - * - * Return 0 - Success, negative - Failure **/ static void link_status_event_handler(void *handle, void *buf_in, u16 in_size, void *buf_out, u16 *out_size) @@ -1382,8 +1380,6 @@ err_pci_regions: return err; } -#define HINIC_WAIT_SRIOV_CFG_TIMEOUT 15000 - static void wait_sriov_cfg_complete(struct hinic_dev *nic_dev) { struct hinic_sriov_info *sriov_info = &nic_dev->sriov_info; diff --git a/drivers/net/ethernet/huawei/hinic/hinic_rx.c b/drivers/net/ethernet/huawei/hinic/hinic_rx.c index e5828a658caf..d649c6e323c8 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_rx.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_rx.c @@ -50,7 +50,7 @@ * hinic_rxq_clean_stats - Clean the statistics of specific queue * @rxq: Logical Rx Queue **/ -void hinic_rxq_clean_stats(struct hinic_rxq *rxq) +static void hinic_rxq_clean_stats(struct hinic_rxq *rxq) { struct hinic_rxq_stats *rxq_stats = &rxq->rxq_stats; diff --git a/drivers/net/ethernet/huawei/hinic/hinic_rx.h b/drivers/net/ethernet/huawei/hinic/hinic_rx.h index 507dcbae9085..8f7bd6a049bd 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_rx.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_rx.h @@ -41,8 +41,6 @@ struct hinic_rxq { struct napi_struct napi; }; -void hinic_rxq_clean_stats(struct hinic_rxq *rxq); - void hinic_rxq_get_stats(struct hinic_rxq *rxq, struct hinic_rxq_stats *stats); int hinic_init_rxq(struct hinic_rxq *rxq, struct hinic_rq *rq, diff --git a/drivers/net/ethernet/huawei/hinic/hinic_sriov.c b/drivers/net/ethernet/huawei/hinic/hinic_sriov.c index df555847afb5..a5f08b969e3f 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_sriov.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_sriov.c @@ -24,6 +24,7 @@ MODULE_PARM_DESC(set_vf_link_state, "Set vf link state, 0 represents link auto, #define HINIC_VLAN_PRIORITY_SHIFT 13 #define HINIC_ADD_VLAN_IN_MAC 0x8000 #define HINIC_TX_RATE_TABLE_FULL 12 +#define HINIC_MAX_QOS 7 static int hinic_set_mac(struct hinic_hwdev *hwdev, const u8 *mac_addr, u16 vlan_id, u16 func_id) @@ -774,7 +775,7 @@ int hinic_ndo_set_vf_vlan(struct net_device *netdev, int vf, u16 vlan, u8 qos, u16 vlanprio, cur_vlanprio; sriov_info = &nic_dev->sriov_info; - if (vf >= sriov_info->num_vfs || vlan > 4095 || qos > 7) + if (vf >= sriov_info->num_vfs || vlan >= VLAN_N_VID || qos > HINIC_MAX_QOS) return -EINVAL; if (vlan_proto != htons(ETH_P_8021Q)) return -EPROTONOSUPPORT; @@ -820,7 +821,7 @@ int hinic_ndo_set_vf_trust(struct net_device *netdev, int vf, bool setting) cur_trust = nic_io->vf_infos[vf].trust; /* same request, so just return success */ - if ((setting && cur_trust) || (!setting && !cur_trust)) + if (setting == cur_trust) return 0; err = hinic_set_vf_trust(adapter->hwdev, vf, setting); @@ -940,7 +941,7 @@ int hinic_ndo_set_vf_spoofchk(struct net_device *netdev, int vf, bool setting) cur_spoofchk = nic_dev->hwdev->func_to_io.vf_infos[vf].spoofchk; /* same request, so just return success */ - if ((setting && cur_spoofchk) || (!setting && !cur_spoofchk)) + if (setting == cur_spoofchk) return 0; err = hinic_set_vf_spoofchk(sriov_info->hwdev, @@ -1131,8 +1132,8 @@ static void hinic_clear_vf_infos(struct hinic_dev *nic_dev, u16 vf_id) hinic_init_vf_infos(&nic_dev->hwdev->func_to_io, HW_VF_ID_TO_OS(vf_id)); } -static int hinic_deinit_vf_hw(struct hinic_sriov_info *sriov_info, - u16 start_vf_id, u16 end_vf_id) +static void hinic_deinit_vf_hw(struct hinic_sriov_info *sriov_info, + u16 start_vf_id, u16 end_vf_id) { struct hinic_dev *nic_dev; u16 func_idx, idx; @@ -1145,8 +1146,6 @@ static int hinic_deinit_vf_hw(struct hinic_sriov_info *sriov_info, HINIC_HW_WQ_PAGE_SIZE); hinic_clear_vf_infos(nic_dev, idx); } - - return 0; } int hinic_vf_func_init(struct hinic_hwdev *hwdev) @@ -1293,7 +1292,7 @@ int hinic_pci_sriov_disable(struct pci_dev *pdev) return 0; } -int hinic_pci_sriov_enable(struct pci_dev *pdev, int num_vfs) +static int hinic_pci_sriov_enable(struct pci_dev *pdev, int num_vfs) { struct hinic_sriov_info *sriov_info; int err; diff --git a/drivers/net/ethernet/huawei/hinic/hinic_sriov.h b/drivers/net/ethernet/huawei/hinic/hinic_sriov.h index ba627a362f9a..d4d4e63d31ea 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_sriov.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_sriov.h @@ -98,8 +98,6 @@ void hinic_notify_all_vfs_link_changed(struct hinic_hwdev *hwdev, int hinic_pci_sriov_disable(struct pci_dev *dev); -int hinic_pci_sriov_enable(struct pci_dev *dev, int num_vfs); - int hinic_vf_func_init(struct hinic_hwdev *hwdev); void hinic_vf_func_free(struct hinic_hwdev *hwdev); diff --git a/drivers/net/ethernet/huawei/hinic/hinic_tx.c b/drivers/net/ethernet/huawei/hinic/hinic_tx.c index 3b6c7b585737..e91476c8ff8b 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_tx.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_tx.c @@ -74,7 +74,7 @@ enum hinic_offload_type { * hinic_txq_clean_stats - Clean the statistics of specific queue * @txq: Logical Tx Queue **/ -void hinic_txq_clean_stats(struct hinic_txq *txq) +static void hinic_txq_clean_stats(struct hinic_txq *txq) { struct hinic_txq_stats *txq_stats = &txq->txq_stats; @@ -530,7 +530,7 @@ netdev_tx_t hinic_lb_xmit_frame(struct sk_buff *skb, struct net_device *netdev) } process_sq_wqe: - hinic_sq_prepare_wqe(txq->sq, prod_idx, sq_wqe, txq->sges, nr_sges); + hinic_sq_prepare_wqe(txq->sq, sq_wqe, txq->sges, nr_sges); hinic_sq_write_wqe(txq->sq, prod_idx, sq_wqe, skb, wqe_size); flush_skbs: @@ -614,7 +614,7 @@ netdev_tx_t hinic_xmit_frame(struct sk_buff *skb, struct net_device *netdev) } process_sq_wqe: - hinic_sq_prepare_wqe(txq->sq, prod_idx, sq_wqe, txq->sges, nr_sges); + hinic_sq_prepare_wqe(txq->sq, sq_wqe, txq->sges, nr_sges); err = hinic_tx_offload(skb, &sq_wqe->task, &sq_wqe->ctrl.queue_info); if (err) diff --git a/drivers/net/ethernet/huawei/hinic/hinic_tx.h b/drivers/net/ethernet/huawei/hinic/hinic_tx.h index b3c8657774a7..91dc778362f3 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_tx.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_tx.h @@ -40,8 +40,6 @@ struct hinic_txq { struct napi_struct napi; }; -void hinic_txq_clean_stats(struct hinic_txq *txq); - void hinic_txq_get_stats(struct hinic_txq *txq, struct hinic_txq_stats *stats); netdev_tx_t hinic_lb_xmit_frame(struct sk_buff *skb, struct net_device *netdev); diff --git a/drivers/net/ethernet/ibm/ehea/ehea_ethtool.c b/drivers/net/ethernet/ibm/ehea/ehea_ethtool.c index 6cb86032ce46..1db5b6790a41 100644 --- a/drivers/net/ethernet/ibm/ehea/ehea_ethtool.c +++ b/drivers/net/ethernet/ibm/ehea/ehea_ethtool.c @@ -159,8 +159,8 @@ static int ehea_nway_reset(struct net_device *dev) static void ehea_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { - strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); - strlcpy(info->version, DRV_VERSION, sizeof(info->version)); + strscpy(info->driver, DRV_NAME, sizeof(info->driver)); + strscpy(info->version, DRV_VERSION, sizeof(info->version)); } static u32 ehea_get_msglevel(struct net_device *dev) diff --git a/drivers/net/ethernet/ibm/ehea/ehea_main.c b/drivers/net/ethernet/ibm/ehea/ehea_main.c index 5dc302880f5f..294bdbbeacc3 100644 --- a/drivers/net/ethernet/ibm/ehea/ehea_main.c +++ b/drivers/net/ethernet/ibm/ehea/ehea_main.c @@ -1546,7 +1546,7 @@ static int ehea_init_port_res(struct ehea_port *port, struct ehea_port_res *pr, kfree(init_attr); - netif_napi_add(pr->port->netdev, &pr->napi, ehea_poll, 64); + netif_napi_add(pr->port->netdev, &pr->napi, ehea_poll); ret = 0; goto out; diff --git a/drivers/net/ethernet/ibm/emac/core.c b/drivers/net/ethernet/ibm/emac/core.c index fbea9f7efe8c..9b08e41ccc29 100644 --- a/drivers/net/ethernet/ibm/emac/core.c +++ b/drivers/net/ethernet/ibm/emac/core.c @@ -2284,8 +2284,8 @@ static void emac_ethtool_get_drvinfo(struct net_device *ndev, { struct emac_instance *dev = netdev_priv(ndev); - strlcpy(info->driver, "ibm_emac", sizeof(info->driver)); - strlcpy(info->version, DRV_VERSION, sizeof(info->version)); + strscpy(info->driver, "ibm_emac", sizeof(info->driver)); + strscpy(info->version, DRV_VERSION, sizeof(info->version)); snprintf(info->bus_info, sizeof(info->bus_info), "PPC 4xx EMAC-%d %pOF", dev->cell_index, dev->ofdev->dev.of_node); } @@ -2979,11 +2979,9 @@ static int emac_init_config(struct emac_instance *dev) /* Read MAC-address */ err = of_get_ethdev_address(np, dev->ndev); - if (err) { - if (err != -EPROBE_DEFER) - dev_err(&dev->ofdev->dev, "Can't get valid [local-]mac-address from OF !\n"); - return err; - } + if (err) + return dev_err_probe(&dev->ofdev->dev, err, + "Can't get valid [local-]mac-address from OF !\n"); /* IAHT and GAHT filter parameterization */ if (emac_has_feature(dev, EMAC_FTR_EMAC4SYNC)) { diff --git a/drivers/net/ethernet/ibm/ibmveth.c b/drivers/net/ethernet/ibm/ibmveth.c index 5c6a04d29f5b..3b14dc93f59d 100644 --- a/drivers/net/ethernet/ibm/ibmveth.c +++ b/drivers/net/ethernet/ibm/ibmveth.c @@ -141,6 +141,13 @@ static inline int ibmveth_rxq_csum_good(struct ibmveth_adapter *adapter) return ibmveth_rxq_flags(adapter) & IBMVETH_RXQ_CSUM_GOOD; } +static unsigned int ibmveth_real_max_tx_queues(void) +{ + unsigned int n_cpu = num_online_cpus(); + + return min(n_cpu, IBMVETH_MAX_QUEUES); +} + /* setup the initial settings for a buffer pool */ static void ibmveth_init_buffer_pool(struct ibmveth_buff_pool *pool, u32 pool_index, u32 pool_size, @@ -456,6 +463,38 @@ static void ibmveth_rxq_harvest_buffer(struct ibmveth_adapter *adapter) } } +static void ibmveth_free_tx_ltb(struct ibmveth_adapter *adapter, int idx) +{ + dma_unmap_single(&adapter->vdev->dev, adapter->tx_ltb_dma[idx], + adapter->tx_ltb_size, DMA_TO_DEVICE); + kfree(adapter->tx_ltb_ptr[idx]); + adapter->tx_ltb_ptr[idx] = NULL; +} + +static int ibmveth_allocate_tx_ltb(struct ibmveth_adapter *adapter, int idx) +{ + adapter->tx_ltb_ptr[idx] = kzalloc(adapter->tx_ltb_size, + GFP_KERNEL); + if (!adapter->tx_ltb_ptr[idx]) { + netdev_err(adapter->netdev, + "unable to allocate tx long term buffer\n"); + return -ENOMEM; + } + adapter->tx_ltb_dma[idx] = dma_map_single(&adapter->vdev->dev, + adapter->tx_ltb_ptr[idx], + adapter->tx_ltb_size, + DMA_TO_DEVICE); + if (dma_mapping_error(&adapter->vdev->dev, adapter->tx_ltb_dma[idx])) { + netdev_err(adapter->netdev, + "unable to DMA map tx long term buffer\n"); + kfree(adapter->tx_ltb_ptr[idx]); + adapter->tx_ltb_ptr[idx] = NULL; + return -ENOMEM; + } + + return 0; +} + static int ibmveth_register_logical_lan(struct ibmveth_adapter *adapter, union ibmveth_buf_desc rxq_desc, u64 mac_address) { @@ -538,6 +577,11 @@ static int ibmveth_open(struct net_device *netdev) goto out_unmap_buffer_list; } + for (i = 0; i < netdev->real_num_tx_queues; i++) { + if (ibmveth_allocate_tx_ltb(adapter, i)) + goto out_free_tx_ltb; + } + adapter->rx_queue.index = 0; adapter->rx_queue.num_slots = rxq_entries; adapter->rx_queue.toggle = 1; @@ -595,25 +639,15 @@ static int ibmveth_open(struct net_device *netdev) rc = -ENOMEM; - adapter->bounce_buffer = dma_alloc_coherent(&adapter->vdev->dev, - netdev->mtu + IBMVETH_BUFF_OH, - &adapter->bounce_buffer_dma, GFP_KERNEL); - if (!adapter->bounce_buffer) { - netdev_err(netdev, "unable to alloc bounce buffer\n"); - goto out_free_irq; - } - netdev_dbg(netdev, "initial replenish cycle\n"); ibmveth_interrupt(netdev->irq, netdev); - netif_start_queue(netdev); + netif_tx_start_all_queues(netdev); netdev_dbg(netdev, "open complete\n"); return 0; -out_free_irq: - free_irq(netdev->irq, netdev); out_free_buffer_pools: while (--i >= 0) { if (adapter->rx_buff_pool[i].active) @@ -623,6 +657,12 @@ out_free_buffer_pools: out_unmap_filter_list: dma_unmap_single(dev, adapter->filter_list_dma, 4096, DMA_BIDIRECTIONAL); + +out_free_tx_ltb: + while (--i >= 0) { + ibmveth_free_tx_ltb(adapter, i); + } + out_unmap_buffer_list: dma_unmap_single(dev, adapter->buffer_list_dma, 4096, DMA_BIDIRECTIONAL); @@ -651,7 +691,7 @@ static int ibmveth_close(struct net_device *netdev) napi_disable(&adapter->napi); if (!adapter->pool_config) - netif_stop_queue(netdev); + netif_tx_stop_all_queues(netdev); h_vio_signal(adapter->vdev->unit_address, VIO_IRQ_DISABLE); @@ -685,9 +725,8 @@ static int ibmveth_close(struct net_device *netdev) ibmveth_free_buffer_pool(adapter, &adapter->rx_buff_pool[i]); - dma_free_coherent(&adapter->vdev->dev, - adapter->netdev->mtu + IBMVETH_BUFF_OH, - adapter->bounce_buffer, adapter->bounce_buffer_dma); + for (i = 0; i < netdev->real_num_tx_queues; i++) + ibmveth_free_tx_ltb(adapter, i); netdev_dbg(netdev, "close complete\n"); @@ -727,8 +766,8 @@ static void ibmveth_init_link_settings(struct net_device *dev) static void netdev_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { - strlcpy(info->driver, ibmveth_driver_name, sizeof(info->driver)); - strlcpy(info->version, ibmveth_driver_version, sizeof(info->version)); + strscpy(info->driver, ibmveth_driver_name, sizeof(info->driver)); + strscpy(info->version, ibmveth_driver_version, sizeof(info->version)); } static netdev_features_t ibmveth_fix_features(struct net_device *dev, @@ -953,6 +992,69 @@ static void ibmveth_get_ethtool_stats(struct net_device *dev, data[i] = IBMVETH_GET_STAT(adapter, ibmveth_stats[i].offset); } +static void ibmveth_get_channels(struct net_device *netdev, + struct ethtool_channels *channels) +{ + channels->max_tx = ibmveth_real_max_tx_queues(); + channels->tx_count = netdev->real_num_tx_queues; + + channels->max_rx = netdev->real_num_rx_queues; + channels->rx_count = netdev->real_num_rx_queues; +} + +static int ibmveth_set_channels(struct net_device *netdev, + struct ethtool_channels *channels) +{ + struct ibmveth_adapter *adapter = netdev_priv(netdev); + unsigned int old = netdev->real_num_tx_queues, + goal = channels->tx_count; + int rc, i; + + /* If ndo_open has not been called yet then don't allocate, just set + * desired netdev_queue's and return + */ + if (!(netdev->flags & IFF_UP)) + return netif_set_real_num_tx_queues(netdev, goal); + + /* We have IBMVETH_MAX_QUEUES netdev_queue's allocated + * but we may need to alloc/free the ltb's. + */ + netif_tx_stop_all_queues(netdev); + + /* Allocate any queue that we need */ + for (i = old; i < goal; i++) { + if (adapter->tx_ltb_ptr[i]) + continue; + + rc = ibmveth_allocate_tx_ltb(adapter, i); + if (!rc) + continue; + + /* if something goes wrong, free everything we just allocated */ + netdev_err(netdev, "Failed to allocate more tx queues, returning to %d queues\n", + old); + goal = old; + old = i; + break; + } + rc = netif_set_real_num_tx_queues(netdev, goal); + if (rc) { + netdev_err(netdev, "Failed to set real tx queues, returning to %d queues\n", + old); + goal = old; + old = i; + } + /* Free any that are no longer needed */ + for (i = old; i > goal; i--) { + if (adapter->tx_ltb_ptr[i - 1]) + ibmveth_free_tx_ltb(adapter, i - 1); + } + + netif_tx_wake_all_queues(netdev); + + return rc; +} + static const struct ethtool_ops netdev_ethtool_ops = { .get_drvinfo = netdev_get_drvinfo, .get_link = ethtool_op_get_link, @@ -961,6 +1063,8 @@ static const struct ethtool_ops netdev_ethtool_ops = { .get_ethtool_stats = ibmveth_get_ethtool_stats, .get_link_ksettings = ibmveth_get_link_ksettings, .set_link_ksettings = ibmveth_set_link_ksettings, + .get_channels = ibmveth_get_channels, + .set_channels = ibmveth_set_channels }; static int ibmveth_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) @@ -969,7 +1073,7 @@ static int ibmveth_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) } static int ibmveth_send(struct ibmveth_adapter *adapter, - union ibmveth_buf_desc *descs, unsigned long mss) + unsigned long desc, unsigned long mss) { unsigned long correlator; unsigned int retry_count; @@ -982,12 +1086,9 @@ static int ibmveth_send(struct ibmveth_adapter *adapter, retry_count = 1024; correlator = 0; do { - ret = h_send_logical_lan(adapter->vdev->unit_address, - descs[0].desc, descs[1].desc, - descs[2].desc, descs[3].desc, - descs[4].desc, descs[5].desc, - correlator, &correlator, mss, - adapter->fw_large_send_support); + ret = h_send_logical_lan(adapter->vdev->unit_address, desc, + correlator, &correlator, mss, + adapter->fw_large_send_support); } while ((ret == H_BUSY) && (retry_count--)); if (ret != H_SUCCESS && ret != H_DROPPED) { @@ -1020,34 +1121,13 @@ static netdev_tx_t ibmveth_start_xmit(struct sk_buff *skb, struct net_device *netdev) { struct ibmveth_adapter *adapter = netdev_priv(netdev); - unsigned int desc_flags; - union ibmveth_buf_desc descs[6]; - int last, i; - int force_bounce = 0; - dma_addr_t dma_addr; + unsigned int desc_flags, total_bytes; + union ibmveth_buf_desc desc; + int i, queue_num = skb_get_queue_mapping(skb); unsigned long mss = 0; if (ibmveth_is_packet_unsupported(skb, netdev)) goto out; - - /* veth doesn't handle frag_list, so linearize the skb. - * When GRO is enabled SKB's can have frag_list. - */ - if (adapter->is_active_trunk && - skb_has_frag_list(skb) && __skb_linearize(skb)) { - netdev->stats.tx_dropped++; - goto out; - } - - /* - * veth handles a maximum of 6 segments including the header, so - * we have to linearize the skb if there are more than this. - */ - if (skb_shinfo(skb)->nr_frags > 5 && __skb_linearize(skb)) { - netdev->stats.tx_dropped++; - goto out; - } - /* veth can't checksum offload UDP */ if (skb->ip_summed == CHECKSUM_PARTIAL && ((skb->protocol == htons(ETH_P_IP) && @@ -1077,56 +1157,6 @@ static netdev_tx_t ibmveth_start_xmit(struct sk_buff *skb, desc_flags |= IBMVETH_BUF_LRG_SND; } -retry_bounce: - memset(descs, 0, sizeof(descs)); - - /* - * If a linear packet is below the rx threshold then - * copy it into the static bounce buffer. This avoids the - * cost of a TCE insert and remove. - */ - if (force_bounce || (!skb_is_nonlinear(skb) && - (skb->len < tx_copybreak))) { - skb_copy_from_linear_data(skb, adapter->bounce_buffer, - skb->len); - - descs[0].fields.flags_len = desc_flags | skb->len; - descs[0].fields.address = adapter->bounce_buffer_dma; - - if (ibmveth_send(adapter, descs, 0)) { - adapter->tx_send_failed++; - netdev->stats.tx_dropped++; - } else { - netdev->stats.tx_packets++; - netdev->stats.tx_bytes += skb->len; - } - - goto out; - } - - /* Map the header */ - dma_addr = dma_map_single(&adapter->vdev->dev, skb->data, - skb_headlen(skb), DMA_TO_DEVICE); - if (dma_mapping_error(&adapter->vdev->dev, dma_addr)) - goto map_failed; - - descs[0].fields.flags_len = desc_flags | skb_headlen(skb); - descs[0].fields.address = dma_addr; - - /* Map the frags */ - for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { - const skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; - - dma_addr = skb_frag_dma_map(&adapter->vdev->dev, frag, 0, - skb_frag_size(frag), DMA_TO_DEVICE); - - if (dma_mapping_error(&adapter->vdev->dev, dma_addr)) - goto map_failed_frags; - - descs[i+1].fields.flags_len = desc_flags | skb_frag_size(frag); - descs[i+1].fields.address = dma_addr; - } - if (skb->ip_summed == CHECKSUM_PARTIAL && skb_is_gso(skb)) { if (adapter->fw_large_send_support) { mss = (unsigned long)skb_shinfo(skb)->gso_size; @@ -1143,7 +1173,36 @@ retry_bounce: } } - if (ibmveth_send(adapter, descs, mss)) { + /* Copy header into mapped buffer */ + if (unlikely(skb->len > adapter->tx_ltb_size)) { + netdev_err(adapter->netdev, "tx: packet size (%u) exceeds ltb (%u)\n", + skb->len, adapter->tx_ltb_size); + netdev->stats.tx_dropped++; + goto out; + } + memcpy(adapter->tx_ltb_ptr[queue_num], skb->data, skb_headlen(skb)); + total_bytes = skb_headlen(skb); + /* Copy frags into mapped buffers */ + for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { + const skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; + + memcpy(adapter->tx_ltb_ptr[queue_num] + total_bytes, + skb_frag_address_safe(frag), skb_frag_size(frag)); + total_bytes += skb_frag_size(frag); + } + + if (unlikely(total_bytes != skb->len)) { + netdev_err(adapter->netdev, "tx: incorrect packet len copied into ltb (%u != %u)\n", + skb->len, total_bytes); + netdev->stats.tx_dropped++; + goto out; + } + desc.fields.flags_len = desc_flags | skb->len; + desc.fields.address = adapter->tx_ltb_dma[queue_num]; + /* finish writing to long_term_buff before VIOS accessing it */ + dma_wmb(); + + if (ibmveth_send(adapter, desc.desc, mss)) { adapter->tx_send_failed++; netdev->stats.tx_dropped++; } else { @@ -1151,41 +1210,11 @@ retry_bounce: netdev->stats.tx_bytes += skb->len; } - dma_unmap_single(&adapter->vdev->dev, - descs[0].fields.address, - descs[0].fields.flags_len & IBMVETH_BUF_LEN_MASK, - DMA_TO_DEVICE); - - for (i = 1; i < skb_shinfo(skb)->nr_frags + 1; i++) - dma_unmap_page(&adapter->vdev->dev, descs[i].fields.address, - descs[i].fields.flags_len & IBMVETH_BUF_LEN_MASK, - DMA_TO_DEVICE); - out: dev_consume_skb_any(skb); return NETDEV_TX_OK; -map_failed_frags: - last = i+1; - for (i = 1; i < last; i++) - dma_unmap_page(&adapter->vdev->dev, descs[i].fields.address, - descs[i].fields.flags_len & IBMVETH_BUF_LEN_MASK, - DMA_TO_DEVICE); - dma_unmap_single(&adapter->vdev->dev, - descs[0].fields.address, - descs[0].fields.flags_len & IBMVETH_BUF_LEN_MASK, - DMA_TO_DEVICE); -map_failed: - if (!firmware_has_feature(FW_FEATURE_CMO)) - netdev_err(netdev, "tx: unable to map xmit buffer\n"); - adapter->tx_map_failed++; - if (skb_linearize(skb)) { - netdev->stats.tx_dropped++; - goto out; - } - force_bounce = 1; - goto retry_bounce; } static void ibmveth_rx_mss_helper(struct sk_buff *skb, u16 mss, int lrg_pkt) @@ -1568,6 +1597,8 @@ static unsigned long ibmveth_get_desired_dma(struct vio_dev *vdev) ret = IBMVETH_BUFF_LIST_SIZE + IBMVETH_FILT_LIST_SIZE; ret += IOMMU_PAGE_ALIGN(netdev->mtu, tbl); + /* add size of mapped tx buffers */ + ret += IOMMU_PAGE_ALIGN(IBMVETH_MAX_TX_BUF_SIZE, tbl); for (i = 0; i < IBMVETH_NUM_BUFF_POOLS; i++) { /* add the size of the active receive buffers */ @@ -1660,8 +1691,7 @@ static int ibmveth_probe(struct vio_dev *dev, const struct vio_device_id *id) return -EINVAL; } - netdev = alloc_etherdev(sizeof(struct ibmveth_adapter)); - + netdev = alloc_etherdev_mqs(sizeof(struct ibmveth_adapter), IBMVETH_MAX_QUEUES, 1); if (!netdev) return -ENOMEM; @@ -1727,6 +1757,17 @@ static int ibmveth_probe(struct vio_dev *dev, const struct vio_device_id *id) kobject_uevent(kobj, KOBJ_ADD); } + rc = netif_set_real_num_tx_queues(netdev, ibmveth_real_max_tx_queues()); + if (rc) { + netdev_dbg(netdev, "failed to set number of tx queues rc=%d\n", + rc); + free_netdev(netdev); + return rc; + } + adapter->tx_ltb_size = PAGE_ALIGN(IBMVETH_MAX_TX_BUF_SIZE); + for (i = 0; i < IBMVETH_MAX_QUEUES; i++) + adapter->tx_ltb_ptr[i] = NULL; + netdev_dbg(netdev, "adapter @ 0x%p\n", adapter); netdev_dbg(netdev, "registering netdev...\n"); diff --git a/drivers/net/ethernet/ibm/ibmveth.h b/drivers/net/ethernet/ibm/ibmveth.h index 27dfff200166..daf6f615c03f 100644 --- a/drivers/net/ethernet/ibm/ibmveth.h +++ b/drivers/net/ethernet/ibm/ibmveth.h @@ -46,23 +46,23 @@ #define h_add_logical_lan_buffer(ua, buf) \ plpar_hcall_norets(H_ADD_LOGICAL_LAN_BUFFER, ua, buf) +/* FW allows us to send 6 descriptors but we only use one so mark + * the other 5 as unused (0) + */ static inline long h_send_logical_lan(unsigned long unit_address, - unsigned long desc1, unsigned long desc2, unsigned long desc3, - unsigned long desc4, unsigned long desc5, unsigned long desc6, - unsigned long corellator_in, unsigned long *corellator_out, - unsigned long mss, unsigned long large_send_support) + unsigned long desc, unsigned long corellator_in, + unsigned long *corellator_out, unsigned long mss, + unsigned long large_send_support) { long rc; unsigned long retbuf[PLPAR_HCALL9_BUFSIZE]; if (large_send_support) rc = plpar_hcall9(H_SEND_LOGICAL_LAN, retbuf, unit_address, - desc1, desc2, desc3, desc4, desc5, desc6, - corellator_in, mss); + desc, 0, 0, 0, 0, 0, corellator_in, mss); else rc = plpar_hcall9(H_SEND_LOGICAL_LAN, retbuf, unit_address, - desc1, desc2, desc3, desc4, desc5, desc6, - corellator_in); + desc, 0, 0, 0, 0, 0, corellator_in); *corellator_out = retbuf[0]; @@ -98,6 +98,8 @@ static inline long h_illan_attributes(unsigned long unit_address, #define IBMVETH_BUFF_LIST_SIZE 4096 #define IBMVETH_FILT_LIST_SIZE 4096 #define IBMVETH_MAX_BUF_SIZE (1024 * 128) +#define IBMVETH_MAX_TX_BUF_SIZE (1024 * 64) +#define IBMVETH_MAX_QUEUES 16U static int pool_size[] = { 512, 1024 * 2, 1024 * 16, 1024 * 32, 1024 * 64 }; static int pool_count[] = { 256, 512, 256, 256, 256 }; @@ -137,6 +139,9 @@ struct ibmveth_adapter { unsigned int mcastFilterSize; void * buffer_list_addr; void * filter_list_addr; + void *tx_ltb_ptr[IBMVETH_MAX_QUEUES]; + unsigned int tx_ltb_size; + dma_addr_t tx_ltb_dma[IBMVETH_MAX_QUEUES]; dma_addr_t buffer_list_dma; dma_addr_t filter_list_dma; struct ibmveth_buff_pool rx_buff_pool[IBMVETH_NUM_BUFF_POOLS]; @@ -145,8 +150,6 @@ struct ibmveth_adapter { int rx_csum; int large_send; bool is_active_trunk; - void *bounce_buffer; - dma_addr_t bounce_buffer_dma; u64 fw_ipv6_csum_support; u64 fw_ipv4_csum_support; diff --git a/drivers/net/ethernet/ibm/ibmvnic.c b/drivers/net/ethernet/ibm/ibmvnic.c index 5ab7c0f81e9a..65dbfbec487a 100644 --- a/drivers/net/ethernet/ibm/ibmvnic.c +++ b/drivers/net/ethernet/ibm/ibmvnic.c @@ -1262,7 +1262,7 @@ static int init_napi(struct ibmvnic_adapter *adapter) for (i = 0; i < adapter->req_rx_queues; i++) { netdev_dbg(adapter->netdev, "Adding napi[%d]\n", i); netif_napi_add(adapter->netdev, &adapter->napi[i], - ibmvnic_poll, NAPI_POLL_WEIGHT); + ibmvnic_poll); } adapter->num_active_rx_napi = adapter->req_rx_queues; diff --git a/drivers/net/ethernet/intel/e100.c b/drivers/net/ethernet/intel/e100.c index 11a884aa5082..560d1d442232 100644 --- a/drivers/net/ethernet/intel/e100.c +++ b/drivers/net/ethernet/intel/e100.c @@ -2431,8 +2431,8 @@ static void e100_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *info) { struct nic *nic = netdev_priv(netdev); - strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); - strlcpy(info->bus_info, pci_name(nic->pdev), + strscpy(info->driver, DRV_NAME, sizeof(info->driver)); + strscpy(info->bus_info, pci_name(nic->pdev), sizeof(info->bus_info)); } diff --git a/drivers/net/ethernet/intel/e1000/e1000_ethtool.c b/drivers/net/ethernet/intel/e1000/e1000_ethtool.c index 32803b0cf1e8..d06d29c6c037 100644 --- a/drivers/net/ethernet/intel/e1000/e1000_ethtool.c +++ b/drivers/net/ethernet/intel/e1000/e1000_ethtool.c @@ -531,10 +531,10 @@ static void e1000_get_drvinfo(struct net_device *netdev, { struct e1000_adapter *adapter = netdev_priv(netdev); - strlcpy(drvinfo->driver, e1000_driver_name, + strscpy(drvinfo->driver, e1000_driver_name, sizeof(drvinfo->driver)); - strlcpy(drvinfo->bus_info, pci_name(adapter->pdev), + strscpy(drvinfo->bus_info, pci_name(adapter->pdev), sizeof(drvinfo->bus_info)); } diff --git a/drivers/net/ethernet/intel/e1000/e1000_main.c b/drivers/net/ethernet/intel/e1000/e1000_main.c index 23299fc56199..61e60e4de600 100644 --- a/drivers/net/ethernet/intel/e1000/e1000_main.c +++ b/drivers/net/ethernet/intel/e1000/e1000_main.c @@ -1012,7 +1012,7 @@ static int e1000_probe(struct pci_dev *pdev, const struct pci_device_id *ent) netdev->netdev_ops = &e1000_netdev_ops; e1000_set_ethtool_ops(netdev); netdev->watchdog_timeo = 5 * HZ; - netif_napi_add(netdev, &adapter->napi, e1000_clean, 64); + netif_napi_add(netdev, &adapter->napi, e1000_clean); strncpy(netdev->name, pci_name(pdev), sizeof(netdev->name) - 1); diff --git a/drivers/net/ethernet/intel/e1000e/ethtool.c b/drivers/net/ethernet/intel/e1000e/ethtool.c index b80ae9a82224..51a5afe9df2f 100644 --- a/drivers/net/ethernet/intel/e1000e/ethtool.c +++ b/drivers/net/ethernet/intel/e1000e/ethtool.c @@ -639,7 +639,7 @@ static void e1000_get_drvinfo(struct net_device *netdev, { struct e1000_adapter *adapter = netdev_priv(netdev); - strlcpy(drvinfo->driver, e1000e_driver_name, sizeof(drvinfo->driver)); + strscpy(drvinfo->driver, e1000e_driver_name, sizeof(drvinfo->driver)); /* EEPROM image version # is reported as firmware version # for * PCI-E controllers @@ -650,7 +650,7 @@ static void e1000_get_drvinfo(struct net_device *netdev, (adapter->eeprom_vers & 0x0FF0) >> 4, (adapter->eeprom_vers & 0x000F)); - strlcpy(drvinfo->bus_info, pci_name(adapter->pdev), + strscpy(drvinfo->bus_info, pci_name(adapter->pdev), sizeof(drvinfo->bus_info)); } diff --git a/drivers/net/ethernet/intel/e1000e/netdev.c b/drivers/net/ethernet/intel/e1000e/netdev.c index 321f2a95ae3a..49e926959ad3 100644 --- a/drivers/net/ethernet/intel/e1000e/netdev.c +++ b/drivers/net/ethernet/intel/e1000e/netdev.c @@ -7267,7 +7267,7 @@ static void e1000_print_device_info(struct e1000_adapter *adapter) ret_val = e1000_read_pba_string_generic(hw, pba_str, E1000_PBANUM_LENGTH); if (ret_val) - strlcpy((char *)pba_str, "Unknown", sizeof(pba_str)); + strscpy((char *)pba_str, "Unknown", sizeof(pba_str)); e_info("MAC: %d, PHY: %d, PBA No: %s\n", hw->mac.type, hw->phy.type, pba_str); } @@ -7479,8 +7479,8 @@ static int e1000_probe(struct pci_dev *pdev, const struct pci_device_id *ent) netdev->netdev_ops = &e1000e_netdev_ops; e1000e_set_ethtool_ops(netdev); netdev->watchdog_timeo = 5 * HZ; - netif_napi_add(netdev, &adapter->napi, e1000e_poll, 64); - strlcpy(netdev->name, pci_name(pdev), sizeof(netdev->name)); + netif_napi_add(netdev, &adapter->napi, e1000e_poll); + strscpy(netdev->name, pci_name(pdev), sizeof(netdev->name)); netdev->mem_start = mmio_start; netdev->mem_end = mmio_start + mmio_len; @@ -7676,7 +7676,7 @@ static int e1000_probe(struct pci_dev *pdev, const struct pci_device_id *ent) if (hw->mac.type >= e1000_pch_cnp) adapter->flags2 |= FLAG2_ENABLE_S0IX_FLOWS; - strlcpy(netdev->name, "eth%d", sizeof(netdev->name)); + strscpy(netdev->name, "eth%d", sizeof(netdev->name)); err = register_netdev(netdev); if (err) goto err_register; diff --git a/drivers/net/ethernet/intel/e1000e/phy.c b/drivers/net/ethernet/intel/e1000e/phy.c index fd07c3679bb1..060b263348ce 100644 --- a/drivers/net/ethernet/intel/e1000e/phy.c +++ b/drivers/net/ethernet/intel/e1000e/phy.c @@ -2697,9 +2697,14 @@ static s32 e1000_access_phy_wakeup_reg_bm(struct e1000_hw *hw, u32 offset, void e1000_power_up_phy_copper(struct e1000_hw *hw) { u16 mii_reg = 0; + int ret; /* The PHY will retain its settings across a power down/up cycle */ - e1e_rphy(hw, MII_BMCR, &mii_reg); + ret = e1e_rphy(hw, MII_BMCR, &mii_reg); + if (ret) { + e_dbg("Error reading PHY register\n"); + return; + } mii_reg &= ~BMCR_PDOWN; e1e_wphy(hw, MII_BMCR, mii_reg); } @@ -2715,9 +2720,14 @@ void e1000_power_up_phy_copper(struct e1000_hw *hw) void e1000_power_down_phy_copper(struct e1000_hw *hw) { u16 mii_reg = 0; + int ret; /* The PHY will retain its settings across a power down/up cycle */ - e1e_rphy(hw, MII_BMCR, &mii_reg); + ret = e1e_rphy(hw, MII_BMCR, &mii_reg); + if (ret) { + e_dbg("Error reading PHY register\n"); + return; + } mii_reg |= BMCR_PDOWN; e1e_wphy(hw, MII_BMCR, mii_reg); usleep_range(1000, 2000); @@ -3037,7 +3047,11 @@ s32 e1000_link_stall_workaround_hv(struct e1000_hw *hw) return 0; /* Do not apply workaround if in PHY loopback bit 14 set */ - e1e_rphy(hw, MII_BMCR, &data); + ret_val = e1e_rphy(hw, MII_BMCR, &data); + if (ret_val) { + e_dbg("Error reading PHY register\n"); + return ret_val; + } if (data & BMCR_LOOPBACK) return 0; diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_main.c b/drivers/net/ethernet/intel/fm10k/fm10k_main.c index 3362f26d7f99..4a6630586ec9 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_main.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_main.c @@ -1595,8 +1595,7 @@ static int fm10k_alloc_q_vector(struct fm10k_intfc *interface, return -ENOMEM; /* initialize NAPI */ - netif_napi_add(interface->netdev, &q_vector->napi, - fm10k_poll, NAPI_POLL_WEIGHT); + netif_napi_add(interface->netdev, &q_vector->napi, fm10k_poll); /* tie q_vector and interface together */ interface->q_vector[v_idx] = q_vector; diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h index d86b6d349ea9..9a60d6b207f7 100644 --- a/drivers/net/ethernet/intel/i40e/i40e.h +++ b/drivers/net/ethernet/intel/i40e/i40e.h @@ -399,6 +399,20 @@ struct i40e_ddp_old_profile_list { I40E_FLEX_54_MASK | I40E_FLEX_55_MASK | \ I40E_FLEX_56_MASK | I40E_FLEX_57_MASK) +#define I40E_QINT_TQCTL_VAL(qp, vector, nextq_type) \ + (I40E_QINT_TQCTL_CAUSE_ENA_MASK | \ + (I40E_TX_ITR << I40E_QINT_TQCTL_ITR_INDX_SHIFT) | \ + ((vector) << I40E_QINT_TQCTL_MSIX_INDX_SHIFT) | \ + ((qp) << I40E_QINT_TQCTL_NEXTQ_INDX_SHIFT) | \ + (I40E_QUEUE_TYPE_##nextq_type << I40E_QINT_TQCTL_NEXTQ_TYPE_SHIFT)) + +#define I40E_QINT_RQCTL_VAL(qp, vector, nextq_type) \ + (I40E_QINT_RQCTL_CAUSE_ENA_MASK | \ + (I40E_RX_ITR << I40E_QINT_RQCTL_ITR_INDX_SHIFT) | \ + ((vector) << I40E_QINT_RQCTL_MSIX_INDX_SHIFT) | \ + ((qp) << I40E_QINT_RQCTL_NEXTQ_INDX_SHIFT) | \ + (I40E_QUEUE_TYPE_##nextq_type << I40E_QINT_RQCTL_NEXTQ_TYPE_SHIFT)) + struct i40e_flex_pit { struct list_head list; u16 src_offset; diff --git a/drivers/net/ethernet/intel/i40e/i40e_common.c b/drivers/net/ethernet/intel/i40e/i40e_common.c index 2819e261a126..4f01e2a6b6bb 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_common.c +++ b/drivers/net/ethernet/intel/i40e/i40e_common.c @@ -27,6 +27,7 @@ i40e_status i40e_set_mac_type(struct i40e_hw *hw) case I40E_DEV_ID_QSFP_A: case I40E_DEV_ID_QSFP_B: case I40E_DEV_ID_QSFP_C: + case I40E_DEV_ID_1G_BASE_T_BC: case I40E_DEV_ID_5G_BASE_T_BC: case I40E_DEV_ID_10G_BASE_T: case I40E_DEV_ID_10G_BASE_T4: @@ -4974,6 +4975,7 @@ i40e_status i40e_write_phy_register(struct i40e_hw *hw, status = i40e_write_phy_register_clause22(hw, reg, phy_addr, value); break; + case I40E_DEV_ID_1G_BASE_T_BC: case I40E_DEV_ID_5G_BASE_T_BC: case I40E_DEV_ID_10G_BASE_T: case I40E_DEV_ID_10G_BASE_T4: @@ -5012,6 +5014,7 @@ i40e_status i40e_read_phy_register(struct i40e_hw *hw, status = i40e_read_phy_register_clause22(hw, reg, phy_addr, value); break; + case I40E_DEV_ID_1G_BASE_T_BC: case I40E_DEV_ID_5G_BASE_T_BC: case I40E_DEV_ID_10G_BASE_T: case I40E_DEV_ID_10G_BASE_T4: diff --git a/drivers/net/ethernet/intel/i40e/i40e_devids.h b/drivers/net/ethernet/intel/i40e/i40e_devids.h index 2610338002fe..d9c51a238dcc 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_devids.h +++ b/drivers/net/ethernet/intel/i40e/i40e_devids.h @@ -24,8 +24,10 @@ #define I40E_DEV_ID_10G_B 0x104F #define I40E_DEV_ID_10G_SFP 0x104E #define I40E_DEV_ID_5G_BASE_T_BC 0x101F +#define I40E_DEV_ID_1G_BASE_T_BC 0x0DD2 #define I40E_IS_X710TL_DEVICE(d) \ - (((d) == I40E_DEV_ID_5G_BASE_T_BC) || \ + (((d) == I40E_DEV_ID_1G_BASE_T_BC) || \ + ((d) == I40E_DEV_ID_5G_BASE_T_BC) || \ ((d) == I40E_DEV_ID_10G_BASE_T_BC)) #define I40E_DEV_ID_KX_X722 0x37CE #define I40E_DEV_ID_QSFP_X722 0x37CF diff --git a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c index e9cd0fa6a0d2..7e75706f76db 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c +++ b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c @@ -2001,10 +2001,10 @@ static void i40e_get_drvinfo(struct net_device *netdev, struct i40e_vsi *vsi = np->vsi; struct i40e_pf *pf = vsi->back; - strlcpy(drvinfo->driver, i40e_driver_name, sizeof(drvinfo->driver)); - strlcpy(drvinfo->fw_version, i40e_nvm_version_str(&pf->hw), + strscpy(drvinfo->driver, i40e_driver_name, sizeof(drvinfo->driver)); + strscpy(drvinfo->fw_version, i40e_nvm_version_str(&pf->hw), sizeof(drvinfo->fw_version)); - strlcpy(drvinfo->bus_info, pci_name(pf->pdev), + strscpy(drvinfo->bus_info, pci_name(pf->pdev), sizeof(drvinfo->bus_info)); drvinfo->n_priv_flags = I40E_PRIV_FLAGS_STR_LEN; if (pf->hw.pf_id == 0) diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index e3d9804aeb25..2c07fa8ecfc8 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -66,6 +66,7 @@ static const struct pci_device_id i40e_pci_tbl[] = { {PCI_VDEVICE(INTEL, I40E_DEV_ID_QSFP_A), 0}, {PCI_VDEVICE(INTEL, I40E_DEV_ID_QSFP_B), 0}, {PCI_VDEVICE(INTEL, I40E_DEV_ID_QSFP_C), 0}, + {PCI_VDEVICE(INTEL, I40E_DEV_ID_1G_BASE_T_BC), 0}, {PCI_VDEVICE(INTEL, I40E_DEV_ID_10G_BASE_T), 0}, {PCI_VDEVICE(INTEL, I40E_DEV_ID_10G_BASE_T4), 0}, {PCI_VDEVICE(INTEL, I40E_DEV_ID_10G_BASE_T_BC), 0}, @@ -3878,7 +3879,7 @@ static void i40e_vsi_configure_msix(struct i40e_vsi *vsi) wr32(hw, I40E_PFINT_RATEN(vector - 1), i40e_intrl_usec_to_reg(vsi->int_rate_limit)); - /* Linked list for the queuepairs assigned to this vector */ + /* begin of linked list for RX queue assigned to this vector */ wr32(hw, I40E_PFINT_LNKLSTN(vector - 1), qp); for (q = 0; q < q_vector->num_ringpairs; q++) { u32 nextqp = has_xdp ? qp + vsi->alloc_queue_pairs : qp; @@ -3894,6 +3895,7 @@ static void i40e_vsi_configure_msix(struct i40e_vsi *vsi) wr32(hw, I40E_QINT_RQCTL(qp), val); if (has_xdp) { + /* TX queue with next queue set to TX */ val = I40E_QINT_TQCTL_CAUSE_ENA_MASK | (I40E_TX_ITR << I40E_QINT_TQCTL_ITR_INDX_SHIFT) | (vector << I40E_QINT_TQCTL_MSIX_INDX_SHIFT) | @@ -3903,7 +3905,7 @@ static void i40e_vsi_configure_msix(struct i40e_vsi *vsi) wr32(hw, I40E_QINT_TQCTL(nextqp), val); } - + /* TX queue with next RX or end of linked list */ val = I40E_QINT_TQCTL_CAUSE_ENA_MASK | (I40E_TX_ITR << I40E_QINT_TQCTL_ITR_INDX_SHIFT) | (vector << I40E_QINT_TQCTL_MSIX_INDX_SHIFT) | @@ -3972,7 +3974,6 @@ static void i40e_configure_msi_and_legacy(struct i40e_vsi *vsi) struct i40e_q_vector *q_vector = vsi->q_vectors[0]; struct i40e_pf *pf = vsi->back; struct i40e_hw *hw = &pf->hw; - u32 val; /* set the ITR configuration */ q_vector->rx.next_update = jiffies + 1; @@ -3989,28 +3990,20 @@ static void i40e_configure_msi_and_legacy(struct i40e_vsi *vsi) /* FIRSTQ_INDX = 0, FIRSTQ_TYPE = 0 (rx) */ wr32(hw, I40E_PFINT_LNKLST0, 0); - /* Associate the queue pair to the vector and enable the queue int */ - val = I40E_QINT_RQCTL_CAUSE_ENA_MASK | - (I40E_RX_ITR << I40E_QINT_RQCTL_ITR_INDX_SHIFT) | - (nextqp << I40E_QINT_RQCTL_NEXTQ_INDX_SHIFT)| - (I40E_QUEUE_TYPE_TX << I40E_QINT_TQCTL_NEXTQ_TYPE_SHIFT); - - wr32(hw, I40E_QINT_RQCTL(0), val); + /* Associate the queue pair to the vector and enable the queue + * interrupt RX queue in linked list with next queue set to TX + */ + wr32(hw, I40E_QINT_RQCTL(0), I40E_QINT_RQCTL_VAL(nextqp, 0, TX)); if (i40e_enabled_xdp_vsi(vsi)) { - val = I40E_QINT_TQCTL_CAUSE_ENA_MASK | - (I40E_TX_ITR << I40E_QINT_TQCTL_ITR_INDX_SHIFT)| - (I40E_QUEUE_TYPE_TX - << I40E_QINT_TQCTL_NEXTQ_TYPE_SHIFT); - - wr32(hw, I40E_QINT_TQCTL(nextqp), val); + /* TX queue in linked list with next queue set to TX */ + wr32(hw, I40E_QINT_TQCTL(nextqp), + I40E_QINT_TQCTL_VAL(nextqp, 0, TX)); } - val = I40E_QINT_TQCTL_CAUSE_ENA_MASK | - (I40E_TX_ITR << I40E_QINT_TQCTL_ITR_INDX_SHIFT) | - (I40E_QUEUE_END_OF_LIST << I40E_QINT_TQCTL_NEXTQ_INDX_SHIFT); - - wr32(hw, I40E_QINT_TQCTL(0), val); + /* last TX queue so the next RX queue doesn't matter */ + wr32(hw, I40E_QINT_TQCTL(0), + I40E_QINT_TQCTL_VAL(I40E_QUEUE_END_OF_LIST, 0, RX)); i40e_flush(hw); } @@ -10724,7 +10717,7 @@ static void i40e_send_version(struct i40e_pf *pf) dv.minor_version = 0xff; dv.build_version = 0xff; dv.subbuild_version = 0; - strlcpy(dv.driver_string, UTS_RELEASE, sizeof(dv.driver_string)); + strscpy(dv.driver_string, UTS_RELEASE, sizeof(dv.driver_string)); i40e_aq_send_driver_version(&pf->hw, &dv, NULL); } @@ -11948,8 +11941,7 @@ static int i40e_vsi_alloc_q_vector(struct i40e_vsi *vsi, int v_idx) cpumask_copy(&q_vector->affinity_mask, cpu_possible_mask); if (vsi->netdev) - netif_napi_add(vsi->netdev, &q_vector->napi, - i40e_napi_poll, NAPI_POLL_WEIGHT); + netif_napi_add(vsi->netdev, &q_vector->napi, i40e_napi_poll); /* tie q_vector and vsi together */ vsi->q_vectors[v_idx] = q_vector; @@ -16072,23 +16064,23 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent) switch (hw->bus.speed) { case i40e_bus_speed_8000: - strlcpy(speed, "8.0", PCI_SPEED_SIZE); break; + strscpy(speed, "8.0", PCI_SPEED_SIZE); break; case i40e_bus_speed_5000: - strlcpy(speed, "5.0", PCI_SPEED_SIZE); break; + strscpy(speed, "5.0", PCI_SPEED_SIZE); break; case i40e_bus_speed_2500: - strlcpy(speed, "2.5", PCI_SPEED_SIZE); break; + strscpy(speed, "2.5", PCI_SPEED_SIZE); break; default: break; } switch (hw->bus.width) { case i40e_bus_width_pcie_x8: - strlcpy(width, "8", PCI_WIDTH_SIZE); break; + strscpy(width, "8", PCI_WIDTH_SIZE); break; case i40e_bus_width_pcie_x4: - strlcpy(width, "4", PCI_WIDTH_SIZE); break; + strscpy(width, "4", PCI_WIDTH_SIZE); break; case i40e_bus_width_pcie_x2: - strlcpy(width, "2", PCI_WIDTH_SIZE); break; + strscpy(width, "2", PCI_WIDTH_SIZE); break; case i40e_bus_width_pcie_x1: - strlcpy(width, "1", PCI_WIDTH_SIZE); break; + strscpy(width, "1", PCI_WIDTH_SIZE); break; default: break; } diff --git a/drivers/net/ethernet/intel/i40e/i40e_ptp.c b/drivers/net/ethernet/intel/i40e/i40e_ptp.c index 2d3533f38d7b..ffea0c9c82f1 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_ptp.c +++ b/drivers/net/ethernet/intel/i40e/i40e_ptp.c @@ -1390,7 +1390,7 @@ static long i40e_ptp_create_clock(struct i40e_pf *pf) if (!IS_ERR_OR_NULL(pf->ptp_clock)) return 0; - strlcpy(pf->ptp_caps.name, i40e_driver_name, + strscpy(pf->ptp_caps.name, i40e_driver_name, sizeof(pf->ptp_caps.name) - 1); pf->ptp_caps.owner = THIS_MODULE; pf->ptp_caps.max_adj = 999999999; diff --git a/drivers/net/ethernet/intel/iavf/iavf_ethtool.c b/drivers/net/ethernet/intel/iavf/iavf_ethtool.c index e535d4c3da49..a056e1545615 100644 --- a/drivers/net/ethernet/intel/iavf/iavf_ethtool.c +++ b/drivers/net/ethernet/intel/iavf/iavf_ethtool.c @@ -581,9 +581,9 @@ static void iavf_get_drvinfo(struct net_device *netdev, { struct iavf_adapter *adapter = netdev_priv(netdev); - strlcpy(drvinfo->driver, iavf_driver_name, 32); - strlcpy(drvinfo->fw_version, "N/A", 4); - strlcpy(drvinfo->bus_info, pci_name(adapter->pdev), 32); + strscpy(drvinfo->driver, iavf_driver_name, 32); + strscpy(drvinfo->fw_version, "N/A", 4); + strscpy(drvinfo->bus_info, pci_name(adapter->pdev), 32); drvinfo->n_priv_flags = IAVF_PRIV_FLAGS_STR_LEN; } diff --git a/drivers/net/ethernet/intel/iavf/iavf_main.c b/drivers/net/ethernet/intel/iavf/iavf_main.c index 0c89f16bf1e2..3fc572341781 100644 --- a/drivers/net/ethernet/intel/iavf/iavf_main.c +++ b/drivers/net/ethernet/intel/iavf/iavf_main.c @@ -1267,66 +1267,138 @@ static void iavf_up_complete(struct iavf_adapter *adapter) } /** - * iavf_down - Shutdown the connection processing + * iavf_clear_mac_vlan_filters - Remove mac and vlan filters not sent to PF + * yet and mark other to be removed. * @adapter: board private structure - * - * Expects to be called while holding the __IAVF_IN_CRITICAL_TASK bit lock. **/ -void iavf_down(struct iavf_adapter *adapter) +static void iavf_clear_mac_vlan_filters(struct iavf_adapter *adapter) { - struct net_device *netdev = adapter->netdev; - struct iavf_vlan_filter *vlf; - struct iavf_cloud_filter *cf; - struct iavf_fdir_fltr *fdir; - struct iavf_mac_filter *f; - struct iavf_adv_rss *rss; - - if (adapter->state <= __IAVF_DOWN_PENDING) - return; - - netif_carrier_off(netdev); - netif_tx_disable(netdev); - adapter->link_up = false; - iavf_napi_disable_all(adapter); - iavf_irq_disable(adapter); + struct iavf_vlan_filter *vlf, *vlftmp; + struct iavf_mac_filter *f, *ftmp; spin_lock_bh(&adapter->mac_vlan_list_lock); - /* clear the sync flag on all filters */ __dev_uc_unsync(adapter->netdev, NULL); __dev_mc_unsync(adapter->netdev, NULL); /* remove all MAC filters */ - list_for_each_entry(f, &adapter->mac_filter_list, list) { - f->remove = true; + list_for_each_entry_safe(f, ftmp, &adapter->mac_filter_list, + list) { + if (f->add) { + list_del(&f->list); + kfree(f); + } else { + f->remove = true; + } } /* remove all VLAN filters */ - list_for_each_entry(vlf, &adapter->vlan_filter_list, list) { - vlf->remove = true; + list_for_each_entry_safe(vlf, vlftmp, &adapter->vlan_filter_list, + list) { + if (vlf->add) { + list_del(&vlf->list); + kfree(vlf); + } else { + vlf->remove = true; + } } - spin_unlock_bh(&adapter->mac_vlan_list_lock); +} + +/** + * iavf_clear_cloud_filters - Remove cloud filters not sent to PF yet and + * mark other to be removed. + * @adapter: board private structure + **/ +static void iavf_clear_cloud_filters(struct iavf_adapter *adapter) +{ + struct iavf_cloud_filter *cf, *cftmp; /* remove all cloud filters */ spin_lock_bh(&adapter->cloud_filter_list_lock); - list_for_each_entry(cf, &adapter->cloud_filter_list, list) { - cf->del = true; + list_for_each_entry_safe(cf, cftmp, &adapter->cloud_filter_list, + list) { + if (cf->add) { + list_del(&cf->list); + kfree(cf); + adapter->num_cloud_filters--; + } else { + cf->del = true; + } } spin_unlock_bh(&adapter->cloud_filter_list_lock); +} + +/** + * iavf_clear_fdir_filters - Remove fdir filters not sent to PF yet and mark + * other to be removed. + * @adapter: board private structure + **/ +static void iavf_clear_fdir_filters(struct iavf_adapter *adapter) +{ + struct iavf_fdir_fltr *fdir, *fdirtmp; /* remove all Flow Director filters */ spin_lock_bh(&adapter->fdir_fltr_lock); - list_for_each_entry(fdir, &adapter->fdir_list_head, list) { - fdir->state = IAVF_FDIR_FLTR_DEL_REQUEST; + list_for_each_entry_safe(fdir, fdirtmp, &adapter->fdir_list_head, + list) { + if (fdir->state == IAVF_FDIR_FLTR_ADD_REQUEST) { + list_del(&fdir->list); + kfree(fdir); + adapter->fdir_active_fltr--; + } else { + fdir->state = IAVF_FDIR_FLTR_DEL_REQUEST; + } } spin_unlock_bh(&adapter->fdir_fltr_lock); +} + +/** + * iavf_clear_adv_rss_conf - Remove adv rss conf not sent to PF yet and mark + * other to be removed. + * @adapter: board private structure + **/ +static void iavf_clear_adv_rss_conf(struct iavf_adapter *adapter) +{ + struct iavf_adv_rss *rss, *rsstmp; /* remove all advance RSS configuration */ spin_lock_bh(&adapter->adv_rss_lock); - list_for_each_entry(rss, &adapter->adv_rss_list_head, list) - rss->state = IAVF_ADV_RSS_DEL_REQUEST; + list_for_each_entry_safe(rss, rsstmp, &adapter->adv_rss_list_head, + list) { + if (rss->state == IAVF_ADV_RSS_ADD_REQUEST) { + list_del(&rss->list); + kfree(rss); + } else { + rss->state = IAVF_ADV_RSS_DEL_REQUEST; + } + } spin_unlock_bh(&adapter->adv_rss_lock); +} + +/** + * iavf_down - Shutdown the connection processing + * @adapter: board private structure + * + * Expects to be called while holding the __IAVF_IN_CRITICAL_TASK bit lock. + **/ +void iavf_down(struct iavf_adapter *adapter) +{ + struct net_device *netdev = adapter->netdev; + + if (adapter->state <= __IAVF_DOWN_PENDING) + return; + + netif_carrier_off(netdev); + netif_tx_disable(netdev); + adapter->link_up = false; + iavf_napi_disable_all(adapter); + iavf_irq_disable(adapter); + + iavf_clear_mac_vlan_filters(adapter); + iavf_clear_cloud_filters(adapter); + iavf_clear_fdir_filters(adapter); + iavf_clear_adv_rss_conf(adapter); if (!(adapter->flags & IAVF_FLAG_PF_COMMS_FAILED)) { /* cancel any current operation */ @@ -1335,11 +1407,16 @@ void iavf_down(struct iavf_adapter *adapter) * here for this to complete. The watchdog is still running * and it will take care of this. */ - adapter->aq_required = IAVF_FLAG_AQ_DEL_MAC_FILTER; - adapter->aq_required |= IAVF_FLAG_AQ_DEL_VLAN_FILTER; - adapter->aq_required |= IAVF_FLAG_AQ_DEL_CLOUD_FILTER; - adapter->aq_required |= IAVF_FLAG_AQ_DEL_FDIR_FILTER; - adapter->aq_required |= IAVF_FLAG_AQ_DEL_ADV_RSS_CFG; + if (!list_empty(&adapter->mac_filter_list)) + adapter->aq_required |= IAVF_FLAG_AQ_DEL_MAC_FILTER; + if (!list_empty(&adapter->vlan_filter_list)) + adapter->aq_required |= IAVF_FLAG_AQ_DEL_VLAN_FILTER; + if (!list_empty(&adapter->cloud_filter_list)) + adapter->aq_required |= IAVF_FLAG_AQ_DEL_CLOUD_FILTER; + if (!list_empty(&adapter->fdir_list_head)) + adapter->aq_required |= IAVF_FLAG_AQ_DEL_FDIR_FILTER; + if (!list_empty(&adapter->adv_rss_list_head)) + adapter->aq_required |= IAVF_FLAG_AQ_DEL_ADV_RSS_CFG; adapter->aq_required |= IAVF_FLAG_AQ_DISABLE_QUEUES; } @@ -1754,7 +1831,7 @@ static int iavf_alloc_q_vectors(struct iavf_adapter *adapter) q_vector->reg_idx = q_idx; cpumask_copy(&q_vector->affinity_mask, cpu_possible_mask); netif_napi_add(adapter->netdev, &q_vector->napi, - iavf_napi_poll, NAPI_POLL_WEIGHT); + iavf_napi_poll); } return 0; @@ -4178,6 +4255,7 @@ err_unlock: static int iavf_close(struct net_device *netdev) { struct iavf_adapter *adapter = netdev_priv(netdev); + u64 aq_to_restore; int status; mutex_lock(&adapter->crit_lock); @@ -4190,6 +4268,29 @@ static int iavf_close(struct net_device *netdev) set_bit(__IAVF_VSI_DOWN, adapter->vsi.state); if (CLIENT_ENABLED(adapter)) adapter->flags |= IAVF_FLAG_CLIENT_NEEDS_CLOSE; + /* We cannot send IAVF_FLAG_AQ_GET_OFFLOAD_VLAN_V2_CAPS before + * IAVF_FLAG_AQ_DISABLE_QUEUES because in such case there is rtnl + * deadlock with adminq_task() until iavf_close timeouts. We must send + * IAVF_FLAG_AQ_GET_CONFIG before IAVF_FLAG_AQ_DISABLE_QUEUES to make + * disable queues possible for vf. Give only necessary flags to + * iavf_down and save other to set them right before iavf_close() + * returns, when IAVF_FLAG_AQ_DISABLE_QUEUES will be already sent and + * iavf will be in DOWN state. + */ + aq_to_restore = adapter->aq_required; + adapter->aq_required &= IAVF_FLAG_AQ_GET_CONFIG; + + /* Remove flags which we do not want to send after close or we want to + * send before disable queues. + */ + aq_to_restore &= ~(IAVF_FLAG_AQ_GET_CONFIG | + IAVF_FLAG_AQ_ENABLE_QUEUES | + IAVF_FLAG_AQ_CONFIGURE_QUEUES | + IAVF_FLAG_AQ_ADD_VLAN_FILTER | + IAVF_FLAG_AQ_ADD_MAC_FILTER | + IAVF_FLAG_AQ_ADD_CLOUD_FILTER | + IAVF_FLAG_AQ_ADD_FDIR_FILTER | + IAVF_FLAG_AQ_ADD_ADV_RSS_CFG); iavf_down(adapter); iavf_change_state(adapter, __IAVF_DOWN_PENDING); @@ -4213,6 +4314,10 @@ static int iavf_close(struct net_device *netdev) msecs_to_jiffies(500)); if (!status) netdev_warn(netdev, "Device resources not yet released\n"); + + mutex_lock(&adapter->crit_lock); + adapter->aq_required |= aq_to_restore; + mutex_unlock(&adapter->crit_lock); return 0; } diff --git a/drivers/net/ethernet/intel/ice/ice.h b/drivers/net/ethernet/intel/ice/ice.h index 841fa149c407..001500afc4a6 100644 --- a/drivers/net/ethernet/intel/ice/ice.h +++ b/drivers/net/ethernet/intel/ice/ice.h @@ -864,6 +864,7 @@ ice_fetch_u64_stats_per_ring(struct u64_stats_sync *syncp, struct ice_q_stats stats, u64 *pkts, u64 *bytes); int ice_up(struct ice_vsi *vsi); int ice_down(struct ice_vsi *vsi); +int ice_down_up(struct ice_vsi *vsi); int ice_vsi_cfg(struct ice_vsi *vsi); struct ice_vsi *ice_lb_vsi_setup(struct ice_pf *pf, struct ice_port_info *pi); int ice_vsi_determine_xdp_res(struct ice_vsi *vsi); diff --git a/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h b/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h index 9939238573a4..1bdc70aa979d 100644 --- a/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h +++ b/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h @@ -1423,6 +1423,56 @@ struct ice_aqc_set_port_id_led { u8 rsvd[13]; }; +/* Get Port Options (indirect, 0x06EA) */ +struct ice_aqc_get_port_options { + u8 lport_num; + u8 lport_num_valid; + u8 port_options_count; +#define ICE_AQC_PORT_OPT_COUNT_M GENMASK(3, 0) +#define ICE_AQC_PORT_OPT_MAX 16 + + u8 innermost_phy_index; + u8 port_options; +#define ICE_AQC_PORT_OPT_ACTIVE_M GENMASK(3, 0) +#define ICE_AQC_PORT_OPT_VALID BIT(7) + + u8 pending_port_option_status; +#define ICE_AQC_PENDING_PORT_OPT_IDX_M GENMASK(3, 0) +#define ICE_AQC_PENDING_PORT_OPT_VALID BIT(7) + + u8 rsvd[2]; + __le32 addr_high; + __le32 addr_low; +}; + +struct ice_aqc_get_port_options_elem { + u8 pmd; +#define ICE_AQC_PORT_OPT_PMD_COUNT_M GENMASK(3, 0) + + u8 max_lane_speed; +#define ICE_AQC_PORT_OPT_MAX_LANE_M GENMASK(3, 0) +#define ICE_AQC_PORT_OPT_MAX_LANE_100M 0 +#define ICE_AQC_PORT_OPT_MAX_LANE_1G 1 +#define ICE_AQC_PORT_OPT_MAX_LANE_2500M 2 +#define ICE_AQC_PORT_OPT_MAX_LANE_5G 3 +#define ICE_AQC_PORT_OPT_MAX_LANE_10G 4 +#define ICE_AQC_PORT_OPT_MAX_LANE_25G 5 +#define ICE_AQC_PORT_OPT_MAX_LANE_50G 6 +#define ICE_AQC_PORT_OPT_MAX_LANE_100G 7 + + u8 global_scid[2]; + u8 phy_scid[2]; + u8 pf2port_cid[2]; +}; + +/* Set Port Option (direct, 0x06EB) */ +struct ice_aqc_set_port_option { + u8 lport_num; + u8 lport_num_valid; + u8 selected_port_option; + u8 rsvd[13]; +}; + /* Set/Get GPIO (direct, 0x06EC/0x06ED) */ struct ice_aqc_gpio { __le16 gpio_ctrl_handle; @@ -1489,6 +1539,12 @@ struct ice_aqc_nvm { #define ICE_AQC_NVM_PERST_FLAG 1 #define ICE_AQC_NVM_EMPR_FLAG 2 #define ICE_AQC_NVM_EMPR_ENA BIT(0) /* Write Activate reply only */ + /* For Write Activate, several flags are sent as part of a separate + * flags2 field using a separate byte. For simplicity of the software + * interface, we pass the flags as a 16 bit value so these flags are + * all offset by 8 bits + */ +#define ICE_AQC_NVM_ACTIV_REQ_EMPR BIT(8) /* NVM Write Activate only */ __le16 module_typeid; __le16 length; #define ICE_AQC_NVM_ERASE_LEN 0xFFFF @@ -2082,6 +2138,8 @@ struct ice_aq_desc { struct ice_aqc_gpio read_write_gpio; struct ice_aqc_sff_eeprom read_write_sff_param; struct ice_aqc_set_port_id_led set_port_id_led; + struct ice_aqc_get_port_options get_port_options; + struct ice_aqc_set_port_option set_port_option; struct ice_aqc_get_sw_cfg get_sw_conf; struct ice_aqc_set_port_params set_port_params; struct ice_aqc_sw_rules sw_rules; @@ -2243,6 +2301,8 @@ enum ice_adminq_opc { ice_aqc_opc_read_i2c = 0x06E2, ice_aqc_opc_write_i2c = 0x06E3, ice_aqc_opc_set_port_id_led = 0x06E9, + ice_aqc_opc_get_port_options = 0x06EA, + ice_aqc_opc_set_port_option = 0x06EB, ice_aqc_opc_set_gpio = 0x06EC, ice_aqc_opc_get_gpio = 0x06ED, ice_aqc_opc_sff_eeprom = 0x06EE, diff --git a/drivers/net/ethernet/intel/ice/ice_base.c b/drivers/net/ethernet/intel/ice/ice_base.c index 1e3243808178..9e36f01dfa4f 100644 --- a/drivers/net/ethernet/intel/ice/ice_base.c +++ b/drivers/net/ethernet/intel/ice/ice_base.c @@ -130,8 +130,7 @@ static int ice_vsi_alloc_q_vector(struct ice_vsi *vsi, u16 v_idx) * handler here (i.e. resume, reset/rebuild, etc.) */ if (vsi->netdev) - netif_napi_add(vsi->netdev, &q_vector->napi, ice_napi_poll, - NAPI_POLL_WEIGHT); + netif_napi_add(vsi->netdev, &q_vector->napi, ice_napi_poll); out: /* tie q_vector and VSI together */ @@ -405,7 +404,7 @@ static int ice_setup_rx_ctx(struct ice_rx_ring *ring) /* Strip the Ethernet CRC bytes before the packet is posted to host * memory. */ - rlan_ctx.crcstrip = 1; + rlan_ctx.crcstrip = !(ring->flags & ICE_RX_FLAGS_CRC_STRIP_DIS); /* L2TSEL flag defines the reported L2 Tags in the receive descriptor * and it needs to remain 1 for non-DVM capable configurations to not diff --git a/drivers/net/ethernet/intel/ice/ice_common.c b/drivers/net/ethernet/intel/ice/ice_common.c index 27d0cbbd29da..039342a0ed15 100644 --- a/drivers/net/ethernet/intel/ice/ice_common.c +++ b/drivers/net/ethernet/intel/ice/ice_common.c @@ -8,6 +8,108 @@ #define ICE_PF_RESET_WAIT_COUNT 300 +static const char * const ice_link_mode_str_low[] = { + [0] = "100BASE_TX", + [1] = "100M_SGMII", + [2] = "1000BASE_T", + [3] = "1000BASE_SX", + [4] = "1000BASE_LX", + [5] = "1000BASE_KX", + [6] = "1G_SGMII", + [7] = "2500BASE_T", + [8] = "2500BASE_X", + [9] = "2500BASE_KX", + [10] = "5GBASE_T", + [11] = "5GBASE_KR", + [12] = "10GBASE_T", + [13] = "10G_SFI_DA", + [14] = "10GBASE_SR", + [15] = "10GBASE_LR", + [16] = "10GBASE_KR_CR1", + [17] = "10G_SFI_AOC_ACC", + [18] = "10G_SFI_C2C", + [19] = "25GBASE_T", + [20] = "25GBASE_CR", + [21] = "25GBASE_CR_S", + [22] = "25GBASE_CR1", + [23] = "25GBASE_SR", + [24] = "25GBASE_LR", + [25] = "25GBASE_KR", + [26] = "25GBASE_KR_S", + [27] = "25GBASE_KR1", + [28] = "25G_AUI_AOC_ACC", + [29] = "25G_AUI_C2C", + [30] = "40GBASE_CR4", + [31] = "40GBASE_SR4", + [32] = "40GBASE_LR4", + [33] = "40GBASE_KR4", + [34] = "40G_XLAUI_AOC_ACC", + [35] = "40G_XLAUI", + [36] = "50GBASE_CR2", + [37] = "50GBASE_SR2", + [38] = "50GBASE_LR2", + [39] = "50GBASE_KR2", + [40] = "50G_LAUI2_AOC_ACC", + [41] = "50G_LAUI2", + [42] = "50G_AUI2_AOC_ACC", + [43] = "50G_AUI2", + [44] = "50GBASE_CP", + [45] = "50GBASE_SR", + [46] = "50GBASE_FR", + [47] = "50GBASE_LR", + [48] = "50GBASE_KR_PAM4", + [49] = "50G_AUI1_AOC_ACC", + [50] = "50G_AUI1", + [51] = "100GBASE_CR4", + [52] = "100GBASE_SR4", + [53] = "100GBASE_LR4", + [54] = "100GBASE_KR4", + [55] = "100G_CAUI4_AOC_ACC", + [56] = "100G_CAUI4", + [57] = "100G_AUI4_AOC_ACC", + [58] = "100G_AUI4", + [59] = "100GBASE_CR_PAM4", + [60] = "100GBASE_KR_PAM4", + [61] = "100GBASE_CP2", + [62] = "100GBASE_SR2", + [63] = "100GBASE_DR", +}; + +static const char * const ice_link_mode_str_high[] = { + [0] = "100GBASE_KR2_PAM4", + [1] = "100G_CAUI2_AOC_ACC", + [2] = "100G_CAUI2", + [3] = "100G_AUI2_AOC_ACC", + [4] = "100G_AUI2", +}; + +/** + * ice_dump_phy_type - helper function to dump phy_type + * @hw: pointer to the HW structure + * @low: 64 bit value for phy_type_low + * @high: 64 bit value for phy_type_high + * @prefix: prefix string to differentiate multiple dumps + */ +static void +ice_dump_phy_type(struct ice_hw *hw, u64 low, u64 high, const char *prefix) +{ + ice_debug(hw, ICE_DBG_PHY, "%s: phy_type_low: 0x%016llx\n", prefix, low); + + for (u32 i = 0; i < BITS_PER_TYPE(typeof(low)); i++) { + if (low & BIT_ULL(i)) + ice_debug(hw, ICE_DBG_PHY, "%s: bit(%d): %s\n", + prefix, i, ice_link_mode_str_low[i]); + } + + ice_debug(hw, ICE_DBG_PHY, "%s: phy_type_high: 0x%016llx\n", prefix, high); + + for (u32 i = 0; i < BITS_PER_TYPE(typeof(high)); i++) { + if (high & BIT_ULL(i)) + ice_debug(hw, ICE_DBG_PHY, "%s: bit(%d): %s\n", + prefix, i, ice_link_mode_str_high[i]); + } +} + /** * ice_set_mac_type - Sets MAC type * @hw: pointer to the HW structure @@ -80,9 +182,23 @@ bool ice_is_e810t(struct ice_hw *hw) { switch (hw->device_id) { case ICE_DEV_ID_E810C_SFP: - if (hw->subsystem_device_id == ICE_SUBDEV_ID_E810T || - hw->subsystem_device_id == ICE_SUBDEV_ID_E810T2) + switch (hw->subsystem_device_id) { + case ICE_SUBDEV_ID_E810T: + case ICE_SUBDEV_ID_E810T2: + case ICE_SUBDEV_ID_E810T3: + case ICE_SUBDEV_ID_E810T4: + case ICE_SUBDEV_ID_E810T6: + case ICE_SUBDEV_ID_E810T7: + return true; + } + break; + case ICE_DEV_ID_E810C_QSFP: + switch (hw->subsystem_device_id) { + case ICE_SUBDEV_ID_E810T2: + case ICE_SUBDEV_ID_E810T3: + case ICE_SUBDEV_ID_E810T5: return true; + } break; default: break; @@ -183,6 +299,7 @@ ice_aq_get_phy_caps(struct ice_port_info *pi, bool qual_mods, u8 report_mode, struct ice_aqc_get_phy_caps *cmd; u16 pcaps_size = sizeof(*pcaps); struct ice_aq_desc desc; + const char *prefix; struct ice_hw *hw; int status; @@ -204,29 +321,48 @@ ice_aq_get_phy_caps(struct ice_port_info *pi, bool qual_mods, u8 report_mode, cmd->param0 |= cpu_to_le16(report_mode); status = ice_aq_send_cmd(hw, &desc, pcaps, pcaps_size, cd); - ice_debug(hw, ICE_DBG_LINK, "get phy caps - report_mode = 0x%x\n", - report_mode); - ice_debug(hw, ICE_DBG_LINK, " phy_type_low = 0x%llx\n", - (unsigned long long)le64_to_cpu(pcaps->phy_type_low)); - ice_debug(hw, ICE_DBG_LINK, " phy_type_high = 0x%llx\n", - (unsigned long long)le64_to_cpu(pcaps->phy_type_high)); - ice_debug(hw, ICE_DBG_LINK, " caps = 0x%x\n", pcaps->caps); - ice_debug(hw, ICE_DBG_LINK, " low_power_ctrl_an = 0x%x\n", + ice_debug(hw, ICE_DBG_LINK, "get phy caps dump\n"); + + switch (report_mode) { + case ICE_AQC_REPORT_TOPO_CAP_MEDIA: + prefix = "phy_caps_media"; + break; + case ICE_AQC_REPORT_TOPO_CAP_NO_MEDIA: + prefix = "phy_caps_no_media"; + break; + case ICE_AQC_REPORT_ACTIVE_CFG: + prefix = "phy_caps_active"; + break; + case ICE_AQC_REPORT_DFLT_CFG: + prefix = "phy_caps_default"; + break; + default: + prefix = "phy_caps_invalid"; + } + + ice_dump_phy_type(hw, le64_to_cpu(pcaps->phy_type_low), + le64_to_cpu(pcaps->phy_type_high), prefix); + + ice_debug(hw, ICE_DBG_LINK, "%s: report_mode = 0x%x\n", + prefix, report_mode); + ice_debug(hw, ICE_DBG_LINK, "%s: caps = 0x%x\n", prefix, pcaps->caps); + ice_debug(hw, ICE_DBG_LINK, "%s: low_power_ctrl_an = 0x%x\n", prefix, pcaps->low_power_ctrl_an); - ice_debug(hw, ICE_DBG_LINK, " eee_cap = 0x%x\n", pcaps->eee_cap); - ice_debug(hw, ICE_DBG_LINK, " eeer_value = 0x%x\n", + ice_debug(hw, ICE_DBG_LINK, "%s: eee_cap = 0x%x\n", prefix, + pcaps->eee_cap); + ice_debug(hw, ICE_DBG_LINK, "%s: eeer_value = 0x%x\n", prefix, pcaps->eeer_value); - ice_debug(hw, ICE_DBG_LINK, " link_fec_options = 0x%x\n", + ice_debug(hw, ICE_DBG_LINK, "%s: link_fec_options = 0x%x\n", prefix, pcaps->link_fec_options); - ice_debug(hw, ICE_DBG_LINK, " module_compliance_enforcement = 0x%x\n", - pcaps->module_compliance_enforcement); - ice_debug(hw, ICE_DBG_LINK, " extended_compliance_code = 0x%x\n", - pcaps->extended_compliance_code); - ice_debug(hw, ICE_DBG_LINK, " module_type[0] = 0x%x\n", + ice_debug(hw, ICE_DBG_LINK, "%s: module_compliance_enforcement = 0x%x\n", + prefix, pcaps->module_compliance_enforcement); + ice_debug(hw, ICE_DBG_LINK, "%s: extended_compliance_code = 0x%x\n", + prefix, pcaps->extended_compliance_code); + ice_debug(hw, ICE_DBG_LINK, "%s: module_type[0] = 0x%x\n", prefix, pcaps->module_type[0]); - ice_debug(hw, ICE_DBG_LINK, " module_type[1] = 0x%x\n", + ice_debug(hw, ICE_DBG_LINK, "%s: module_type[1] = 0x%x\n", prefix, pcaps->module_type[1]); - ice_debug(hw, ICE_DBG_LINK, " module_type[2] = 0x%x\n", + ice_debug(hw, ICE_DBG_LINK, "%s: module_type[2] = 0x%x\n", prefix, pcaps->module_type[2]); if (!status && report_mode == ICE_AQC_REPORT_TOPO_CAP_MEDIA) { @@ -2397,6 +2533,8 @@ ice_parse_1588_dev_caps(struct ice_hw *hw, struct ice_hw_dev_caps *dev_p, info->tmr1_owned = ((number & ICE_TS_TMR1_OWND_M) != 0); info->tmr1_ena = ((number & ICE_TS_TMR1_ENA_M) != 0); + info->ts_ll_read = ((number & ICE_TS_LL_TX_TS_READ_M) != 0); + info->ena_ports = logical_id; info->tmr_own_map = phys_id; @@ -2414,6 +2552,8 @@ ice_parse_1588_dev_caps(struct ice_hw *hw, struct ice_hw_dev_caps *dev_p, info->tmr1_owned); ice_debug(hw, ICE_DBG_INIT, "dev caps: tmr1_ena = %u\n", info->tmr1_ena); + ice_debug(hw, ICE_DBG_INIT, "dev caps: ts_ll_read = %u\n", + info->ts_ll_read); ice_debug(hw, ICE_DBG_INIT, "dev caps: ieee_1588 ena_ports = %u\n", info->ena_ports); ice_debug(hw, ICE_DBG_INIT, "dev caps: tmr_own_map = %u\n", @@ -2776,6 +2916,26 @@ ice_aq_set_port_params(struct ice_port_info *pi, bool double_vlan, } /** + * ice_is_100m_speed_supported + * @hw: pointer to the HW struct + * + * returns true if 100M speeds are supported by the device, + * false otherwise. + */ +bool ice_is_100m_speed_supported(struct ice_hw *hw) +{ + switch (hw->device_id) { + case ICE_DEV_ID_E822C_SGMII: + case ICE_DEV_ID_E822L_SGMII: + case ICE_DEV_ID_E823L_1GBE: + case ICE_DEV_ID_E823C_SGMII: + return true; + default: + return false; + } +} + +/** * ice_get_link_speed_based_on_phy_type - returns link speed * @phy_type_low: lower part of phy_type * @phy_type_high: higher part of phy_type @@ -3535,6 +3695,121 @@ ice_aq_set_port_id_led(struct ice_port_info *pi, bool is_orig_mode, } /** + * ice_aq_get_port_options + * @hw: pointer to the HW struct + * @options: buffer for the resultant port options + * @option_count: input - size of the buffer in port options structures, + * output - number of returned port options + * @lport: logical port to call the command with (optional) + * @lport_valid: when false, FW uses port owned by the PF instead of lport, + * when PF owns more than 1 port it must be true + * @active_option_idx: index of active port option in returned buffer + * @active_option_valid: active option in returned buffer is valid + * @pending_option_idx: index of pending port option in returned buffer + * @pending_option_valid: pending option in returned buffer is valid + * + * Calls Get Port Options AQC (0x06ea) and verifies result. + */ +int +ice_aq_get_port_options(struct ice_hw *hw, + struct ice_aqc_get_port_options_elem *options, + u8 *option_count, u8 lport, bool lport_valid, + u8 *active_option_idx, bool *active_option_valid, + u8 *pending_option_idx, bool *pending_option_valid) +{ + struct ice_aqc_get_port_options *cmd; + struct ice_aq_desc desc; + int status; + u8 i; + + /* options buffer shall be able to hold max returned options */ + if (*option_count < ICE_AQC_PORT_OPT_COUNT_M) + return -EINVAL; + + cmd = &desc.params.get_port_options; + ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_get_port_options); + + if (lport_valid) + cmd->lport_num = lport; + cmd->lport_num_valid = lport_valid; + + status = ice_aq_send_cmd(hw, &desc, options, + *option_count * sizeof(*options), NULL); + if (status) + return status; + + /* verify direct FW response & set output parameters */ + *option_count = FIELD_GET(ICE_AQC_PORT_OPT_COUNT_M, + cmd->port_options_count); + ice_debug(hw, ICE_DBG_PHY, "options: %x\n", *option_count); + *active_option_valid = FIELD_GET(ICE_AQC_PORT_OPT_VALID, + cmd->port_options); + if (*active_option_valid) { + *active_option_idx = FIELD_GET(ICE_AQC_PORT_OPT_ACTIVE_M, + cmd->port_options); + if (*active_option_idx > (*option_count - 1)) + return -EIO; + ice_debug(hw, ICE_DBG_PHY, "active idx: %x\n", + *active_option_idx); + } + + *pending_option_valid = FIELD_GET(ICE_AQC_PENDING_PORT_OPT_VALID, + cmd->pending_port_option_status); + if (*pending_option_valid) { + *pending_option_idx = FIELD_GET(ICE_AQC_PENDING_PORT_OPT_IDX_M, + cmd->pending_port_option_status); + if (*pending_option_idx > (*option_count - 1)) + return -EIO; + ice_debug(hw, ICE_DBG_PHY, "pending idx: %x\n", + *pending_option_idx); + } + + /* mask output options fields */ + for (i = 0; i < *option_count; i++) { + options[i].pmd = FIELD_GET(ICE_AQC_PORT_OPT_PMD_COUNT_M, + options[i].pmd); + options[i].max_lane_speed = FIELD_GET(ICE_AQC_PORT_OPT_MAX_LANE_M, + options[i].max_lane_speed); + ice_debug(hw, ICE_DBG_PHY, "pmds: %x max speed: %x\n", + options[i].pmd, options[i].max_lane_speed); + } + + return 0; +} + +/** + * ice_aq_set_port_option + * @hw: pointer to the HW struct + * @lport: logical port to call the command with + * @lport_valid: when false, FW uses port owned by the PF instead of lport, + * when PF owns more than 1 port it must be true + * @new_option: new port option to be written + * + * Calls Set Port Options AQC (0x06eb). + */ +int +ice_aq_set_port_option(struct ice_hw *hw, u8 lport, u8 lport_valid, + u8 new_option) +{ + struct ice_aqc_set_port_option *cmd; + struct ice_aq_desc desc; + + if (new_option > ICE_AQC_PORT_OPT_COUNT_M) + return -EINVAL; + + cmd = &desc.params.set_port_option; + ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_set_port_option); + + if (lport_valid) + cmd->lport_num = lport; + + cmd->lport_num_valid = lport_valid; + cmd->selected_port_option = new_option; + + return ice_aq_send_cmd(hw, &desc, NULL, 0, NULL); +} + +/** * ice_aq_sff_eeprom * @hw: pointer to the HW struct * @lport: bits [7:0] = logical port, bit [8] = logical port valid @@ -5029,20 +5304,22 @@ ice_aq_get_gpio(struct ice_hw *hw, u16 gpio_ctrl_handle, u8 pin_idx, } /** - * ice_fw_supports_link_override + * ice_is_fw_api_min_ver * @hw: pointer to the hardware structure + * @maj: major version + * @min: minor version + * @patch: patch version * - * Checks if the firmware supports link override + * Checks if the firmware API is minimum version */ -bool ice_fw_supports_link_override(struct ice_hw *hw) +static bool ice_is_fw_api_min_ver(struct ice_hw *hw, u8 maj, u8 min, u8 patch) { - if (hw->api_maj_ver == ICE_FW_API_LINK_OVERRIDE_MAJ) { - if (hw->api_min_ver > ICE_FW_API_LINK_OVERRIDE_MIN) + if (hw->api_maj_ver == maj) { + if (hw->api_min_ver > min) return true; - if (hw->api_min_ver == ICE_FW_API_LINK_OVERRIDE_MIN && - hw->api_patch >= ICE_FW_API_LINK_OVERRIDE_PATCH) + if (hw->api_min_ver == min && hw->api_patch >= patch) return true; - } else if (hw->api_maj_ver > ICE_FW_API_LINK_OVERRIDE_MAJ) { + } else if (hw->api_maj_ver > maj) { return true; } @@ -5050,6 +5327,19 @@ bool ice_fw_supports_link_override(struct ice_hw *hw) } /** + * ice_fw_supports_link_override + * @hw: pointer to the hardware structure + * + * Checks if the firmware supports link override + */ +bool ice_fw_supports_link_override(struct ice_hw *hw) +{ + return ice_is_fw_api_min_ver(hw, ICE_FW_API_LINK_OVERRIDE_MAJ, + ICE_FW_API_LINK_OVERRIDE_MIN, + ICE_FW_API_LINK_OVERRIDE_PATCH); +} + +/** * ice_get_link_default_override * @ldo: pointer to the link default override struct * @pi: pointer to the port info struct @@ -5179,16 +5469,9 @@ bool ice_fw_supports_lldp_fltr_ctrl(struct ice_hw *hw) if (hw->mac_type != ICE_MAC_E810) return false; - if (hw->api_maj_ver == ICE_FW_API_LLDP_FLTR_MAJ) { - if (hw->api_min_ver > ICE_FW_API_LLDP_FLTR_MIN) - return true; - if (hw->api_min_ver == ICE_FW_API_LLDP_FLTR_MIN && - hw->api_patch >= ICE_FW_API_LLDP_FLTR_PATCH) - return true; - } else if (hw->api_maj_ver > ICE_FW_API_LLDP_FLTR_MAJ) { - return true; - } - return false; + return ice_is_fw_api_min_ver(hw, ICE_FW_API_LLDP_FLTR_MAJ, + ICE_FW_API_LLDP_FLTR_MIN, + ICE_FW_API_LLDP_FLTR_PATCH); } /** @@ -5225,14 +5508,7 @@ ice_lldp_fltr_add_remove(struct ice_hw *hw, u16 vsi_num, bool add) */ bool ice_fw_supports_report_dflt_cfg(struct ice_hw *hw) { - if (hw->api_maj_ver == ICE_FW_API_REPORT_DFLT_CFG_MAJ) { - if (hw->api_min_ver > ICE_FW_API_REPORT_DFLT_CFG_MIN) - return true; - if (hw->api_min_ver == ICE_FW_API_REPORT_DFLT_CFG_MIN && - hw->api_patch >= ICE_FW_API_REPORT_DFLT_CFG_PATCH) - return true; - } else if (hw->api_maj_ver > ICE_FW_API_REPORT_DFLT_CFG_MAJ) { - return true; - } - return false; + return ice_is_fw_api_min_ver(hw, ICE_FW_API_REPORT_DFLT_CFG_MAJ, + ICE_FW_API_REPORT_DFLT_CFG_MIN, + ICE_FW_API_REPORT_DFLT_CFG_PATCH); } diff --git a/drivers/net/ethernet/intel/ice/ice_common.h b/drivers/net/ethernet/intel/ice/ice_common.h index 61b7c60db689..8b6712b92e84 100644 --- a/drivers/net/ethernet/intel/ice/ice_common.h +++ b/drivers/net/ethernet/intel/ice/ice_common.h @@ -151,6 +151,15 @@ int ice_aq_set_port_id_led(struct ice_port_info *pi, bool is_orig_mode, struct ice_sq_cd *cd); int +ice_aq_get_port_options(struct ice_hw *hw, + struct ice_aqc_get_port_options_elem *options, + u8 *option_count, u8 lport, bool lport_valid, + u8 *active_option_idx, bool *active_option_valid, + u8 *pending_option_idx, bool *pending_option_valid); +int +ice_aq_set_port_option(struct ice_hw *hw, u8 lport, u8 lport_valid, + u8 new_option); +int ice_aq_sff_eeprom(struct ice_hw *hw, u16 lport, u8 bus_addr, u16 mem_addr, u8 page, u8 set_page, u8 *data, u8 length, bool write, struct ice_sq_cd *cd); @@ -204,6 +213,7 @@ ice_aq_set_gpio(struct ice_hw *hw, u16 gpio_ctrl_handle, u8 pin_idx, bool value, int ice_aq_get_gpio(struct ice_hw *hw, u16 gpio_ctrl_handle, u8 pin_idx, bool *value, struct ice_sq_cd *cd); +bool ice_is_100m_speed_supported(struct ice_hw *hw); int ice_aq_set_lldp_mib(struct ice_hw *hw, u8 mib_type, void *buf, u16 buf_size, struct ice_sq_cd *cd); diff --git a/drivers/net/ethernet/intel/ice/ice_devids.h b/drivers/net/ethernet/intel/ice/ice_devids.h index b41bc3dc1745..6d560d1c74a4 100644 --- a/drivers/net/ethernet/intel/ice/ice_devids.h +++ b/drivers/net/ethernet/intel/ice/ice_devids.h @@ -24,6 +24,11 @@ #define ICE_DEV_ID_E810C_SFP 0x1593 #define ICE_SUBDEV_ID_E810T 0x000E #define ICE_SUBDEV_ID_E810T2 0x000F +#define ICE_SUBDEV_ID_E810T3 0x0010 +#define ICE_SUBDEV_ID_E810T4 0x0011 +#define ICE_SUBDEV_ID_E810T5 0x0012 +#define ICE_SUBDEV_ID_E810T6 0x02E9 +#define ICE_SUBDEV_ID_E810T7 0x02EA /* Intel(R) Ethernet Controller E810-XXV for backplane */ #define ICE_DEV_ID_E810_XXV_BACKPLANE 0x1599 /* Intel(R) Ethernet Controller E810-XXV for QSFP */ diff --git a/drivers/net/ethernet/intel/ice/ice_devlink.c b/drivers/net/ethernet/intel/ice/ice_devlink.c index 3337314a7b35..e6ec20079ced 100644 --- a/drivers/net/ethernet/intel/ice/ice_devlink.c +++ b/drivers/net/ethernet/intel/ice/ice_devlink.c @@ -9,6 +9,8 @@ #include "ice_eswitch.h" #include "ice_fw_update.h" +static int ice_active_port_option = -1; + /* context for devlink info version reporting */ struct ice_info_ctx { char buf[128]; @@ -466,12 +468,259 @@ ice_devlink_reload_empr_finish(struct devlink *devlink, return 0; } +/** + * ice_devlink_port_opt_speed_str - convert speed to a string + * @speed: speed value + */ +static const char *ice_devlink_port_opt_speed_str(u8 speed) +{ + switch (speed & ICE_AQC_PORT_OPT_MAX_LANE_M) { + case ICE_AQC_PORT_OPT_MAX_LANE_100M: + return "0.1"; + case ICE_AQC_PORT_OPT_MAX_LANE_1G: + return "1"; + case ICE_AQC_PORT_OPT_MAX_LANE_2500M: + return "2.5"; + case ICE_AQC_PORT_OPT_MAX_LANE_5G: + return "5"; + case ICE_AQC_PORT_OPT_MAX_LANE_10G: + return "10"; + case ICE_AQC_PORT_OPT_MAX_LANE_25G: + return "25"; + case ICE_AQC_PORT_OPT_MAX_LANE_50G: + return "50"; + case ICE_AQC_PORT_OPT_MAX_LANE_100G: + return "100"; + } + + return "-"; +} + +#define ICE_PORT_OPT_DESC_LEN 50 +/** + * ice_devlink_port_options_print - Print available port split options + * @pf: the PF to print split port options + * + * Prints a table with available port split options and max port speeds + */ +static void ice_devlink_port_options_print(struct ice_pf *pf) +{ + u8 i, j, options_count, cnt, speed, pending_idx, active_idx; + struct ice_aqc_get_port_options_elem *options, *opt; + struct device *dev = ice_pf_to_dev(pf); + bool active_valid, pending_valid; + char desc[ICE_PORT_OPT_DESC_LEN]; + const char *str; + int status; + + options = kcalloc(ICE_AQC_PORT_OPT_MAX * ICE_MAX_PORT_PER_PCI_DEV, + sizeof(*options), GFP_KERNEL); + if (!options) + return; + + for (i = 0; i < ICE_MAX_PORT_PER_PCI_DEV; i++) { + opt = options + i * ICE_AQC_PORT_OPT_MAX; + options_count = ICE_AQC_PORT_OPT_MAX; + active_valid = 0; + + status = ice_aq_get_port_options(&pf->hw, opt, &options_count, + i, true, &active_idx, + &active_valid, &pending_idx, + &pending_valid); + if (status) { + dev_dbg(dev, "Couldn't read port option for port %d, err %d\n", + i, status); + goto err; + } + } + + dev_dbg(dev, "Available port split options and max port speeds (Gbps):\n"); + dev_dbg(dev, "Status Split Quad 0 Quad 1\n"); + dev_dbg(dev, " count L0 L1 L2 L3 L4 L5 L6 L7\n"); + + for (i = 0; i < options_count; i++) { + cnt = 0; + + if (i == ice_active_port_option) + str = "Active"; + else if ((i == pending_idx) && pending_valid) + str = "Pending"; + else + str = ""; + + cnt += snprintf(&desc[cnt], ICE_PORT_OPT_DESC_LEN - cnt, + "%-8s", str); + + cnt += snprintf(&desc[cnt], ICE_PORT_OPT_DESC_LEN - cnt, + "%-6u", options[i].pmd); + + for (j = 0; j < ICE_MAX_PORT_PER_PCI_DEV; ++j) { + speed = options[i + j * ICE_AQC_PORT_OPT_MAX].max_lane_speed; + str = ice_devlink_port_opt_speed_str(speed); + cnt += snprintf(&desc[cnt], ICE_PORT_OPT_DESC_LEN - cnt, + "%3s ", str); + } + + dev_dbg(dev, "%s\n", desc); + } + +err: + kfree(options); +} + +/** + * ice_devlink_aq_set_port_option - Send set port option admin queue command + * @pf: the PF to print split port options + * @option_idx: selected port option + * @extack: extended netdev ack structure + * + * Sends set port option admin queue command with selected port option and + * calls NVM write activate. + */ +static int +ice_devlink_aq_set_port_option(struct ice_pf *pf, u8 option_idx, + struct netlink_ext_ack *extack) +{ + struct device *dev = ice_pf_to_dev(pf); + int status; + + status = ice_aq_set_port_option(&pf->hw, 0, true, option_idx); + if (status) { + dev_dbg(dev, "ice_aq_set_port_option, err %d aq_err %d\n", + status, pf->hw.adminq.sq_last_status); + NL_SET_ERR_MSG_MOD(extack, "Port split request failed"); + return -EIO; + } + + status = ice_acquire_nvm(&pf->hw, ICE_RES_WRITE); + if (status) { + dev_dbg(dev, "ice_acquire_nvm failed, err %d aq_err %d\n", + status, pf->hw.adminq.sq_last_status); + NL_SET_ERR_MSG_MOD(extack, "Failed to acquire NVM semaphore"); + return -EIO; + } + + status = ice_nvm_write_activate(&pf->hw, ICE_AQC_NVM_ACTIV_REQ_EMPR, NULL); + if (status) { + dev_dbg(dev, "ice_nvm_write_activate failed, err %d aq_err %d\n", + status, pf->hw.adminq.sq_last_status); + NL_SET_ERR_MSG_MOD(extack, "Port split request failed to save data"); + ice_release_nvm(&pf->hw); + return -EIO; + } + + ice_release_nvm(&pf->hw); + + NL_SET_ERR_MSG_MOD(extack, "Reboot required to finish port split"); + return 0; +} + +/** + * ice_devlink_port_split - .port_split devlink handler + * @devlink: devlink instance structure + * @port: devlink port structure + * @count: number of ports to split to + * @extack: extended netdev ack structure + * + * Callback for the devlink .port_split operation. + * + * Unfortunately, the devlink expression of available options is limited + * to just a number, so search for an FW port option which supports + * the specified number. As there could be multiple FW port options with + * the same port split count, allow switching between them. When the same + * port split count request is issued again, switch to the next FW port + * option with the same port split count. + * + * Return: zero on success or an error code on failure. + */ +static int +ice_devlink_port_split(struct devlink *devlink, struct devlink_port *port, + unsigned int count, struct netlink_ext_ack *extack) +{ + struct ice_aqc_get_port_options_elem options[ICE_AQC_PORT_OPT_MAX]; + u8 i, j, active_idx, pending_idx, new_option; + struct ice_pf *pf = devlink_priv(devlink); + u8 option_count = ICE_AQC_PORT_OPT_MAX; + struct device *dev = ice_pf_to_dev(pf); + bool active_valid, pending_valid; + int status; + + status = ice_aq_get_port_options(&pf->hw, options, &option_count, + 0, true, &active_idx, &active_valid, + &pending_idx, &pending_valid); + if (status) { + dev_dbg(dev, "Couldn't read port split options, err = %d\n", + status); + NL_SET_ERR_MSG_MOD(extack, "Failed to get available port split options"); + return -EIO; + } + + new_option = ICE_AQC_PORT_OPT_MAX; + active_idx = pending_valid ? pending_idx : active_idx; + for (i = 1; i <= option_count; i++) { + /* In order to allow switching between FW port options with + * the same port split count, search for a new option starting + * from the active/pending option (with array wrap around). + */ + j = (active_idx + i) % option_count; + + if (count == options[j].pmd) { + new_option = j; + break; + } + } + + if (new_option == active_idx) { + dev_dbg(dev, "request to split: count: %u is already set and there are no other options\n", + count); + NL_SET_ERR_MSG_MOD(extack, "Requested split count is already set"); + ice_devlink_port_options_print(pf); + return -EINVAL; + } + + if (new_option == ICE_AQC_PORT_OPT_MAX) { + dev_dbg(dev, "request to split: count: %u not found\n", count); + NL_SET_ERR_MSG_MOD(extack, "Port split requested unsupported port config"); + ice_devlink_port_options_print(pf); + return -EINVAL; + } + + status = ice_devlink_aq_set_port_option(pf, new_option, extack); + if (status) + return status; + + ice_devlink_port_options_print(pf); + + return 0; +} + +/** + * ice_devlink_port_unsplit - .port_unsplit devlink handler + * @devlink: devlink instance structure + * @port: devlink port structure + * @extack: extended netdev ack structure + * + * Callback for the devlink .port_unsplit operation. + * Calls ice_devlink_port_split with split count set to 1. + * There could be no FW option available with split count 1. + * + * Return: zero on success or an error code on failure. + */ +static int +ice_devlink_port_unsplit(struct devlink *devlink, struct devlink_port *port, + struct netlink_ext_ack *extack) +{ + return ice_devlink_port_split(devlink, port, 1, extack); +} + static const struct devlink_ops ice_devlink_ops = { .supported_flash_update_params = DEVLINK_SUPPORT_FLASH_UPDATE_OVERWRITE_MASK, .reload_actions = BIT(DEVLINK_RELOAD_ACTION_FW_ACTIVATE), /* The ice driver currently does not support driver reinit */ .reload_down = ice_devlink_reload_empr_start, .reload_up = ice_devlink_reload_empr_finish, + .port_split = ice_devlink_port_split, + .port_unsplit = ice_devlink_port_unsplit, .eswitch_mode_get = ice_eswitch_mode_get, .eswitch_mode_set = ice_eswitch_mode_set, .info_get = ice_devlink_info_get, @@ -695,6 +944,39 @@ void ice_devlink_unregister_params(struct ice_pf *pf) } /** + * ice_devlink_set_port_split_options - Set port split options + * @pf: the PF to set port split options + * @attrs: devlink attributes + * + * Sets devlink port split options based on available FW port options + */ +static void +ice_devlink_set_port_split_options(struct ice_pf *pf, + struct devlink_port_attrs *attrs) +{ + struct ice_aqc_get_port_options_elem options[ICE_AQC_PORT_OPT_MAX]; + u8 i, active_idx, pending_idx, option_count = ICE_AQC_PORT_OPT_MAX; + bool active_valid, pending_valid; + int status; + + status = ice_aq_get_port_options(&pf->hw, options, &option_count, + 0, true, &active_idx, &active_valid, + &pending_idx, &pending_valid); + if (status) { + dev_dbg(ice_pf_to_dev(pf), "Couldn't read port split options, err = %d\n", + status); + return; + } + + /* find the biggest available port split count */ + for (i = 0; i < option_count; i++) + attrs->lanes = max_t(int, attrs->lanes, options[i].pmd); + + attrs->splittable = attrs->lanes ? 1 : 0; + ice_active_port_option = active_idx; +} + +/** * ice_devlink_create_pf_port - Create a devlink port for this PF * @pf: the PF to create a devlink port for * @@ -722,6 +1004,12 @@ int ice_devlink_create_pf_port(struct ice_pf *pf) attrs.flavour = DEVLINK_PORT_FLAVOUR_PHYSICAL; attrs.phys.port_number = pf->hw.bus.func; + /* As FW supports only port split options for whole device, + * set port split options only for first PF. + */ + if (pf->hw.pf_id == 0) + ice_devlink_set_port_split_options(pf, &attrs); + ice_devlink_set_switch_id(pf, &attrs.switch_id); devlink_port_attrs_set(devlink_port, &attrs); diff --git a/drivers/net/ethernet/intel/ice/ice_eswitch.c b/drivers/net/ethernet/intel/ice/ice_eswitch.c index e35371e61e07..f9f15acae90a 100644 --- a/drivers/net/ethernet/intel/ice/ice_eswitch.c +++ b/drivers/net/ethernet/intel/ice/ice_eswitch.c @@ -292,8 +292,8 @@ static int ice_eswitch_setup_reprs(struct ice_pf *pf) if (max_vsi_num < vsi->vsi_num) max_vsi_num = vsi->vsi_num; - netif_napi_add(vf->repr->netdev, &vf->repr->q_vector->napi, ice_napi_poll, - NAPI_POLL_WEIGHT); + netif_napi_add(vf->repr->netdev, &vf->repr->q_vector->napi, + ice_napi_poll); netif_keep_dst(vf->repr->netdev); } diff --git a/drivers/net/ethernet/intel/ice/ice_ethtool.c b/drivers/net/ethernet/intel/ice/ice_ethtool.c index a6fff8ebaf9d..b7be84bbe72d 100644 --- a/drivers/net/ethernet/intel/ice/ice_ethtool.c +++ b/drivers/net/ethernet/intel/ice/ice_ethtool.c @@ -136,6 +136,11 @@ static const struct ice_stats ice_gstrings_pf_stats[] = { ICE_PF_STAT("mac_remote_faults.nic", stats.mac_remote_faults), ICE_PF_STAT("fdir_sb_match.nic", stats.fd_sb_match), ICE_PF_STAT("fdir_sb_status.nic", stats.fd_sb_status), + ICE_PF_STAT("tx_hwtstamp_skipped", ptp.tx_hwtstamp_skipped), + ICE_PF_STAT("tx_hwtstamp_timeouts", ptp.tx_hwtstamp_timeouts), + ICE_PF_STAT("tx_hwtstamp_flushed", ptp.tx_hwtstamp_flushed), + ICE_PF_STAT("tx_hwtstamp_discarded", ptp.tx_hwtstamp_discarded), + ICE_PF_STAT("late_cached_phc_updates", ptp.late_cached_phc_updates), }; static const u32 ice_regs_dump_list[] = { @@ -1284,10 +1289,7 @@ static int ice_set_priv_flags(struct net_device *netdev, u32 flags) } if (test_bit(ICE_FLAG_LEGACY_RX, change_flags)) { /* down and up VSI so that changes of Rx cfg are reflected. */ - if (!test_and_set_bit(ICE_VSI_DOWN, vsi->state)) { - ice_down(vsi); - ice_up(vsi); - } + ice_down_up(vsi); } /* don't allow modification of this flag when a single VF is in * promiscuous mode because it's not supported @@ -1468,20 +1470,22 @@ ice_get_ethtool_stats(struct net_device *netdev, /** * ice_mask_min_supported_speeds + * @hw: pointer to the HW structure * @phy_types_high: PHY type high * @phy_types_low: PHY type low to apply minimum supported speeds mask * * Apply minimum supported speeds mask to PHY type low. These are the speeds * for ethtool supported link mode. */ -static -void ice_mask_min_supported_speeds(u64 phy_types_high, u64 *phy_types_low) +static void +ice_mask_min_supported_speeds(struct ice_hw *hw, + u64 phy_types_high, u64 *phy_types_low) { /* if QSFP connection with 100G speed, minimum supported speed is 25G */ if (*phy_types_low & ICE_PHY_TYPE_LOW_MASK_100G || phy_types_high & ICE_PHY_TYPE_HIGH_MASK_100G) *phy_types_low &= ~ICE_PHY_TYPE_LOW_MASK_MIN_25G; - else + else if (!ice_is_100m_speed_supported(hw)) *phy_types_low &= ~ICE_PHY_TYPE_LOW_MASK_MIN_1G; } @@ -1531,7 +1535,8 @@ ice_phy_type_to_ethtool(struct net_device *netdev, phy_types_low = le64_to_cpu(pf->nvm_phy_type_lo); phy_types_high = le64_to_cpu(pf->nvm_phy_type_hi); - ice_mask_min_supported_speeds(phy_types_high, &phy_types_low); + ice_mask_min_supported_speeds(&pf->hw, phy_types_high, + &phy_types_low); /* determine advertised modes based on link override only * if it's supported and if the FW doesn't abstract the * driver from having to account for link overrides @@ -2826,6 +2831,7 @@ ice_set_ringparam(struct net_device *netdev, struct ethtool_ringparam *ring, tx_rings[i].count = new_tx_cnt; tx_rings[i].desc = NULL; tx_rings[i].tx_buf = NULL; + tx_rings[i].tx_tstamps = &pf->ptp.port.tx; err = ice_setup_tx_ring(&tx_rings[i]); if (err) { while (i--) @@ -2884,6 +2890,7 @@ process_rx: /* clone ring and setup updated count */ rx_rings[i] = *vsi->rx_rings[i]; rx_rings[i].count = new_rx_cnt; + rx_rings[i].cached_phctime = pf->ptp.cached_phc_time; rx_rings[i].desc = NULL; rx_rings[i].rx_buf = NULL; /* this is to allow wr32 to have something to write to diff --git a/drivers/net/ethernet/intel/ice/ice_lag.c b/drivers/net/ethernet/intel/ice/ice_lag.c index c9f7393b783d..ee5b36941ba3 100644 --- a/drivers/net/ethernet/intel/ice/ice_lag.c +++ b/drivers/net/ethernet/intel/ice/ice_lag.c @@ -61,13 +61,13 @@ static void ice_lag_set_backup(struct ice_lag *lag) */ static void ice_display_lag_info(struct ice_lag *lag) { - const char *name, *peer, *upper, *role, *bonded, *master; + const char *name, *peer, *upper, *role, *bonded, *primary; struct device *dev = &lag->pf->pdev->dev; name = lag->netdev ? netdev_name(lag->netdev) : "unset"; peer = lag->peer_netdev ? netdev_name(lag->peer_netdev) : "unset"; upper = lag->upper_netdev ? netdev_name(lag->upper_netdev) : "unset"; - master = lag->master ? "TRUE" : "FALSE"; + primary = lag->primary ? "TRUE" : "FALSE"; bonded = lag->bonded ? "BONDED" : "UNBONDED"; switch (lag->role) { @@ -87,8 +87,8 @@ static void ice_display_lag_info(struct ice_lag *lag) role = "ERROR"; } - dev_dbg(dev, "%s %s, peer:%s, upper:%s, role:%s, master:%s\n", name, - bonded, peer, upper, role, master); + dev_dbg(dev, "%s %s, peer:%s, upper:%s, role:%s, primary:%s\n", name, + bonded, peer, upper, role, primary); } /** @@ -119,7 +119,7 @@ static void ice_lag_info_event(struct ice_lag *lag, void *ptr) } if (strcmp(bonding_info->slave.slave_name, lag_netdev_name)) { - netdev_dbg(lag->netdev, "Bonding event recv, but slave info not for us\n"); + netdev_dbg(lag->netdev, "Bonding event recv, but secondary info not for us\n"); goto lag_out; } @@ -164,8 +164,8 @@ ice_lag_link(struct ice_lag *lag, struct netdev_notifier_changeupper_info *info) lag->bonded = true; lag->role = ICE_LAG_UNSET; - /* if this is the first element in an LAG mark as master */ - lag->master = !!(peers == 1); + /* if this is the first element in an LAG mark as primary */ + lag->primary = !!(peers == 1); } /** @@ -264,7 +264,7 @@ static void ice_lag_changeupper_event(struct ice_lag *lag, void *ptr) netdev_dbg(netdev, "bonding %s\n", info->linking ? "LINK" : "UNLINK"); if (!netif_is_lag_master(info->upper_dev)) { - netdev_dbg(netdev, "changeupper rcvd, but not master. bail\n"); + netdev_dbg(netdev, "changeupper rcvd, but not primary. bail\n"); return; } diff --git a/drivers/net/ethernet/intel/ice/ice_lag.h b/drivers/net/ethernet/intel/ice/ice_lag.h index c2e3688dd8fd..51b5cf467ce2 100644 --- a/drivers/net/ethernet/intel/ice/ice_lag.h +++ b/drivers/net/ethernet/intel/ice/ice_lag.h @@ -24,7 +24,7 @@ struct ice_lag { struct net_device *upper_netdev; /* upper bonding netdev */ struct notifier_block notif_block; u8 bonded:1; /* currently bonded */ - u8 master:1; /* this is a master */ + u8 primary:1; /* this is primary */ u8 handler:1; /* did we register a rx_netdev_handler */ /* each thing blocking bonding will increment this value by one. * If this value is zero, then bonding is allowed. diff --git a/drivers/net/ethernet/intel/ice/ice_lib.c b/drivers/net/ethernet/intel/ice/ice_lib.c index 58d483e2f539..938ba8c215cb 100644 --- a/drivers/net/ethernet/intel/ice/ice_lib.c +++ b/drivers/net/ethernet/intel/ice/ice_lib.c @@ -1524,6 +1524,7 @@ static int ice_vsi_alloc_rings(struct ice_vsi *vsi) ring->netdev = vsi->netdev; ring->dev = dev; ring->count = vsi->num_rx_desc; + ring->cached_phctime = pf->ptp.cached_phc_time; WRITE_ONCE(vsi->rx_rings[i], ring); } @@ -1564,6 +1565,22 @@ void ice_vsi_manage_rss_lut(struct ice_vsi *vsi, bool ena) } /** + * ice_vsi_cfg_crc_strip - Configure CRC stripping for a VSI + * @vsi: VSI to be configured + * @disable: set to true to have FCS / CRC in the frame data + */ +void ice_vsi_cfg_crc_strip(struct ice_vsi *vsi, bool disable) +{ + int i; + + ice_for_each_rxq(vsi, i) + if (disable) + vsi->rx_rings[i]->flags |= ICE_RX_FLAGS_CRC_STRIP_DIS; + else + vsi->rx_rings[i]->flags &= ~ICE_RX_FLAGS_CRC_STRIP_DIS; +} + +/** * ice_vsi_cfg_rss_lut_key - Configure RSS params for a VSI * @vsi: VSI to be configured */ @@ -2971,9 +2988,6 @@ int ice_vsi_release(struct ice_vsi *vsi) clear_bit(ICE_VSI_NETDEV_REGISTERED, vsi->state); } - if (vsi->type == ICE_VSI_PF) - ice_devlink_destroy_pf_port(pf); - if (test_bit(ICE_FLAG_RSS_ENA, pf->flags)) ice_rss_clean(vsi); @@ -3031,6 +3045,9 @@ int ice_vsi_release(struct ice_vsi *vsi) } } + if (vsi->type == ICE_VSI_PF) + ice_devlink_destroy_pf_port(pf); + if (vsi->type == ICE_VSI_VF && vsi->agg_node && vsi->agg_node->valid) vsi->agg_node->num_vsis--; @@ -3278,6 +3295,12 @@ int ice_vsi_rebuild(struct ice_vsi *vsi, bool init_vsi) */ if (test_bit(ICE_FLAG_RSS_ENA, pf->flags)) ice_vsi_cfg_rss_lut_key(vsi); + + /* disable or enable CRC stripping */ + if (vsi->netdev) + ice_vsi_cfg_crc_strip(vsi, !!(vsi->netdev->features & + NETIF_F_RXFCS)); + break; case ICE_VSI_VF: ret = ice_vsi_alloc_q_vectors(vsi); diff --git a/drivers/net/ethernet/intel/ice/ice_lib.h b/drivers/net/ethernet/intel/ice/ice_lib.h index 8712b1d2ceec..ec4bf0c89857 100644 --- a/drivers/net/ethernet/intel/ice/ice_lib.h +++ b/drivers/net/ethernet/intel/ice/ice_lib.h @@ -89,6 +89,8 @@ void ice_vsi_free_tx_rings(struct ice_vsi *vsi); void ice_vsi_manage_rss_lut(struct ice_vsi *vsi, bool ena); +void ice_vsi_cfg_crc_strip(struct ice_vsi *vsi, bool disable); + void ice_update_tx_ring_stats(struct ice_tx_ring *ring, u64 pkts, u64 bytes); void ice_update_rx_ring_stats(struct ice_rx_ring *ring, u64 pkts, u64 bytes); diff --git a/drivers/net/ethernet/intel/ice/ice_main.c b/drivers/net/ethernet/intel/ice/ice_main.c index e109cb93886b..0f6718719453 100644 --- a/drivers/net/ethernet/intel/ice/ice_main.c +++ b/drivers/net/ethernet/intel/ice/ice_main.c @@ -3093,7 +3093,8 @@ static irqreturn_t ice_misc_intr(int __always_unused irq, void *data) if (oicr & PFINT_OICR_TSYN_TX_M) { ena_mask &= ~PFINT_OICR_TSYN_TX_M; - ice_ptp_process_ts(pf); + if (!hw->reset_ongoing) + ret = IRQ_WAKE_THREAD; } if (oicr & PFINT_OICR_TSYN_EVNT_M) { @@ -3128,7 +3129,8 @@ static irqreturn_t ice_misc_intr(int __always_unused irq, void *data) ice_service_task_schedule(pf); } } - ret = IRQ_HANDLED; + if (!ret) + ret = IRQ_HANDLED; ice_service_task_schedule(pf); ice_irq_dynamic_ena(hw, NULL, NULL); @@ -3137,6 +3139,24 @@ static irqreturn_t ice_misc_intr(int __always_unused irq, void *data) } /** + * ice_misc_intr_thread_fn - misc interrupt thread function + * @irq: interrupt number + * @data: pointer to a q_vector + */ +static irqreturn_t ice_misc_intr_thread_fn(int __always_unused irq, void *data) +{ + irqreturn_t ret = IRQ_HANDLED; + struct ice_pf *pf = data; + bool irq_handled; + + irq_handled = ice_ptp_process_ts(pf); + if (!irq_handled) + ret = IRQ_WAKE_THREAD; + + return ret; +} + +/** * ice_dis_ctrlq_interrupts - disable control queue interrupts * @hw: pointer to HW structure */ @@ -3248,10 +3268,12 @@ static int ice_req_irq_msix_misc(struct ice_pf *pf) pf->num_avail_sw_msix -= 1; pf->oicr_idx = (u16)oicr_idx; - err = devm_request_irq(dev, pf->msix_entries[pf->oicr_idx].vector, - ice_misc_intr, 0, pf->int_name, pf); + err = devm_request_threaded_irq(dev, + pf->msix_entries[pf->oicr_idx].vector, + ice_misc_intr, ice_misc_intr_thread_fn, + 0, pf->int_name, pf); if (err) { - dev_err(dev, "devm_request_irq for %s failed: %d\n", + dev_err(dev, "devm_request_threaded_irq for %s failed: %d\n", pf->int_name, err); ice_free_res(pf->irq_tracker, 1, ICE_RES_MISC_VEC_ID); pf->num_avail_sw_msix += 1; @@ -3288,7 +3310,7 @@ static void ice_napi_add(struct ice_vsi *vsi) ice_for_each_q_vector(vsi, v_idx) netif_napi_add(vsi->netdev, &vsi->q_vectors[v_idx]->napi, - ice_napi_poll, NAPI_POLL_WEIGHT); + ice_napi_poll); } /** @@ -3391,6 +3413,11 @@ static void ice_set_netdev_features(struct net_device *netdev) if (is_dvm_ena) netdev->hw_features |= NETIF_F_HW_VLAN_STAG_RX | NETIF_F_HW_VLAN_STAG_TX; + + /* Leave CRC / FCS stripping enabled by default, but allow the value to + * be changed at runtime + */ + netdev->hw_features |= NETIF_F_RXFCS; } /** @@ -3923,87 +3950,134 @@ static int ice_init_pf(struct ice_pf *pf) } /** + * ice_reduce_msix_usage - Reduce usage of MSI-X vectors + * @pf: board private structure + * @v_remain: number of remaining MSI-X vectors to be distributed + * + * Reduce the usage of MSI-X vectors when entire request cannot be fulfilled. + * pf->num_lan_msix and pf->num_rdma_msix values are set based on number of + * remaining vectors. + */ +static void ice_reduce_msix_usage(struct ice_pf *pf, int v_remain) +{ + int v_rdma; + + if (!ice_is_rdma_ena(pf)) { + pf->num_lan_msix = v_remain; + return; + } + + /* RDMA needs at least 1 interrupt in addition to AEQ MSIX */ + v_rdma = ICE_RDMA_NUM_AEQ_MSIX + 1; + + if (v_remain < ICE_MIN_LAN_TXRX_MSIX + ICE_MIN_RDMA_MSIX) { + dev_warn(ice_pf_to_dev(pf), "Not enough MSI-X vectors to support RDMA.\n"); + clear_bit(ICE_FLAG_RDMA_ENA, pf->flags); + + pf->num_rdma_msix = 0; + pf->num_lan_msix = ICE_MIN_LAN_TXRX_MSIX; + } else if ((v_remain < ICE_MIN_LAN_TXRX_MSIX + v_rdma) || + (v_remain - v_rdma < v_rdma)) { + /* Support minimum RDMA and give remaining vectors to LAN MSIX */ + pf->num_rdma_msix = ICE_MIN_RDMA_MSIX; + pf->num_lan_msix = v_remain - ICE_MIN_RDMA_MSIX; + } else { + /* Split remaining MSIX with RDMA after accounting for AEQ MSIX + */ + pf->num_rdma_msix = (v_remain - ICE_RDMA_NUM_AEQ_MSIX) / 2 + + ICE_RDMA_NUM_AEQ_MSIX; + pf->num_lan_msix = v_remain - pf->num_rdma_msix; + } +} + +/** * ice_ena_msix_range - Request a range of MSIX vectors from the OS * @pf: board private structure * - * compute the number of MSIX vectors required (v_budget) and request from - * the OS. Return the number of vectors reserved or negative on failure + * Compute the number of MSIX vectors wanted and request from the OS. Adjust + * device usage if there are not enough vectors. Return the number of vectors + * reserved or negative on failure. */ static int ice_ena_msix_range(struct ice_pf *pf) { - int num_cpus, v_left, v_actual, v_other, v_budget = 0; + int num_cpus, hw_num_msix, v_other, v_wanted, v_actual; struct device *dev = ice_pf_to_dev(pf); - int needed, err, i; + int err, i; - v_left = pf->hw.func_caps.common_cap.num_msix_vectors; + hw_num_msix = pf->hw.func_caps.common_cap.num_msix_vectors; num_cpus = num_online_cpus(); - /* reserve for LAN miscellaneous handler */ - needed = ICE_MIN_LAN_OICR_MSIX; - if (v_left < needed) - goto no_hw_vecs_left_err; - v_budget += needed; - v_left -= needed; + /* LAN miscellaneous handler */ + v_other = ICE_MIN_LAN_OICR_MSIX; - /* reserve for flow director */ - if (test_bit(ICE_FLAG_FD_ENA, pf->flags)) { - needed = ICE_FDIR_MSIX; - if (v_left < needed) - goto no_hw_vecs_left_err; - v_budget += needed; - v_left -= needed; - } - - /* reserve for switchdev */ - needed = ICE_ESWITCH_MSIX; - if (v_left < needed) - goto no_hw_vecs_left_err; - v_budget += needed; - v_left -= needed; - - /* total used for non-traffic vectors */ - v_other = v_budget; - - /* reserve vectors for LAN traffic */ - needed = num_cpus; - if (v_left < needed) - goto no_hw_vecs_left_err; - pf->num_lan_msix = needed; - v_budget += needed; - v_left -= needed; - - /* reserve vectors for RDMA auxiliary driver */ + /* Flow Director */ + if (test_bit(ICE_FLAG_FD_ENA, pf->flags)) + v_other += ICE_FDIR_MSIX; + + /* switchdev */ + v_other += ICE_ESWITCH_MSIX; + + v_wanted = v_other; + + /* LAN traffic */ + pf->num_lan_msix = num_cpus; + v_wanted += pf->num_lan_msix; + + /* RDMA auxiliary driver */ if (ice_is_rdma_ena(pf)) { - needed = num_cpus + ICE_RDMA_NUM_AEQ_MSIX; - if (v_left < needed) - goto no_hw_vecs_left_err; - pf->num_rdma_msix = needed; - v_budget += needed; - v_left -= needed; + pf->num_rdma_msix = num_cpus + ICE_RDMA_NUM_AEQ_MSIX; + v_wanted += pf->num_rdma_msix; } - pf->msix_entries = devm_kcalloc(dev, v_budget, + if (v_wanted > hw_num_msix) { + int v_remain; + + dev_warn(dev, "not enough device MSI-X vectors. wanted = %d, available = %d\n", + v_wanted, hw_num_msix); + + if (hw_num_msix < ICE_MIN_MSIX) { + err = -ERANGE; + goto exit_err; + } + + v_remain = hw_num_msix - v_other; + if (v_remain < ICE_MIN_LAN_TXRX_MSIX) { + v_other = ICE_MIN_MSIX - ICE_MIN_LAN_TXRX_MSIX; + v_remain = ICE_MIN_LAN_TXRX_MSIX; + } + + ice_reduce_msix_usage(pf, v_remain); + v_wanted = pf->num_lan_msix + pf->num_rdma_msix + v_other; + + dev_notice(dev, "Reducing request to %d MSI-X vectors for LAN traffic.\n", + pf->num_lan_msix); + if (ice_is_rdma_ena(pf)) + dev_notice(dev, "Reducing request to %d MSI-X vectors for RDMA.\n", + pf->num_rdma_msix); + } + + pf->msix_entries = devm_kcalloc(dev, v_wanted, sizeof(*pf->msix_entries), GFP_KERNEL); if (!pf->msix_entries) { err = -ENOMEM; goto exit_err; } - for (i = 0; i < v_budget; i++) + for (i = 0; i < v_wanted; i++) pf->msix_entries[i].entry = i; /* actually reserve the vectors */ v_actual = pci_enable_msix_range(pf->pdev, pf->msix_entries, - ICE_MIN_MSIX, v_budget); + ICE_MIN_MSIX, v_wanted); if (v_actual < 0) { dev_err(dev, "unable to reserve MSI-X vectors\n"); err = v_actual; goto msix_err; } - if (v_actual < v_budget) { + if (v_actual < v_wanted) { dev_warn(dev, "not enough OS MSI-X vectors. requested = %d, obtained = %d\n", - v_budget, v_actual); + v_wanted, v_actual); if (v_actual < ICE_MIN_MSIX) { /* error if we can't get minimum vectors */ @@ -4012,38 +4086,11 @@ static int ice_ena_msix_range(struct ice_pf *pf) goto msix_err; } else { int v_remain = v_actual - v_other; - int v_rdma = 0, v_min_rdma = 0; - if (ice_is_rdma_ena(pf)) { - /* Need at least 1 interrupt in addition to - * AEQ MSIX - */ - v_rdma = ICE_RDMA_NUM_AEQ_MSIX + 1; - v_min_rdma = ICE_MIN_RDMA_MSIX; - } + if (v_remain < ICE_MIN_LAN_TXRX_MSIX) + v_remain = ICE_MIN_LAN_TXRX_MSIX; - if (v_actual == ICE_MIN_MSIX || - v_remain < ICE_MIN_LAN_TXRX_MSIX + v_min_rdma) { - dev_warn(dev, "Not enough MSI-X vectors to support RDMA.\n"); - clear_bit(ICE_FLAG_RDMA_ENA, pf->flags); - - pf->num_rdma_msix = 0; - pf->num_lan_msix = ICE_MIN_LAN_TXRX_MSIX; - } else if ((v_remain < ICE_MIN_LAN_TXRX_MSIX + v_rdma) || - (v_remain - v_rdma < v_rdma)) { - /* Support minimum RDMA and give remaining - * vectors to LAN MSIX - */ - pf->num_rdma_msix = v_min_rdma; - pf->num_lan_msix = v_remain - v_min_rdma; - } else { - /* Split remaining MSIX with RDMA after - * accounting for AEQ MSIX - */ - pf->num_rdma_msix = (v_remain - ICE_RDMA_NUM_AEQ_MSIX) / 2 + - ICE_RDMA_NUM_AEQ_MSIX; - pf->num_lan_msix = v_remain - pf->num_rdma_msix; - } + ice_reduce_msix_usage(pf, v_remain); dev_notice(dev, "Enabled %d MSI-X vectors for LAN traffic.\n", pf->num_lan_msix); @@ -4058,12 +4105,7 @@ static int ice_ena_msix_range(struct ice_pf *pf) msix_err: devm_kfree(dev, pf->msix_entries); - goto exit_err; -no_hw_vecs_left_err: - dev_err(dev, "not enough device MSI-X vectors. requested = %d, available = %d\n", - needed, v_left); - err = -ERANGE; exit_err: pf->num_rdma_msix = 0; pf->num_lan_msix = 0; @@ -4557,6 +4599,10 @@ static int ice_register_netdev(struct ice_pf *pf) if (!vsi || !vsi->netdev) return -EIO; + err = ice_devlink_create_pf_port(pf); + if (err) + goto err_devlink_create; + err = register_netdev(vsi->netdev); if (err) goto err_register_netdev; @@ -4564,17 +4610,13 @@ static int ice_register_netdev(struct ice_pf *pf) set_bit(ICE_VSI_NETDEV_REGISTERED, vsi->state); netif_carrier_off(vsi->netdev); netif_tx_stop_all_queues(vsi->netdev); - err = ice_devlink_create_pf_port(pf); - if (err) - goto err_devlink_create; devlink_port_type_eth_set(&pf->devlink_port, vsi->netdev); return 0; -err_devlink_create: - unregister_netdev(vsi->netdev); - clear_bit(ICE_VSI_NETDEV_REGISTERED, vsi->state); err_register_netdev: + ice_devlink_destroy_pf_port(pf); +err_devlink_create: free_netdev(vsi->netdev); vsi->netdev = NULL; clear_bit(ICE_VSI_NETDEV_ALLOCD, vsi->state); @@ -4682,8 +4724,6 @@ ice_probe(struct pci_dev *pdev, const struct pci_device_id __always_unused *ent) ice_set_safe_mode_caps(hw); } - hw->ucast_shared = true; - err = ice_init_pf(pf); if (err) { dev_err(dev, "ice_init_pf failed: %d\n", err); @@ -5742,6 +5782,9 @@ ice_fdb_del(struct ndmsg *ndm, __always_unused struct nlattr *tb[], NETIF_F_HW_VLAN_STAG_RX | \ NETIF_F_HW_VLAN_STAG_TX) +#define NETIF_VLAN_STRIPPING_FEATURES (NETIF_F_HW_VLAN_CTAG_RX | \ + NETIF_F_HW_VLAN_STAG_RX) + #define NETIF_VLAN_FILTERING_FEATURES (NETIF_F_HW_VLAN_CTAG_FILTER | \ NETIF_F_HW_VLAN_STAG_FILTER) @@ -5828,6 +5871,14 @@ ice_fix_features(struct net_device *netdev, netdev_features_t features) NETIF_F_HW_VLAN_STAG_TX); } + if (!(netdev->features & NETIF_F_RXFCS) && + (features & NETIF_F_RXFCS) && + (features & NETIF_VLAN_STRIPPING_FEATURES) && + !ice_vsi_has_non_zero_vlans(np->vsi)) { + netdev_warn(netdev, "Disabling VLAN stripping as FCS/CRC stripping is also disabled and there is no VLAN configured\n"); + features &= ~NETIF_VLAN_STRIPPING_FEATURES; + } + return features; } @@ -5921,6 +5972,13 @@ ice_set_vlan_features(struct net_device *netdev, netdev_features_t features) current_vlan_features = netdev->features & NETIF_VLAN_OFFLOAD_FEATURES; requested_vlan_features = features & NETIF_VLAN_OFFLOAD_FEATURES; if (current_vlan_features ^ requested_vlan_features) { + if ((features & NETIF_F_RXFCS) && + (features & NETIF_VLAN_STRIPPING_FEATURES)) { + dev_err(ice_pf_to_dev(vsi->back), + "To enable VLAN stripping, you must first enable FCS/CRC stripping\n"); + return -EIO; + } + err = ice_set_vlan_offload_features(vsi, features); if (err) return err; @@ -6002,6 +6060,23 @@ ice_set_features(struct net_device *netdev, netdev_features_t features) if (ret) return ret; + /* Turn on receive of FCS aka CRC, and after setting this + * flag the packet data will have the 4 byte CRC appended + */ + if (changed & NETIF_F_RXFCS) { + if ((features & NETIF_F_RXFCS) && + (features & NETIF_VLAN_STRIPPING_FEATURES)) { + dev_err(ice_pf_to_dev(vsi->back), + "To disable FCS/CRC stripping, you must first disable VLAN stripping\n"); + return -EIO; + } + + ice_vsi_cfg_crc_strip(vsi, !!(features & NETIF_F_RXFCS)); + ret = ice_down_up(vsi); + if (ret) + return ret; + } + if (changed & NETIF_F_NTUPLE) { bool ena = !!(features & NETIF_F_NTUPLE); @@ -6699,6 +6774,31 @@ int ice_down(struct ice_vsi *vsi) } /** + * ice_down_up - shutdown the VSI connection and bring it up + * @vsi: the VSI to be reconnected + */ +int ice_down_up(struct ice_vsi *vsi) +{ + int ret; + + /* if DOWN already set, nothing to do */ + if (test_and_set_bit(ICE_VSI_DOWN, vsi->state)) + return 0; + + ret = ice_down(vsi); + if (ret) + return ret; + + ret = ice_up(vsi); + if (ret) { + netdev_err(vsi->netdev, "reallocating resources failed during netdev features change, may need to reload driver\n"); + return ret; + } + + return 0; +} + +/** * ice_vsi_setup_tx_rings - Allocate VSI Tx queue resources * @vsi: VSI having resources allocated * diff --git a/drivers/net/ethernet/intel/ice/ice_nvm.c b/drivers/net/ethernet/intel/ice/ice_nvm.c index 13cdb5ea594d..c262dc886e6a 100644 --- a/drivers/net/ethernet/intel/ice/ice_nvm.c +++ b/drivers/net/ethernet/intel/ice/ice_nvm.c @@ -1114,14 +1114,18 @@ int ice_nvm_validate_checksum(struct ice_hw *hw) * Update the control word with the required banks' validity bits * and dumps the Shadow RAM to flash (0x0707) * - * cmd_flags controls which banks to activate, and the preservation level to - * use when activating the NVM bank. + * cmd_flags controls which banks to activate, the preservation level to use + * when activating the NVM bank, and whether an EMP reset is required for + * activation. + * + * Note that the 16bit cmd_flags value is split between two separate 1 byte + * flag values in the descriptor. * * On successful return of the firmware command, the response_flags variable * is updated with the flags reported by firmware indicating certain status, * such as whether EMP reset is enabled. */ -int ice_nvm_write_activate(struct ice_hw *hw, u8 cmd_flags, u8 *response_flags) +int ice_nvm_write_activate(struct ice_hw *hw, u16 cmd_flags, u8 *response_flags) { struct ice_aqc_nvm *cmd; struct ice_aq_desc desc; @@ -1130,7 +1134,8 @@ int ice_nvm_write_activate(struct ice_hw *hw, u8 cmd_flags, u8 *response_flags) cmd = &desc.params.nvm; ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_nvm_write_activate); - cmd->cmd_flags = cmd_flags; + cmd->cmd_flags = (u8)(cmd_flags & 0xFF); + cmd->offset_high = (u8)((cmd_flags >> 8) & 0xFF); err = ice_aq_send_cmd(hw, &desc, NULL, 0, NULL); if (!err && response_flags) diff --git a/drivers/net/ethernet/intel/ice/ice_nvm.h b/drivers/net/ethernet/intel/ice/ice_nvm.h index 856d1ad4398b..774c2317967d 100644 --- a/drivers/net/ethernet/intel/ice/ice_nvm.h +++ b/drivers/net/ethernet/intel/ice/ice_nvm.h @@ -34,7 +34,7 @@ ice_aq_update_nvm(struct ice_hw *hw, u16 module_typeid, u32 offset, int ice_aq_erase_nvm(struct ice_hw *hw, u16 module_typeid, struct ice_sq_cd *cd); int ice_nvm_validate_checksum(struct ice_hw *hw); -int ice_nvm_write_activate(struct ice_hw *hw, u8 cmd_flags, u8 *response_flags); +int ice_nvm_write_activate(struct ice_hw *hw, u16 cmd_flags, u8 *response_flags); int ice_aq_nvm_update_empr(struct ice_hw *hw); int ice_nvm_set_pkg_data(struct ice_hw *hw, bool del_pkg_data_flag, u8 *data, diff --git a/drivers/net/ethernet/intel/ice/ice_protocol_type.h b/drivers/net/ethernet/intel/ice/ice_protocol_type.h index 560efc7654c7..02a4e1cf624e 100644 --- a/drivers/net/ethernet/intel/ice/ice_protocol_type.h +++ b/drivers/net/ethernet/intel/ice/ice_protocol_type.h @@ -44,6 +44,7 @@ enum ice_protocol_type { ICE_GTP, ICE_GTP_NO_PAY, ICE_PPPOE, + ICE_L2TPV3, ICE_VLAN_EX, ICE_VLAN_IN, ICE_VXLAN_GPE, @@ -111,6 +112,7 @@ enum ice_prot_id { #define ICE_UDP_ILOS_HW 53 #define ICE_GRE_OF_HW 64 #define ICE_PPPOE_HW 103 +#define ICE_L2TPV3_HW 104 #define ICE_UDP_OF_HW 52 /* UDP Tunnels */ #define ICE_META_DATA_ID_HW 255 /* this is used for tunnel and VLAN type */ @@ -217,6 +219,11 @@ struct ice_pppoe_hdr { __be16 ppp_prot_id; /* control and data only */ }; +struct ice_l2tpv3_sess_hdr { + __be32 session_id; + __be64 cookie; +}; + struct ice_nvgre_hdr { __be16 flags; __be16 protocol; @@ -235,6 +242,7 @@ union ice_prot_hdr { struct ice_nvgre_hdr nvgre_hdr; struct ice_udp_gtp_hdr gtp_hdr; struct ice_pppoe_hdr pppoe_hdr; + struct ice_l2tpv3_sess_hdr l2tpv3_sess_hdr; }; /* This is mapping table entry that maps every word within a given protocol diff --git a/drivers/net/ethernet/intel/ice/ice_ptp.c b/drivers/net/ethernet/intel/ice/ice_ptp.c index 72b663108a4a..011b727ab190 100644 --- a/drivers/net/ethernet/intel/ice/ice_ptp.c +++ b/drivers/net/ethernet/intel/ice/ice_ptp.c @@ -491,56 +491,6 @@ ice_ptp_read_src_clk_reg(struct ice_pf *pf, struct ptp_system_timestamp *sts) } /** - * ice_ptp_update_cached_phctime - Update the cached PHC time values - * @pf: Board specific private structure - * - * This function updates the system time values which are cached in the PF - * structure and the Rx rings. - * - * This function must be called periodically to ensure that the cached value - * is never more than 2 seconds old. It must also be called whenever the PHC - * time has been changed. - * - * Return: - * * 0 - OK, successfully updated - * * -EAGAIN - PF was busy, need to reschedule the update - */ -static int ice_ptp_update_cached_phctime(struct ice_pf *pf) -{ - u64 systime; - int i; - - if (test_and_set_bit(ICE_CFG_BUSY, pf->state)) - return -EAGAIN; - - /* Read the current PHC time */ - systime = ice_ptp_read_src_clk_reg(pf, NULL); - - /* Update the cached PHC time stored in the PF structure */ - WRITE_ONCE(pf->ptp.cached_phc_time, systime); - - ice_for_each_vsi(pf, i) { - struct ice_vsi *vsi = pf->vsi[i]; - int j; - - if (!vsi) - continue; - - if (vsi->type != ICE_VSI_PF) - continue; - - ice_for_each_rxq(vsi, j) { - if (!vsi->rx_rings[j]) - continue; - WRITE_ONCE(vsi->rx_rings[j]->cached_phctime, systime); - } - } - clear_bit(ICE_CFG_BUSY, pf->state); - - return 0; -} - -/** * ice_ptp_extend_32b_ts - Convert a 32b nanoseconds timestamp to 64b * @cached_phc_time: recently cached copy of PHC time * @in_tstamp: Ingress/egress 32b nanoseconds timestamp value @@ -636,12 +586,400 @@ static u64 ice_ptp_extend_32b_ts(u64 cached_phc_time, u32 in_tstamp) static u64 ice_ptp_extend_40b_ts(struct ice_pf *pf, u64 in_tstamp) { const u64 mask = GENMASK_ULL(31, 0); + unsigned long discard_time; + + /* Discard the hardware timestamp if the cached PHC time is too old */ + discard_time = pf->ptp.cached_phc_jiffies + msecs_to_jiffies(2000); + if (time_is_before_jiffies(discard_time)) { + pf->ptp.tx_hwtstamp_discarded++; + return 0; + } return ice_ptp_extend_32b_ts(pf->ptp.cached_phc_time, (in_tstamp >> 8) & mask); } /** + * ice_ptp_tx_tstamp - Process Tx timestamps for a port + * @tx: the PTP Tx timestamp tracker + * + * Process timestamps captured by the PHY associated with this port. To do + * this, loop over each index with a waiting skb. + * + * If a given index has a valid timestamp, perform the following steps: + * + * 1) copy the timestamp out of the PHY register + * 4) clear the timestamp valid bit in the PHY register + * 5) unlock the index by clearing the associated in_use bit. + * 2) extend the 40b timestamp value to get a 64bit timestamp + * 3) send that timestamp to the stack + * + * After looping, if we still have waiting SKBs, return true. This may cause us + * effectively poll even when not strictly necessary. We do this because it's + * possible a new timestamp was requested around the same time as the interrupt. + * In some cases hardware might not interrupt us again when the timestamp is + * captured. + * + * Note that we only take the tracking lock when clearing the bit and when + * checking if we need to re-queue this task. The only place where bits can be + * set is the hard xmit routine where an SKB has a request flag set. The only + * places where we clear bits are this work function, or the periodic cleanup + * thread. If the cleanup thread clears a bit we're processing we catch it + * when we lock to clear the bit and then grab the SKB pointer. If a Tx thread + * starts a new timestamp, we might not begin processing it right away but we + * will notice it at the end when we re-queue the task. If a Tx thread starts + * a new timestamp just after this function exits without re-queuing, + * the interrupt when the timestamp finishes should trigger. Avoiding holding + * the lock for the entire function is important in order to ensure that Tx + * threads do not get blocked while waiting for the lock. + */ +static bool ice_ptp_tx_tstamp(struct ice_ptp_tx *tx) +{ + struct ice_ptp_port *ptp_port; + bool ts_handled = true; + struct ice_pf *pf; + u8 idx; + + if (!tx->init) + return false; + + ptp_port = container_of(tx, struct ice_ptp_port, tx); + pf = ptp_port_to_pf(ptp_port); + + for_each_set_bit(idx, tx->in_use, tx->len) { + struct skb_shared_hwtstamps shhwtstamps = {}; + u8 phy_idx = idx + tx->quad_offset; + u64 raw_tstamp, tstamp; + struct sk_buff *skb; + int err; + + ice_trace(tx_tstamp_fw_req, tx->tstamps[idx].skb, idx); + + err = ice_read_phy_tstamp(&pf->hw, tx->quad, phy_idx, + &raw_tstamp); + if (err) + continue; + + ice_trace(tx_tstamp_fw_done, tx->tstamps[idx].skb, idx); + + /* Check if the timestamp is invalid or stale */ + if (!(raw_tstamp & ICE_PTP_TS_VALID) || + raw_tstamp == tx->tstamps[idx].cached_tstamp) + continue; + + /* The timestamp is valid, so we'll go ahead and clear this + * index and then send the timestamp up to the stack. + */ + spin_lock(&tx->lock); + tx->tstamps[idx].cached_tstamp = raw_tstamp; + clear_bit(idx, tx->in_use); + skb = tx->tstamps[idx].skb; + tx->tstamps[idx].skb = NULL; + spin_unlock(&tx->lock); + + /* it's (unlikely but) possible we raced with the cleanup + * thread for discarding old timestamp requests. + */ + if (!skb) + continue; + + /* Extend the timestamp using cached PHC time */ + tstamp = ice_ptp_extend_40b_ts(pf, raw_tstamp); + if (tstamp) { + shhwtstamps.hwtstamp = ns_to_ktime(tstamp); + ice_trace(tx_tstamp_complete, skb, idx); + } + + skb_tstamp_tx(skb, &shhwtstamps); + dev_kfree_skb_any(skb); + } + + /* Check if we still have work to do. If so, re-queue this task to + * poll for remaining timestamps. + */ + spin_lock(&tx->lock); + if (!bitmap_empty(tx->in_use, tx->len)) + ts_handled = false; + spin_unlock(&tx->lock); + + return ts_handled; +} + +/** + * ice_ptp_alloc_tx_tracker - Initialize tracking for Tx timestamps + * @tx: Tx tracking structure to initialize + * + * Assumes that the length has already been initialized. Do not call directly, + * use the ice_ptp_init_tx_e822 or ice_ptp_init_tx_e810 instead. + */ +static int +ice_ptp_alloc_tx_tracker(struct ice_ptp_tx *tx) +{ + tx->tstamps = kcalloc(tx->len, sizeof(*tx->tstamps), GFP_KERNEL); + if (!tx->tstamps) + return -ENOMEM; + + tx->in_use = bitmap_zalloc(tx->len, GFP_KERNEL); + if (!tx->in_use) { + kfree(tx->tstamps); + tx->tstamps = NULL; + return -ENOMEM; + } + + spin_lock_init(&tx->lock); + + tx->init = 1; + + return 0; +} + +/** + * ice_ptp_flush_tx_tracker - Flush any remaining timestamps from the tracker + * @pf: Board private structure + * @tx: the tracker to flush + */ +static void +ice_ptp_flush_tx_tracker(struct ice_pf *pf, struct ice_ptp_tx *tx) +{ + u8 idx; + + for (idx = 0; idx < tx->len; idx++) { + u8 phy_idx = idx + tx->quad_offset; + + spin_lock(&tx->lock); + if (tx->tstamps[idx].skb) { + dev_kfree_skb_any(tx->tstamps[idx].skb); + tx->tstamps[idx].skb = NULL; + pf->ptp.tx_hwtstamp_flushed++; + } + clear_bit(idx, tx->in_use); + spin_unlock(&tx->lock); + + /* Clear any potential residual timestamp in the PHY block */ + if (!pf->hw.reset_ongoing) + ice_clear_phy_tstamp(&pf->hw, tx->quad, phy_idx); + } +} + +/** + * ice_ptp_release_tx_tracker - Release allocated memory for Tx tracker + * @pf: Board private structure + * @tx: Tx tracking structure to release + * + * Free memory associated with the Tx timestamp tracker. + */ +static void +ice_ptp_release_tx_tracker(struct ice_pf *pf, struct ice_ptp_tx *tx) +{ + tx->init = 0; + + ice_ptp_flush_tx_tracker(pf, tx); + + kfree(tx->tstamps); + tx->tstamps = NULL; + + bitmap_free(tx->in_use); + tx->in_use = NULL; + + tx->len = 0; +} + +/** + * ice_ptp_init_tx_e822 - Initialize tracking for Tx timestamps + * @pf: Board private structure + * @tx: the Tx tracking structure to initialize + * @port: the port this structure tracks + * + * Initialize the Tx timestamp tracker for this port. For generic MAC devices, + * the timestamp block is shared for all ports in the same quad. To avoid + * ports using the same timestamp index, logically break the block of + * registers into chunks based on the port number. + */ +static int +ice_ptp_init_tx_e822(struct ice_pf *pf, struct ice_ptp_tx *tx, u8 port) +{ + tx->quad = port / ICE_PORTS_PER_QUAD; + tx->quad_offset = (port % ICE_PORTS_PER_QUAD) * INDEX_PER_PORT; + tx->len = INDEX_PER_PORT; + + return ice_ptp_alloc_tx_tracker(tx); +} + +/** + * ice_ptp_init_tx_e810 - Initialize tracking for Tx timestamps + * @pf: Board private structure + * @tx: the Tx tracking structure to initialize + * + * Initialize the Tx timestamp tracker for this PF. For E810 devices, each + * port has its own block of timestamps, independent of the other ports. + */ +static int +ice_ptp_init_tx_e810(struct ice_pf *pf, struct ice_ptp_tx *tx) +{ + tx->quad = pf->hw.port_info->lport; + tx->quad_offset = 0; + tx->len = INDEX_PER_QUAD; + + return ice_ptp_alloc_tx_tracker(tx); +} + +/** + * ice_ptp_tx_tstamp_cleanup - Cleanup old timestamp requests that got dropped + * @pf: pointer to the PF struct + * @tx: PTP Tx tracker to clean up + * + * Loop through the Tx timestamp requests and see if any of them have been + * waiting for a long time. Discard any SKBs that have been waiting for more + * than 2 seconds. This is long enough to be reasonably sure that the + * timestamp will never be captured. This might happen if the packet gets + * discarded before it reaches the PHY timestamping block. + */ +static void ice_ptp_tx_tstamp_cleanup(struct ice_pf *pf, struct ice_ptp_tx *tx) +{ + struct ice_hw *hw = &pf->hw; + u8 idx; + + if (!tx->init) + return; + + for_each_set_bit(idx, tx->in_use, tx->len) { + struct sk_buff *skb; + u64 raw_tstamp; + + /* Check if this SKB has been waiting for too long */ + if (time_is_after_jiffies(tx->tstamps[idx].start + 2 * HZ)) + continue; + + /* Read tstamp to be able to use this register again */ + ice_read_phy_tstamp(hw, tx->quad, idx + tx->quad_offset, + &raw_tstamp); + + spin_lock(&tx->lock); + skb = tx->tstamps[idx].skb; + tx->tstamps[idx].skb = NULL; + clear_bit(idx, tx->in_use); + spin_unlock(&tx->lock); + + /* Count the number of Tx timestamps which have timed out */ + pf->ptp.tx_hwtstamp_timeouts++; + + /* Free the SKB after we've cleared the bit */ + dev_kfree_skb_any(skb); + } +} + +/** + * ice_ptp_update_cached_phctime - Update the cached PHC time values + * @pf: Board specific private structure + * + * This function updates the system time values which are cached in the PF + * structure and the Rx rings. + * + * This function must be called periodically to ensure that the cached value + * is never more than 2 seconds old. + * + * Note that the cached copy in the PF PTP structure is always updated, even + * if we can't update the copy in the Rx rings. + * + * Return: + * * 0 - OK, successfully updated + * * -EAGAIN - PF was busy, need to reschedule the update + */ +static int ice_ptp_update_cached_phctime(struct ice_pf *pf) +{ + struct device *dev = ice_pf_to_dev(pf); + unsigned long update_before; + u64 systime; + int i; + + update_before = pf->ptp.cached_phc_jiffies + msecs_to_jiffies(2000); + if (pf->ptp.cached_phc_time && + time_is_before_jiffies(update_before)) { + unsigned long time_taken = jiffies - pf->ptp.cached_phc_jiffies; + + dev_warn(dev, "%u msecs passed between update to cached PHC time\n", + jiffies_to_msecs(time_taken)); + pf->ptp.late_cached_phc_updates++; + } + + /* Read the current PHC time */ + systime = ice_ptp_read_src_clk_reg(pf, NULL); + + /* Update the cached PHC time stored in the PF structure */ + WRITE_ONCE(pf->ptp.cached_phc_time, systime); + WRITE_ONCE(pf->ptp.cached_phc_jiffies, jiffies); + + if (test_and_set_bit(ICE_CFG_BUSY, pf->state)) + return -EAGAIN; + + ice_for_each_vsi(pf, i) { + struct ice_vsi *vsi = pf->vsi[i]; + int j; + + if (!vsi) + continue; + + if (vsi->type != ICE_VSI_PF) + continue; + + ice_for_each_rxq(vsi, j) { + if (!vsi->rx_rings[j]) + continue; + WRITE_ONCE(vsi->rx_rings[j]->cached_phctime, systime); + } + } + clear_bit(ICE_CFG_BUSY, pf->state); + + return 0; +} + +/** + * ice_ptp_reset_cached_phctime - Reset cached PHC time after an update + * @pf: Board specific private structure + * + * This function must be called when the cached PHC time is no longer valid, + * such as after a time adjustment. It discards any outstanding Tx timestamps, + * and updates the cached PHC time for both the PF and Rx rings. If updating + * the PHC time cannot be done immediately, a warning message is logged and + * the work item is scheduled. + * + * These steps are required in order to ensure that we do not accidentally + * report a timestamp extended by the wrong PHC cached copy. Note that we + * do not directly update the cached timestamp here because it is possible + * this might produce an error when ICE_CFG_BUSY is set. If this occurred, we + * would have to try again. During that time window, timestamps might be + * requested and returned with an invalid extension. Thus, on failure to + * immediately update the cached PHC time we would need to zero the value + * anyways. For this reason, we just zero the value immediately and queue the + * update work item. + */ +static void ice_ptp_reset_cached_phctime(struct ice_pf *pf) +{ + struct device *dev = ice_pf_to_dev(pf); + int err; + + /* Update the cached PHC time immediately if possible, otherwise + * schedule the work item to execute soon. + */ + err = ice_ptp_update_cached_phctime(pf); + if (err) { + /* If another thread is updating the Rx rings, we won't + * properly reset them here. This could lead to reporting of + * invalid timestamps, but there isn't much we can do. + */ + dev_warn(dev, "%s: ICE_CFG_BUSY, unable to immediately update cached PHC time\n", + __func__); + + /* Queue the work item to update the Rx rings when possible */ + kthread_queue_delayed_work(pf->ptp.kworker, &pf->ptp.work, + msecs_to_jiffies(10)); + } + + /* Flush any outstanding Tx timestamps */ + ice_ptp_flush_tx_tracker(pf, &pf->ptp.port.tx); +} + +/** * ice_ptp_read_time - Read the time from the device * @pf: Board private structure * @ts: timespec structure to hold the current time value @@ -900,6 +1238,9 @@ static void ice_ptp_wait_for_offset_valid(struct kthread_work *work) hw = &pf->hw; dev = ice_pf_to_dev(pf); + if (ice_is_reset_in_progress(pf->state)) + return; + if (ice_ptp_check_offset_valid(port)) { /* Offsets not ready yet, try again later */ kthread_queue_delayed_work(pf->ptp.kworker, @@ -1509,7 +1850,7 @@ ice_ptp_settime64(struct ptp_clock_info *info, const struct timespec64 *ts) ice_ptp_unlock(hw); if (!err) - ice_ptp_update_cached_phctime(pf); + ice_ptp_reset_cached_phctime(pf); /* Reenable periodic outputs */ ice_ptp_enable_all_clkout(pf); @@ -1588,7 +1929,7 @@ static int ice_ptp_adjtime(struct ptp_clock_info *info, s64 delta) return err; } - ice_ptp_update_cached_phctime(pf); + ice_ptp_reset_cached_phctime(pf); return 0; } @@ -1796,26 +2137,31 @@ void ice_ptp_rx_hwtstamp(struct ice_rx_ring *rx_ring, union ice_32b_rx_flex_desc *rx_desc, struct sk_buff *skb) { + struct skb_shared_hwtstamps *hwtstamps; + u64 ts_ns, cached_time; u32 ts_high; - u64 ts_ns; - /* Populate timesync data into skb */ - if (rx_desc->wb.time_stamp_low & ICE_PTP_TS_VALID) { - struct skb_shared_hwtstamps *hwtstamps; + if (!(rx_desc->wb.time_stamp_low & ICE_PTP_TS_VALID)) + return; - /* Use ice_ptp_extend_32b_ts directly, using the ring-specific - * cached PHC value, rather than accessing the PF. This also - * allows us to simply pass the upper 32bits of nanoseconds - * directly. Calling ice_ptp_extend_40b_ts is unnecessary as - * it would just discard these bits itself. - */ - ts_high = le32_to_cpu(rx_desc->wb.flex_ts.ts_high); - ts_ns = ice_ptp_extend_32b_ts(rx_ring->cached_phctime, ts_high); + cached_time = READ_ONCE(rx_ring->cached_phctime); - hwtstamps = skb_hwtstamps(skb); - memset(hwtstamps, 0, sizeof(*hwtstamps)); - hwtstamps->hwtstamp = ns_to_ktime(ts_ns); - } + /* Do not report a timestamp if we don't have a cached PHC time */ + if (!cached_time) + return; + + /* Use ice_ptp_extend_32b_ts directly, using the ring-specific cached + * PHC value, rather than accessing the PF. This also allows us to + * simply pass the upper 32bits of nanoseconds directly. Calling + * ice_ptp_extend_40b_ts is unnecessary as it would just discard these + * bits itself. + */ + ts_high = le32_to_cpu(rx_desc->wb.flex_ts.ts_high); + ts_ns = ice_ptp_extend_32b_ts(cached_time, ts_high); + + hwtstamps = skb_hwtstamps(skb); + memset(hwtstamps, 0, sizeof(*hwtstamps)); + hwtstamps->hwtstamp = ns_to_ktime(ts_ns); } /** @@ -1871,49 +2217,26 @@ ice_ptp_setup_sma_pins_e810t(struct ice_pf *pf, struct ptp_clock_info *info) } /** - * ice_ptp_setup_pins_e810t - Setup PTP pins in sysfs + * ice_ptp_setup_pins_e810 - Setup PTP pins in sysfs * @pf: pointer to the PF instance * @info: PTP clock capabilities */ static void -ice_ptp_setup_pins_e810t(struct ice_pf *pf, struct ptp_clock_info *info) +ice_ptp_setup_pins_e810(struct ice_pf *pf, struct ptp_clock_info *info) { - /* Check if SMA controller is in the netlist */ - if (ice_is_feature_supported(pf, ICE_F_SMA_CTRL) && - !ice_is_pca9575_present(&pf->hw)) - ice_clear_feature_support(pf, ICE_F_SMA_CTRL); - - if (!ice_is_feature_supported(pf, ICE_F_SMA_CTRL)) { - info->n_ext_ts = N_EXT_TS_E810_NO_SMA; - info->n_per_out = N_PER_OUT_E810T_NO_SMA; - return; - } + info->n_per_out = N_PER_OUT_E810; - info->n_per_out = N_PER_OUT_E810T; + if (ice_is_feature_supported(pf, ICE_F_PTP_EXTTS)) + info->n_ext_ts = N_EXT_TS_E810; - if (ice_is_feature_supported(pf, ICE_F_PTP_EXTTS)) { + if (ice_is_feature_supported(pf, ICE_F_SMA_CTRL)) { info->n_ext_ts = N_EXT_TS_E810; info->n_pins = NUM_PTP_PINS_E810T; info->verify = ice_verify_pin_e810t; - } - /* Complete setup of the SMA pins */ - ice_ptp_setup_sma_pins_e810t(pf, info); -} - -/** - * ice_ptp_setup_pins_e810 - Setup PTP pins in sysfs - * @pf: pointer to the PF instance - * @info: PTP clock capabilities - */ -static void ice_ptp_setup_pins_e810(struct ice_pf *pf, struct ptp_clock_info *info) -{ - info->n_per_out = N_PER_OUT_E810; - - if (!ice_is_feature_supported(pf, ICE_F_PTP_EXTTS)) - return; - - info->n_ext_ts = N_EXT_TS_E810; + /* Complete setup of the SMA pins */ + ice_ptp_setup_sma_pins_e810t(pf, info); + } } /** @@ -1950,11 +2273,7 @@ static void ice_ptp_set_funcs_e810(struct ice_pf *pf, struct ptp_clock_info *info) { info->enable = ice_ptp_gpio_enable_e810; - - if (ice_is_e810t(&pf->hw)) - ice_ptp_setup_pins_e810t(pf, info); - else - ice_ptp_setup_pins_e810(pf, info); + ice_ptp_setup_pins_e810(pf, info); } /** @@ -2016,112 +2335,6 @@ static long ice_ptp_create_clock(struct ice_pf *pf) } /** - * ice_ptp_tx_tstamp_work - Process Tx timestamps for a port - * @work: pointer to the kthread_work struct - * - * Process timestamps captured by the PHY associated with this port. To do - * this, loop over each index with a waiting skb. - * - * If a given index has a valid timestamp, perform the following steps: - * - * 1) copy the timestamp out of the PHY register - * 4) clear the timestamp valid bit in the PHY register - * 5) unlock the index by clearing the associated in_use bit. - * 2) extend the 40b timestamp value to get a 64bit timestamp - * 3) send that timestamp to the stack - * - * After looping, if we still have waiting SKBs, then re-queue the work. This - * may cause us effectively poll even when not strictly necessary. We do this - * because it's possible a new timestamp was requested around the same time as - * the interrupt. In some cases hardware might not interrupt us again when the - * timestamp is captured. - * - * Note that we only take the tracking lock when clearing the bit and when - * checking if we need to re-queue this task. The only place where bits can be - * set is the hard xmit routine where an SKB has a request flag set. The only - * places where we clear bits are this work function, or the periodic cleanup - * thread. If the cleanup thread clears a bit we're processing we catch it - * when we lock to clear the bit and then grab the SKB pointer. If a Tx thread - * starts a new timestamp, we might not begin processing it right away but we - * will notice it at the end when we re-queue the work item. If a Tx thread - * starts a new timestamp just after this function exits without re-queuing, - * the interrupt when the timestamp finishes should trigger. Avoiding holding - * the lock for the entire function is important in order to ensure that Tx - * threads do not get blocked while waiting for the lock. - */ -static void ice_ptp_tx_tstamp_work(struct kthread_work *work) -{ - struct ice_ptp_port *ptp_port; - struct ice_ptp_tx *tx; - struct ice_pf *pf; - struct ice_hw *hw; - u8 idx; - - tx = container_of(work, struct ice_ptp_tx, work); - if (!tx->init) - return; - - ptp_port = container_of(tx, struct ice_ptp_port, tx); - pf = ptp_port_to_pf(ptp_port); - hw = &pf->hw; - - for_each_set_bit(idx, tx->in_use, tx->len) { - struct skb_shared_hwtstamps shhwtstamps = {}; - u8 phy_idx = idx + tx->quad_offset; - u64 raw_tstamp, tstamp; - struct sk_buff *skb; - int err; - - ice_trace(tx_tstamp_fw_req, tx->tstamps[idx].skb, idx); - - err = ice_read_phy_tstamp(hw, tx->quad, phy_idx, - &raw_tstamp); - if (err) - continue; - - ice_trace(tx_tstamp_fw_done, tx->tstamps[idx].skb, idx); - - /* Check if the timestamp is invalid or stale */ - if (!(raw_tstamp & ICE_PTP_TS_VALID) || - raw_tstamp == tx->tstamps[idx].cached_tstamp) - continue; - - /* The timestamp is valid, so we'll go ahead and clear this - * index and then send the timestamp up to the stack. - */ - spin_lock(&tx->lock); - tx->tstamps[idx].cached_tstamp = raw_tstamp; - clear_bit(idx, tx->in_use); - skb = tx->tstamps[idx].skb; - tx->tstamps[idx].skb = NULL; - spin_unlock(&tx->lock); - - /* it's (unlikely but) possible we raced with the cleanup - * thread for discarding old timestamp requests. - */ - if (!skb) - continue; - - /* Extend the timestamp using cached PHC time */ - tstamp = ice_ptp_extend_40b_ts(pf, raw_tstamp); - shhwtstamps.hwtstamp = ns_to_ktime(tstamp); - - ice_trace(tx_tstamp_complete, skb, idx); - - skb_tstamp_tx(skb, &shhwtstamps); - dev_kfree_skb_any(skb); - } - - /* Check if we still have work to do. If so, re-queue this task to - * poll for remaining timestamps. - */ - spin_lock(&tx->lock); - if (!bitmap_empty(tx->in_use, tx->len)) - kthread_queue_work(pf->ptp.kworker, &tx->work); - spin_unlock(&tx->lock); -} - -/** * ice_ptp_request_ts - Request an available Tx timestamp index * @tx: the PTP Tx timestamp tracker to request from * @skb: the SKB to associate with this timestamp request @@ -2161,177 +2374,17 @@ s8 ice_ptp_request_ts(struct ice_ptp_tx *tx, struct sk_buff *skb) } /** - * ice_ptp_process_ts - Spawn kthread work to handle timestamps + * ice_ptp_process_ts - Process the PTP Tx timestamps * @pf: Board private structure * - * Queue work required to process the PTP Tx timestamps outside of interrupt - * context. + * Returns true if timestamps are processed. */ -void ice_ptp_process_ts(struct ice_pf *pf) +bool ice_ptp_process_ts(struct ice_pf *pf) { if (pf->ptp.port.tx.init) - kthread_queue_work(pf->ptp.kworker, &pf->ptp.port.tx.work); -} - -/** - * ice_ptp_alloc_tx_tracker - Initialize tracking for Tx timestamps - * @tx: Tx tracking structure to initialize - * - * Assumes that the length has already been initialized. Do not call directly, - * use the ice_ptp_init_tx_e822 or ice_ptp_init_tx_e810 instead. - */ -static int -ice_ptp_alloc_tx_tracker(struct ice_ptp_tx *tx) -{ - tx->tstamps = kcalloc(tx->len, sizeof(*tx->tstamps), GFP_KERNEL); - if (!tx->tstamps) - return -ENOMEM; - - tx->in_use = bitmap_zalloc(tx->len, GFP_KERNEL); - if (!tx->in_use) { - kfree(tx->tstamps); - tx->tstamps = NULL; - return -ENOMEM; - } - - spin_lock_init(&tx->lock); - kthread_init_work(&tx->work, ice_ptp_tx_tstamp_work); - - tx->init = 1; - - return 0; -} - -/** - * ice_ptp_flush_tx_tracker - Flush any remaining timestamps from the tracker - * @pf: Board private structure - * @tx: the tracker to flush - */ -static void -ice_ptp_flush_tx_tracker(struct ice_pf *pf, struct ice_ptp_tx *tx) -{ - u8 idx; - - for (idx = 0; idx < tx->len; idx++) { - u8 phy_idx = idx + tx->quad_offset; - - spin_lock(&tx->lock); - if (tx->tstamps[idx].skb) { - dev_kfree_skb_any(tx->tstamps[idx].skb); - tx->tstamps[idx].skb = NULL; - } - clear_bit(idx, tx->in_use); - spin_unlock(&tx->lock); - - /* Clear any potential residual timestamp in the PHY block */ - if (!pf->hw.reset_ongoing) - ice_clear_phy_tstamp(&pf->hw, tx->quad, phy_idx); - } -} - -/** - * ice_ptp_release_tx_tracker - Release allocated memory for Tx tracker - * @pf: Board private structure - * @tx: Tx tracking structure to release - * - * Free memory associated with the Tx timestamp tracker. - */ -static void -ice_ptp_release_tx_tracker(struct ice_pf *pf, struct ice_ptp_tx *tx) -{ - tx->init = 0; - - kthread_cancel_work_sync(&tx->work); - - ice_ptp_flush_tx_tracker(pf, tx); - - kfree(tx->tstamps); - tx->tstamps = NULL; - - bitmap_free(tx->in_use); - tx->in_use = NULL; + return ice_ptp_tx_tstamp(&pf->ptp.port.tx); - tx->len = 0; -} - -/** - * ice_ptp_init_tx_e822 - Initialize tracking for Tx timestamps - * @pf: Board private structure - * @tx: the Tx tracking structure to initialize - * @port: the port this structure tracks - * - * Initialize the Tx timestamp tracker for this port. For generic MAC devices, - * the timestamp block is shared for all ports in the same quad. To avoid - * ports using the same timestamp index, logically break the block of - * registers into chunks based on the port number. - */ -static int -ice_ptp_init_tx_e822(struct ice_pf *pf, struct ice_ptp_tx *tx, u8 port) -{ - tx->quad = port / ICE_PORTS_PER_QUAD; - tx->quad_offset = (port % ICE_PORTS_PER_QUAD) * INDEX_PER_PORT; - tx->len = INDEX_PER_PORT; - - return ice_ptp_alloc_tx_tracker(tx); -} - -/** - * ice_ptp_init_tx_e810 - Initialize tracking for Tx timestamps - * @pf: Board private structure - * @tx: the Tx tracking structure to initialize - * - * Initialize the Tx timestamp tracker for this PF. For E810 devices, each - * port has its own block of timestamps, independent of the other ports. - */ -static int -ice_ptp_init_tx_e810(struct ice_pf *pf, struct ice_ptp_tx *tx) -{ - tx->quad = pf->hw.port_info->lport; - tx->quad_offset = 0; - tx->len = INDEX_PER_QUAD; - - return ice_ptp_alloc_tx_tracker(tx); -} - -/** - * ice_ptp_tx_tstamp_cleanup - Cleanup old timestamp requests that got dropped - * @hw: pointer to the hw struct - * @tx: PTP Tx tracker to clean up - * - * Loop through the Tx timestamp requests and see if any of them have been - * waiting for a long time. Discard any SKBs that have been waiting for more - * than 2 seconds. This is long enough to be reasonably sure that the - * timestamp will never be captured. This might happen if the packet gets - * discarded before it reaches the PHY timestamping block. - */ -static void ice_ptp_tx_tstamp_cleanup(struct ice_hw *hw, struct ice_ptp_tx *tx) -{ - u8 idx; - - if (!tx->init) - return; - - for_each_set_bit(idx, tx->in_use, tx->len) { - struct sk_buff *skb; - u64 raw_tstamp; - - /* Check if this SKB has been waiting for too long */ - if (time_is_after_jiffies(tx->tstamps[idx].start + 2 * HZ)) - continue; - - /* Read tstamp to be able to use this register again */ - ice_read_phy_tstamp(hw, tx->quad, idx + tx->quad_offset, - &raw_tstamp); - - spin_lock(&tx->lock); - skb = tx->tstamps[idx].skb; - tx->tstamps[idx].skb = NULL; - clear_bit(idx, tx->in_use); - spin_unlock(&tx->lock); - - /* Free the SKB after we've cleared the bit */ - dev_kfree_skb_any(skb); - } + return false; } static void ice_ptp_periodic_work(struct kthread_work *work) @@ -2345,7 +2398,7 @@ static void ice_ptp_periodic_work(struct kthread_work *work) err = ice_ptp_update_cached_phctime(pf); - ice_ptp_tx_tstamp_cleanup(&pf->hw, &pf->ptp.port.tx); + ice_ptp_tx_tstamp_cleanup(pf, &pf->ptp.port.tx); /* Run twice a second or reschedule if phc update failed */ kthread_queue_delayed_work(ptp->kworker, &ptp->work, diff --git a/drivers/net/ethernet/intel/ice/ice_ptp.h b/drivers/net/ethernet/intel/ice/ice_ptp.h index 10e396abf130..028349295b71 100644 --- a/drivers/net/ethernet/intel/ice/ice_ptp.h +++ b/drivers/net/ethernet/intel/ice/ice_ptp.h @@ -105,7 +105,6 @@ struct ice_tx_tstamp { /** * struct ice_ptp_tx - Tracking structure for all Tx timestamp requests on a port - * @work: work function to handle processing of Tx timestamps * @lock: lock to prevent concurrent write to in_use bitmap * @tstamps: array of len to store outstanding requests * @in_use: bitmap of len to indicate which slots are in use @@ -117,7 +116,6 @@ struct ice_tx_tstamp { * window, timestamps are temporarily disabled. */ struct ice_ptp_tx { - struct kthread_work work; spinlock_t lock; /* lock protecting in_use bitmap */ struct ice_tx_tstamp *tstamps; unsigned long *in_use; @@ -163,6 +161,7 @@ struct ice_ptp_port { * @work: delayed work function for periodic tasks * @extts_work: work function for handling external Tx timestamps * @cached_phc_time: a cached copy of the PHC time for timestamp extension + * @cached_phc_jiffies: jiffies when cached_phc_time was last updated * @ext_ts_chan: the external timestamp channel in use * @ext_ts_irq: the external timestamp IRQ in use * @kworker: kwork thread for handling periodic work @@ -171,12 +170,19 @@ struct ice_ptp_port { * @clock: pointer to registered PTP clock device * @tstamp_config: hardware timestamping configuration * @reset_time: kernel time after clock stop on reset + * @tx_hwtstamp_skipped: number of Tx time stamp requests skipped + * @tx_hwtstamp_timeouts: number of Tx skbs discarded with no time stamp + * @tx_hwtstamp_flushed: number of Tx skbs flushed due to interface closed + * @tx_hwtstamp_discarded: number of Tx skbs discarded due to cached PHC time + * being too old to correctly extend timestamp + * @late_cached_phc_updates: number of times cached PHC update is late */ struct ice_ptp { struct ice_ptp_port port; struct kthread_delayed_work work; struct kthread_work extts_work; u64 cached_phc_time; + unsigned long cached_phc_jiffies; u8 ext_ts_chan; u8 ext_ts_irq; struct kthread_worker *kworker; @@ -185,6 +191,11 @@ struct ice_ptp { struct ptp_clock *clock; struct hwtstamp_config tstamp_config; u64 reset_time; + u32 tx_hwtstamp_skipped; + u32 tx_hwtstamp_timeouts; + u32 tx_hwtstamp_flushed; + u32 tx_hwtstamp_discarded; + u32 late_cached_phc_updates; }; #define __ptp_port_to_ptp(p) \ @@ -224,8 +235,8 @@ struct ice_ptp { #define N_EXT_TS_E810 3 #define N_PER_OUT_E810 4 #define N_PER_OUT_E810T 3 -#define N_PER_OUT_E810T_NO_SMA 2 -#define N_EXT_TS_E810_NO_SMA 2 +#define N_PER_OUT_NO_SMA_E810T 2 +#define N_EXT_TS_NO_SMA_E810T 2 #define ETH_GLTSYN_ENA(_i) (0x03000348 + ((_i) * 4)) #if IS_ENABLED(CONFIG_PTP_1588_CLOCK) @@ -236,7 +247,7 @@ void ice_ptp_cfg_timestamp(struct ice_pf *pf, bool ena); int ice_get_ptp_clock_index(struct ice_pf *pf); s8 ice_ptp_request_ts(struct ice_ptp_tx *tx, struct sk_buff *skb); -void ice_ptp_process_ts(struct ice_pf *pf); +bool ice_ptp_process_ts(struct ice_pf *pf); void ice_ptp_rx_hwtstamp(struct ice_rx_ring *rx_ring, @@ -269,7 +280,10 @@ ice_ptp_request_ts(struct ice_ptp_tx *tx, struct sk_buff *skb) return -1; } -static inline void ice_ptp_process_ts(struct ice_pf *pf) { } +static inline bool ice_ptp_process_ts(struct ice_pf *pf) +{ + return true; +} static inline void ice_ptp_rx_hwtstamp(struct ice_rx_ring *rx_ring, union ice_32b_rx_flex_desc *rx_desc, struct sk_buff *skb) { } diff --git a/drivers/net/ethernet/intel/ice/ice_ptp_hw.c b/drivers/net/ethernet/intel/ice/ice_ptp_hw.c index 6dff97d53d81..772b1f566d6e 100644 --- a/drivers/net/ethernet/intel/ice/ice_ptp_hw.c +++ b/drivers/net/ethernet/intel/ice/ice_ptp_hw.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 /* Copyright (C) 2021, Intel Corporation. */ +#include <linux/delay.h> #include "ice_common.h" #include "ice_ptp_hw.h" #include "ice_ptp_consts.h" @@ -2587,38 +2588,113 @@ static int ice_write_phy_reg_e810(struct ice_hw *hw, u32 addr, u32 val) } /** - * ice_read_phy_tstamp_e810 - Read a PHY timestamp out of the external PHY + * ice_read_phy_tstamp_ll_e810 - Read a PHY timestamp registers through the FW + * @hw: pointer to the HW struct + * @idx: the timestamp index to read + * @hi: 8 bit timestamp high value + * @lo: 32 bit timestamp low value + * + * Read a 8bit timestamp high value and 32 bit timestamp low value out of the + * timestamp block of the external PHY on the E810 device using the low latency + * timestamp read. + */ +static int +ice_read_phy_tstamp_ll_e810(struct ice_hw *hw, u8 idx, u8 *hi, u32 *lo) +{ + u32 val; + u8 i; + + /* Write TS index to read to the PF register so the FW can read it */ + val = FIELD_PREP(TS_LL_READ_TS_IDX, idx) | TS_LL_READ_TS; + wr32(hw, PF_SB_ATQBAL, val); + + /* Read the register repeatedly until the FW provides us the TS */ + for (i = TS_LL_READ_RETRIES; i > 0; i--) { + val = rd32(hw, PF_SB_ATQBAL); + + /* When the bit is cleared, the TS is ready in the register */ + if (!(FIELD_GET(TS_LL_READ_TS, val))) { + /* High 8 bit value of the TS is on the bits 16:23 */ + *hi = FIELD_GET(TS_LL_READ_TS_HIGH, val); + + /* Read the low 32 bit value and set the TS valid bit */ + *lo = rd32(hw, PF_SB_ATQBAH) | TS_VALID; + return 0; + } + + udelay(10); + } + + /* FW failed to provide the TS in time */ + ice_debug(hw, ICE_DBG_PTP, "Failed to read PTP timestamp using low latency read\n"); + return -EINVAL; +} + +/** + * ice_read_phy_tstamp_sbq_e810 - Read a PHY timestamp registers through the sbq * @hw: pointer to the HW struct * @lport: the lport to read from * @idx: the timestamp index to read - * @tstamp: on return, the 40bit timestamp value + * @hi: 8 bit timestamp high value + * @lo: 32 bit timestamp low value * - * Read a 40bit timestamp value out of the timestamp block of the external PHY - * on the E810 device. + * Read a 8bit timestamp high value and 32 bit timestamp low value out of the + * timestamp block of the external PHY on the E810 device using sideband queue. */ static int -ice_read_phy_tstamp_e810(struct ice_hw *hw, u8 lport, u8 idx, u64 *tstamp) +ice_read_phy_tstamp_sbq_e810(struct ice_hw *hw, u8 lport, u8 idx, u8 *hi, + u32 *lo) { - u32 lo_addr, hi_addr, lo, hi; + u32 hi_addr = TS_EXT(HIGH_TX_MEMORY_BANK_START, lport, idx); + u32 lo_addr = TS_EXT(LOW_TX_MEMORY_BANK_START, lport, idx); + u32 lo_val, hi_val; int err; - lo_addr = TS_EXT(LOW_TX_MEMORY_BANK_START, lport, idx); - hi_addr = TS_EXT(HIGH_TX_MEMORY_BANK_START, lport, idx); - - err = ice_read_phy_reg_e810(hw, lo_addr, &lo); + err = ice_read_phy_reg_e810(hw, lo_addr, &lo_val); if (err) { ice_debug(hw, ICE_DBG_PTP, "Failed to read low PTP timestamp register, err %d\n", err); return err; } - err = ice_read_phy_reg_e810(hw, hi_addr, &hi); + err = ice_read_phy_reg_e810(hw, hi_addr, &hi_val); if (err) { ice_debug(hw, ICE_DBG_PTP, "Failed to read high PTP timestamp register, err %d\n", err); return err; } + *lo = lo_val; + *hi = (u8)hi_val; + + return 0; +} + +/** + * ice_read_phy_tstamp_e810 - Read a PHY timestamp out of the external PHY + * @hw: pointer to the HW struct + * @lport: the lport to read from + * @idx: the timestamp index to read + * @tstamp: on return, the 40bit timestamp value + * + * Read a 40bit timestamp value out of the timestamp block of the external PHY + * on the E810 device. + */ +static int +ice_read_phy_tstamp_e810(struct ice_hw *hw, u8 lport, u8 idx, u64 *tstamp) +{ + u32 lo = 0; + u8 hi = 0; + int err; + + if (hw->dev_caps.ts_dev_info.ts_ll_read) + err = ice_read_phy_tstamp_ll_e810(hw, idx, &hi, &lo); + else + err = ice_read_phy_tstamp_sbq_e810(hw, lport, idx, &hi, &lo); + + if (err) + return err; + /* For E810 devices, the timestamp is reported with the lower 32 bits * in the low register, and the upper 8 bits in the high register. */ diff --git a/drivers/net/ethernet/intel/ice/ice_ptp_hw.h b/drivers/net/ethernet/intel/ice/ice_ptp_hw.h index 1246e4ee4b5d..2bda64c76abc 100644 --- a/drivers/net/ethernet/intel/ice/ice_ptp_hw.h +++ b/drivers/net/ethernet/intel/ice/ice_ptp_hw.h @@ -402,6 +402,7 @@ bool ice_is_pca9575_present(struct ice_hw *hw); #define INCVAL_HIGH_M 0xFF /* Timestamp block macros */ +#define TS_VALID BIT(0) #define TS_LOW_M 0xFFFFFFFF #define TS_HIGH_M 0xFF #define TS_HIGH_S 32 @@ -413,6 +414,12 @@ bool ice_is_pca9575_present(struct ice_hw *hw); #define BYTES_PER_IDX_ADDR_L_U 8 #define BYTES_PER_IDX_ADDR_L 4 +/* Tx timestamp low latency read definitions */ +#define TS_LL_READ_RETRIES 200 +#define TS_LL_READ_TS_HIGH GENMASK(23, 16) +#define TS_LL_READ_TS_IDX GENMASK(29, 24) +#define TS_LL_READ_TS BIT(31) + /* Internal PHY timestamp address */ #define TS_L(a, idx) ((a) + ((idx) * BYTES_PER_IDX_ADDR_L_U)) #define TS_H(a, idx) ((a) + ((idx) * BYTES_PER_IDX_ADDR_L_U + \ diff --git a/drivers/net/ethernet/intel/ice/ice_repr.c b/drivers/net/ethernet/intel/ice/ice_repr.c index 0dac67cd9c77..bd31748aae1b 100644 --- a/drivers/net/ethernet/intel/ice/ice_repr.c +++ b/drivers/net/ethernet/intel/ice/ice_repr.c @@ -377,10 +377,10 @@ static void ice_repr_rem(struct ice_vf *vf) if (!vf->repr) return; - ice_devlink_destroy_vf_port(vf); kfree(vf->repr->q_vector); vf->repr->q_vector = NULL; unregister_netdev(vf->repr->netdev); + ice_devlink_destroy_vf_port(vf); free_netdev(vf->repr->netdev); vf->repr->netdev = NULL; #ifdef CONFIG_ICE_SWITCHDEV diff --git a/drivers/net/ethernet/intel/ice/ice_sched.c b/drivers/net/ethernet/intel/ice/ice_sched.c index 7947223536e3..118595763bba 100644 --- a/drivers/net/ethernet/intel/ice/ice_sched.c +++ b/drivers/net/ethernet/intel/ice/ice_sched.c @@ -1212,7 +1212,7 @@ int ice_sched_init_port(struct ice_port_info *pi) hw = pi->hw; /* Query the Default Topology from FW */ - buf = devm_kzalloc(ice_hw_to_dev(hw), ICE_AQ_MAX_BUF_LEN, GFP_KERNEL); + buf = kzalloc(ICE_AQ_MAX_BUF_LEN, GFP_KERNEL); if (!buf) return -ENOMEM; @@ -1290,7 +1290,7 @@ err_init_port: pi->root = NULL; } - devm_kfree(ice_hw_to_dev(hw), buf); + kfree(buf); return status; } diff --git a/drivers/net/ethernet/intel/ice/ice_switch.c b/drivers/net/ethernet/intel/ice/ice_switch.c index 3808034f7e7e..9b762f7972ce 100644 --- a/drivers/net/ethernet/intel/ice/ice_switch.c +++ b/drivers/net/ethernet/intel/ice/ice_switch.c @@ -42,6 +42,7 @@ enum { ICE_PKT_GTP_NOPAY = BIT(8), ICE_PKT_KMALLOC = BIT(9), ICE_PKT_PPPOE = BIT(10), + ICE_PKT_L2TPV3 = BIT(11), }; struct ice_dummy_pkt_offsets { @@ -1258,6 +1259,65 @@ ICE_DECLARE_PKT_TEMPLATE(pppoe_ipv6_udp) = { 0x00, 0x00, /* 2 bytes for 4 bytes alignment */ }; +ICE_DECLARE_PKT_OFFSETS(ipv4_l2tpv3) = { + { ICE_MAC_OFOS, 0 }, + { ICE_ETYPE_OL, 12 }, + { ICE_IPV4_OFOS, 14 }, + { ICE_L2TPV3, 34 }, + { ICE_PROTOCOL_LAST, 0 }, +}; + +ICE_DECLARE_PKT_TEMPLATE(ipv4_l2tpv3) = { + 0x00, 0x00, 0x00, 0x00, /* ICE_MAC_OFOS 0 */ + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + 0x08, 0x00, /* ICE_ETYPE_OL 12 */ + + 0x45, 0x00, 0x00, 0x20, /* ICE_IPV4_IL 14 */ + 0x00, 0x00, 0x40, 0x00, + 0x40, 0x73, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + 0x00, 0x00, 0x00, 0x00, /* ICE_L2TPV3 34 */ + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, /* 2 bytes for 4 bytes alignment */ +}; + +ICE_DECLARE_PKT_OFFSETS(ipv6_l2tpv3) = { + { ICE_MAC_OFOS, 0 }, + { ICE_ETYPE_OL, 12 }, + { ICE_IPV6_OFOS, 14 }, + { ICE_L2TPV3, 54 }, + { ICE_PROTOCOL_LAST, 0 }, +}; + +ICE_DECLARE_PKT_TEMPLATE(ipv6_l2tpv3) = { + 0x00, 0x00, 0x00, 0x00, /* ICE_MAC_OFOS 0 */ + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + 0x86, 0xDD, /* ICE_ETYPE_OL 12 */ + + 0x60, 0x00, 0x00, 0x00, /* ICE_IPV6_IL 14 */ + 0x00, 0x0c, 0x73, 0x40, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + 0x00, 0x00, 0x00, 0x00, /* ICE_L2TPV3 54 */ + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, /* 2 bytes for 4 bytes alignment */ +}; + static const struct ice_dummy_pkt_profile ice_dummy_pkt_profiles[] = { ICE_PKT_PROFILE(ipv6_gtp, ICE_PKT_TUN_GTPU | ICE_PKT_OUTER_IPV6 | ICE_PKT_GTP_NOPAY), @@ -1297,6 +1357,8 @@ static const struct ice_dummy_pkt_profile ice_dummy_pkt_profiles[] = { ICE_PKT_PROFILE(udp_tun_ipv6_tcp, ICE_PKT_TUN_UDP | ICE_PKT_INNER_IPV6 | ICE_PKT_INNER_TCP), + ICE_PKT_PROFILE(ipv6_l2tpv3, ICE_PKT_L2TPV3 | ICE_PKT_OUTER_IPV6), + ICE_PKT_PROFILE(ipv4_l2tpv3, ICE_PKT_L2TPV3), ICE_PKT_PROFILE(udp_tun_tcp, ICE_PKT_TUN_UDP | ICE_PKT_INNER_TCP), ICE_PKT_PROFILE(udp_tun_ipv6_udp, ICE_PKT_TUN_UDP | ICE_PKT_INNER_IPV6), @@ -2274,9 +2336,7 @@ int ice_get_initial_sw_cfg(struct ice_hw *hw) int status; u16 i; - rbuf = devm_kzalloc(ice_hw_to_dev(hw), ICE_SW_CFG_MAX_BUF_LEN, - GFP_KERNEL); - + rbuf = kzalloc(ICE_SW_CFG_MAX_BUF_LEN, GFP_KERNEL); if (!rbuf) return -ENOMEM; @@ -2324,7 +2384,7 @@ int ice_get_initial_sw_cfg(struct ice_hw *hw) } } while (req_desc && !status); - devm_kfree(ice_hw_to_dev(hw), rbuf); + kfree(rbuf); return status; } @@ -3449,31 +3509,15 @@ bool ice_vlan_fltr_exist(struct ice_hw *hw, u16 vlan_id, u16 vsi_handle) * ice_add_mac - Add a MAC address based filter rule * @hw: pointer to the hardware structure * @m_list: list of MAC addresses and forwarding information - * - * IMPORTANT: When the ucast_shared flag is set to false and m_list has - * multiple unicast addresses, the function assumes that all the - * addresses are unique in a given add_mac call. It doesn't - * check for duplicates in this case, removing duplicates from a given - * list should be taken care of in the caller of this function. */ int ice_add_mac(struct ice_hw *hw, struct list_head *m_list) { - struct ice_sw_rule_lkup_rx_tx *s_rule, *r_iter; struct ice_fltr_list_entry *m_list_itr; - struct list_head *rule_head; - u16 total_elem_left, s_rule_size; - struct ice_switch_info *sw; - struct mutex *rule_lock; /* Lock to protect filter rule list */ - u16 num_unicast = 0; int status = 0; - u8 elem_sent; if (!m_list || !hw) return -EINVAL; - s_rule = NULL; - sw = hw->switch_info; - rule_lock = &sw->recp_list[ICE_SW_LKUP_MAC].filt_rule_lock; list_for_each_entry(m_list_itr, m_list, list_entry) { u8 *add = &m_list_itr->fltr_info.l_data.mac.mac_addr[0]; u16 vsi_handle; @@ -3492,106 +3536,13 @@ int ice_add_mac(struct ice_hw *hw, struct list_head *m_list) if (m_list_itr->fltr_info.lkup_type != ICE_SW_LKUP_MAC || is_zero_ether_addr(add)) return -EINVAL; - if (is_unicast_ether_addr(add) && !hw->ucast_shared) { - /* Don't overwrite the unicast address */ - mutex_lock(rule_lock); - if (ice_find_rule_entry(hw, ICE_SW_LKUP_MAC, - &m_list_itr->fltr_info)) { - mutex_unlock(rule_lock); - return -EEXIST; - } - mutex_unlock(rule_lock); - num_unicast++; - } else if (is_multicast_ether_addr(add) || - (is_unicast_ether_addr(add) && hw->ucast_shared)) { - m_list_itr->status = - ice_add_rule_internal(hw, ICE_SW_LKUP_MAC, - m_list_itr); - if (m_list_itr->status) - return m_list_itr->status; - } - } - mutex_lock(rule_lock); - /* Exit if no suitable entries were found for adding bulk switch rule */ - if (!num_unicast) { - status = 0; - goto ice_add_mac_exit; - } - - rule_head = &sw->recp_list[ICE_SW_LKUP_MAC].filt_rules; - - /* Allocate switch rule buffer for the bulk update for unicast */ - s_rule_size = ICE_SW_RULE_RX_TX_ETH_HDR_SIZE(s_rule); - s_rule = devm_kcalloc(ice_hw_to_dev(hw), num_unicast, s_rule_size, - GFP_KERNEL); - if (!s_rule) { - status = -ENOMEM; - goto ice_add_mac_exit; - } - - r_iter = s_rule; - list_for_each_entry(m_list_itr, m_list, list_entry) { - struct ice_fltr_info *f_info = &m_list_itr->fltr_info; - u8 *mac_addr = &f_info->l_data.mac.mac_addr[0]; - - if (is_unicast_ether_addr(mac_addr)) { - ice_fill_sw_rule(hw, &m_list_itr->fltr_info, r_iter, - ice_aqc_opc_add_sw_rules); - r_iter = (typeof(s_rule))((u8 *)r_iter + s_rule_size); - } - } - - /* Call AQ bulk switch rule update for all unicast addresses */ - r_iter = s_rule; - /* Call AQ switch rule in AQ_MAX chunk */ - for (total_elem_left = num_unicast; total_elem_left > 0; - total_elem_left -= elem_sent) { - struct ice_sw_rule_lkup_rx_tx *entry = r_iter; - - elem_sent = min_t(u8, total_elem_left, - (ICE_AQ_MAX_BUF_LEN / s_rule_size)); - status = ice_aq_sw_rules(hw, entry, elem_sent * s_rule_size, - elem_sent, ice_aqc_opc_add_sw_rules, - NULL); - if (status) - goto ice_add_mac_exit; - r_iter = (typeof(s_rule)) - ((u8 *)r_iter + (elem_sent * s_rule_size)); + m_list_itr->status = ice_add_rule_internal(hw, ICE_SW_LKUP_MAC, + m_list_itr); + if (m_list_itr->status) + return m_list_itr->status; } - /* Fill up rule ID based on the value returned from FW */ - r_iter = s_rule; - list_for_each_entry(m_list_itr, m_list, list_entry) { - struct ice_fltr_info *f_info = &m_list_itr->fltr_info; - u8 *mac_addr = &f_info->l_data.mac.mac_addr[0]; - struct ice_fltr_mgmt_list_entry *fm_entry; - - if (is_unicast_ether_addr(mac_addr)) { - f_info->fltr_rule_id = le16_to_cpu(r_iter->index); - f_info->fltr_act = ICE_FWD_TO_VSI; - /* Create an entry to track this MAC address */ - fm_entry = devm_kzalloc(ice_hw_to_dev(hw), - sizeof(*fm_entry), GFP_KERNEL); - if (!fm_entry) { - status = -ENOMEM; - goto ice_add_mac_exit; - } - fm_entry->fltr_info = *f_info; - fm_entry->vsi_count = 1; - /* The book keeping entries will get removed when - * base driver calls remove filter AQ command - */ - - list_add(&fm_entry->list_entry, rule_head); - r_iter = (typeof(s_rule))((u8 *)r_iter + s_rule_size); - } - } - -ice_add_mac_exit: - mutex_unlock(rule_lock); - if (s_rule) - devm_kfree(ice_hw_to_dev(hw), s_rule); return status; } @@ -3979,38 +3930,6 @@ ice_check_if_dflt_vsi(struct ice_port_info *pi, u16 vsi_handle, } /** - * ice_find_ucast_rule_entry - Search for a unicast MAC filter rule entry - * @hw: pointer to the hardware structure - * @recp_id: lookup type for which the specified rule needs to be searched - * @f_info: rule information - * - * Helper function to search for a unicast rule entry - this is to be used - * to remove unicast MAC filter that is not shared with other VSIs on the - * PF switch. - * - * Returns pointer to entry storing the rule if found - */ -static struct ice_fltr_mgmt_list_entry * -ice_find_ucast_rule_entry(struct ice_hw *hw, u8 recp_id, - struct ice_fltr_info *f_info) -{ - struct ice_switch_info *sw = hw->switch_info; - struct ice_fltr_mgmt_list_entry *list_itr; - struct list_head *list_head; - - list_head = &sw->recp_list[recp_id].filt_rules; - list_for_each_entry(list_itr, list_head, list_entry) { - if (!memcmp(&f_info->l_data, &list_itr->fltr_info.l_data, - sizeof(f_info->l_data)) && - f_info->fwd_id.hw_vsi_id == - list_itr->fltr_info.fwd_id.hw_vsi_id && - f_info->flag == list_itr->fltr_info.flag) - return list_itr; - } - return NULL; -} - -/** * ice_remove_mac - remove a MAC address based filter rule * @hw: pointer to the hardware structure * @m_list: list of MAC addresses and forwarding information @@ -4026,15 +3945,12 @@ ice_find_ucast_rule_entry(struct ice_hw *hw, u8 recp_id, int ice_remove_mac(struct ice_hw *hw, struct list_head *m_list) { struct ice_fltr_list_entry *list_itr, *tmp; - struct mutex *rule_lock; /* Lock to protect filter rule list */ if (!m_list) return -EINVAL; - rule_lock = &hw->switch_info->recp_list[ICE_SW_LKUP_MAC].filt_rule_lock; list_for_each_entry_safe(list_itr, tmp, m_list, list_entry) { enum ice_sw_lkup_type l_type = list_itr->fltr_info.lkup_type; - u8 *add = &list_itr->fltr_info.l_data.mac.mac_addr[0]; u16 vsi_handle; if (l_type != ICE_SW_LKUP_MAC) @@ -4046,19 +3962,7 @@ int ice_remove_mac(struct ice_hw *hw, struct list_head *m_list) list_itr->fltr_info.fwd_id.hw_vsi_id = ice_get_hw_vsi_num(hw, vsi_handle); - if (is_unicast_ether_addr(add) && !hw->ucast_shared) { - /* Don't remove the unicast address that belongs to - * another VSI on the switch, since it is not being - * shared... - */ - mutex_lock(rule_lock); - if (!ice_find_ucast_rule_entry(hw, ICE_SW_LKUP_MAC, - &list_itr->fltr_info)) { - mutex_unlock(rule_lock); - return -ENOENT; - } - mutex_unlock(rule_lock); - } + list_itr->status = ice_remove_rule_internal(hw, ICE_SW_LKUP_MAC, list_itr); @@ -4648,6 +4552,7 @@ static const struct ice_prot_ext_tbl_entry ice_prot_ext[ICE_PROTOCOL_LAST] = { { ICE_GTP, { 8, 10, 12, 14, 16, 18, 20, 22 } }, { ICE_GTP_NO_PAY, { 8, 10, 12, 14 } }, { ICE_PPPOE, { 0, 2, 4, 6 } }, + { ICE_L2TPV3, { 0, 2, 4, 6, 8, 10 } }, { ICE_VLAN_EX, { 2, 0 } }, { ICE_VLAN_IN, { 2, 0 } }, }; @@ -4671,6 +4576,7 @@ static struct ice_protocol_entry ice_prot_id_tbl[ICE_PROTOCOL_LAST] = { { ICE_GTP, ICE_UDP_OF_HW }, { ICE_GTP_NO_PAY, ICE_UDP_ILOS_HW }, { ICE_PPPOE, ICE_PPPOE_HW }, + { ICE_L2TPV3, ICE_L2TPV3_HW }, { ICE_VLAN_EX, ICE_VLAN_OF_HW }, { ICE_VLAN_IN, ICE_VLAN_OL_HW }, }; @@ -5754,7 +5660,8 @@ ice_find_dummy_packet(struct ice_adv_lkup_elem *lkups, u16 lkups_cnt, if (lkups[i].h_u.pppoe_hdr.ppp_prot_id == htons(PPP_IPV6)) match |= ICE_PKT_OUTER_IPV6; - } + } else if (lkups[i].type == ICE_L2TPV3) + match |= ICE_PKT_L2TPV3; } while (ret->match && (match & ret->match) != ret->match) @@ -5855,6 +5762,9 @@ ice_fill_adv_dummy_packet(struct ice_adv_lkup_elem *lkups, u16 lkups_cnt, case ICE_PPPOE: len = sizeof(struct ice_pppoe_hdr); break; + case ICE_L2TPV3: + len = sizeof(struct ice_l2tpv3_sess_hdr); + break; default: return -EINVAL; } diff --git a/drivers/net/ethernet/intel/ice/ice_tc_lib.c b/drivers/net/ethernet/intel/ice/ice_tc_lib.c index a298862857a8..f68c555be4e9 100644 --- a/drivers/net/ethernet/intel/ice/ice_tc_lib.c +++ b/drivers/net/ethernet/intel/ice/ice_tc_lib.c @@ -36,6 +36,10 @@ ice_tc_count_lkups(u32 flags, struct ice_tc_flower_lyr_2_4_hdrs *headers, ICE_TC_FLWR_FIELD_ENC_DEST_IPV6)) lkups_cnt++; + if (flags & (ICE_TC_FLWR_FIELD_ENC_IP_TOS | + ICE_TC_FLWR_FIELD_ENC_IP_TTL)) + lkups_cnt++; + if (flags & ICE_TC_FLWR_FIELD_ENC_DEST_L4_PORT) lkups_cnt++; @@ -47,11 +51,11 @@ ice_tc_count_lkups(u32 flags, struct ice_tc_flower_lyr_2_4_hdrs *headers, lkups_cnt++; /* is VLAN specified? */ - if (flags & ICE_TC_FLWR_FIELD_VLAN) + if (flags & (ICE_TC_FLWR_FIELD_VLAN | ICE_TC_FLWR_FIELD_VLAN_PRIO)) lkups_cnt++; /* is CVLAN specified? */ - if (flags & ICE_TC_FLWR_FIELD_CVLAN) + if (flags & (ICE_TC_FLWR_FIELD_CVLAN | ICE_TC_FLWR_FIELD_CVLAN_PRIO)) lkups_cnt++; /* are PPPoE options specified? */ @@ -64,6 +68,13 @@ ice_tc_count_lkups(u32 flags, struct ice_tc_flower_lyr_2_4_hdrs *headers, ICE_TC_FLWR_FIELD_DEST_IPV6 | ICE_TC_FLWR_FIELD_SRC_IPV6)) lkups_cnt++; + if (flags & (ICE_TC_FLWR_FIELD_IP_TOS | ICE_TC_FLWR_FIELD_IP_TTL)) + lkups_cnt++; + + /* are L2TPv3 options specified? */ + if (flags & ICE_TC_FLWR_FIELD_L2TPV3_SESSID) + lkups_cnt++; + /* is L4 (TCP/UDP/any other L4 protocol fields) specified? */ if (flags & (ICE_TC_FLWR_FIELD_DEST_L4_PORT | ICE_TC_FLWR_FIELD_SRC_L4_PORT)) @@ -257,6 +268,50 @@ ice_tc_fill_tunnel_outer(u32 flags, struct ice_tc_flower_fltr *fltr, i++; } + if (fltr->inner_headers.l2_key.n_proto == htons(ETH_P_IP) && + (flags & (ICE_TC_FLWR_FIELD_ENC_IP_TOS | + ICE_TC_FLWR_FIELD_ENC_IP_TTL))) { + list[i].type = ice_proto_type_from_ipv4(false); + + if (flags & ICE_TC_FLWR_FIELD_ENC_IP_TOS) { + list[i].h_u.ipv4_hdr.tos = hdr->l3_key.tos; + list[i].m_u.ipv4_hdr.tos = hdr->l3_mask.tos; + } + + if (flags & ICE_TC_FLWR_FIELD_ENC_IP_TTL) { + list[i].h_u.ipv4_hdr.time_to_live = hdr->l3_key.ttl; + list[i].m_u.ipv4_hdr.time_to_live = hdr->l3_mask.ttl; + } + + i++; + } + + if (fltr->inner_headers.l2_key.n_proto == htons(ETH_P_IPV6) && + (flags & (ICE_TC_FLWR_FIELD_ENC_IP_TOS | + ICE_TC_FLWR_FIELD_ENC_IP_TTL))) { + struct ice_ipv6_hdr *hdr_h, *hdr_m; + + hdr_h = &list[i].h_u.ipv6_hdr; + hdr_m = &list[i].m_u.ipv6_hdr; + list[i].type = ice_proto_type_from_ipv6(false); + + if (flags & ICE_TC_FLWR_FIELD_ENC_IP_TOS) { + be32p_replace_bits(&hdr_h->be_ver_tc_flow, + hdr->l3_key.tos, + ICE_IPV6_HDR_TC_MASK); + be32p_replace_bits(&hdr_m->be_ver_tc_flow, + hdr->l3_mask.tos, + ICE_IPV6_HDR_TC_MASK); + } + + if (flags & ICE_TC_FLWR_FIELD_ENC_IP_TTL) { + hdr_h->hop_limit = hdr->l3_key.ttl; + hdr_m->hop_limit = hdr->l3_mask.ttl; + } + + i++; + } + if ((flags & ICE_TC_FLWR_FIELD_ENC_DEST_L4_PORT) && hdr->l3_key.ip_proto == IPPROTO_UDP) { list[i].type = ICE_UDP_OF; @@ -334,7 +389,7 @@ ice_tc_fill_rules(struct ice_hw *hw, u32 flags, } /* copy VLAN info */ - if (flags & ICE_TC_FLWR_FIELD_VLAN) { + if (flags & (ICE_TC_FLWR_FIELD_VLAN | ICE_TC_FLWR_FIELD_VLAN_PRIO)) { vlan_tpid = be16_to_cpu(headers->vlan_hdr.vlan_tpid); rule_info->vlan_type = ice_check_supported_vlan_tpid(vlan_tpid); @@ -343,15 +398,45 @@ ice_tc_fill_rules(struct ice_hw *hw, u32 flags, list[i].type = ICE_VLAN_EX; else list[i].type = ICE_VLAN_OFOS; - list[i].h_u.vlan_hdr.vlan = headers->vlan_hdr.vlan_id; - list[i].m_u.vlan_hdr.vlan = cpu_to_be16(0xFFFF); + + if (flags & ICE_TC_FLWR_FIELD_VLAN) { + list[i].h_u.vlan_hdr.vlan = headers->vlan_hdr.vlan_id; + list[i].m_u.vlan_hdr.vlan = cpu_to_be16(0x0FFF); + } + + if (flags & ICE_TC_FLWR_FIELD_VLAN_PRIO) { + if (flags & ICE_TC_FLWR_FIELD_VLAN) { + list[i].m_u.vlan_hdr.vlan = cpu_to_be16(0xEFFF); + } else { + list[i].m_u.vlan_hdr.vlan = cpu_to_be16(0xE000); + list[i].h_u.vlan_hdr.vlan = 0; + } + list[i].h_u.vlan_hdr.vlan |= + headers->vlan_hdr.vlan_prio; + } + i++; } - if (flags & ICE_TC_FLWR_FIELD_CVLAN) { + if (flags & (ICE_TC_FLWR_FIELD_CVLAN | ICE_TC_FLWR_FIELD_CVLAN_PRIO)) { list[i].type = ICE_VLAN_IN; - list[i].h_u.vlan_hdr.vlan = headers->cvlan_hdr.vlan_id; - list[i].m_u.vlan_hdr.vlan = cpu_to_be16(0xFFFF); + + if (flags & ICE_TC_FLWR_FIELD_CVLAN) { + list[i].h_u.vlan_hdr.vlan = headers->cvlan_hdr.vlan_id; + list[i].m_u.vlan_hdr.vlan = cpu_to_be16(0x0FFF); + } + + if (flags & ICE_TC_FLWR_FIELD_CVLAN_PRIO) { + if (flags & ICE_TC_FLWR_FIELD_CVLAN) { + list[i].m_u.vlan_hdr.vlan = cpu_to_be16(0xEFFF); + } else { + list[i].m_u.vlan_hdr.vlan = cpu_to_be16(0xE000); + list[i].h_u.vlan_hdr.vlan = 0; + } + list[i].h_u.vlan_hdr.vlan |= + headers->cvlan_hdr.vlan_prio; + } + i++; } @@ -420,6 +505,61 @@ ice_tc_fill_rules(struct ice_hw *hw, u32 flags, i++; } + if (headers->l2_key.n_proto == htons(ETH_P_IP) && + (flags & (ICE_TC_FLWR_FIELD_IP_TOS | ICE_TC_FLWR_FIELD_IP_TTL))) { + list[i].type = ice_proto_type_from_ipv4(inner); + + if (flags & ICE_TC_FLWR_FIELD_IP_TOS) { + list[i].h_u.ipv4_hdr.tos = headers->l3_key.tos; + list[i].m_u.ipv4_hdr.tos = headers->l3_mask.tos; + } + + if (flags & ICE_TC_FLWR_FIELD_IP_TTL) { + list[i].h_u.ipv4_hdr.time_to_live = + headers->l3_key.ttl; + list[i].m_u.ipv4_hdr.time_to_live = + headers->l3_mask.ttl; + } + + i++; + } + + if (headers->l2_key.n_proto == htons(ETH_P_IPV6) && + (flags & (ICE_TC_FLWR_FIELD_IP_TOS | ICE_TC_FLWR_FIELD_IP_TTL))) { + struct ice_ipv6_hdr *hdr_h, *hdr_m; + + hdr_h = &list[i].h_u.ipv6_hdr; + hdr_m = &list[i].m_u.ipv6_hdr; + list[i].type = ice_proto_type_from_ipv6(inner); + + if (flags & ICE_TC_FLWR_FIELD_IP_TOS) { + be32p_replace_bits(&hdr_h->be_ver_tc_flow, + headers->l3_key.tos, + ICE_IPV6_HDR_TC_MASK); + be32p_replace_bits(&hdr_m->be_ver_tc_flow, + headers->l3_mask.tos, + ICE_IPV6_HDR_TC_MASK); + } + + if (flags & ICE_TC_FLWR_FIELD_IP_TTL) { + hdr_h->hop_limit = headers->l3_key.ttl; + hdr_m->hop_limit = headers->l3_mask.ttl; + } + + i++; + } + + if (flags & ICE_TC_FLWR_FIELD_L2TPV3_SESSID) { + list[i].type = ICE_L2TPV3; + + list[i].h_u.l2tpv3_sess_hdr.session_id = + headers->l2tpv3_hdr.session_id; + list[i].m_u.l2tpv3_sess_hdr.session_id = + cpu_to_be32(0xFFFFFFFF); + + i++; + } + /* copy L4 (src, dest) port */ if (flags & (ICE_TC_FLWR_FIELD_DEST_L4_PORT | ICE_TC_FLWR_FIELD_SRC_L4_PORT)) { @@ -839,6 +979,40 @@ ice_tc_set_ipv6(struct flow_match_ipv6_addrs *match, } /** + * ice_tc_set_tos_ttl - Parse IP ToS/TTL from TC flower filter + * @match: Pointer to flow match structure + * @fltr: Pointer to filter structure + * @headers: inner or outer header fields + * @is_encap: set true for tunnel + */ +static void +ice_tc_set_tos_ttl(struct flow_match_ip *match, + struct ice_tc_flower_fltr *fltr, + struct ice_tc_flower_lyr_2_4_hdrs *headers, + bool is_encap) +{ + if (match->mask->tos) { + if (is_encap) + fltr->flags |= ICE_TC_FLWR_FIELD_ENC_IP_TOS; + else + fltr->flags |= ICE_TC_FLWR_FIELD_IP_TOS; + + headers->l3_key.tos = match->key->tos; + headers->l3_mask.tos = match->mask->tos; + } + + if (match->mask->ttl) { + if (is_encap) + fltr->flags |= ICE_TC_FLWR_FIELD_ENC_IP_TTL; + else + fltr->flags |= ICE_TC_FLWR_FIELD_IP_TTL; + + headers->l3_key.ttl = match->key->ttl; + headers->l3_mask.ttl = match->mask->ttl; + } +} + +/** * ice_tc_set_port - Parse ports from TC flower filter * @match: Flow match structure * @fltr: Pointer to filter structure @@ -967,10 +1141,7 @@ ice_parse_tunnel_attr(struct net_device *dev, struct flow_rule *rule, struct flow_match_ip match; flow_rule_match_enc_ip(rule, &match); - headers->l3_key.tos = match.key->tos; - headers->l3_key.ttl = match.key->ttl; - headers->l3_mask.tos = match.mask->tos; - headers->l3_mask.ttl = match.mask->ttl; + ice_tc_set_tos_ttl(&match, fltr, headers, true); } if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ENC_PORTS) && @@ -1039,9 +1210,11 @@ ice_parse_cls_flower(struct net_device *filter_dev, struct ice_vsi *vsi, BIT(FLOW_DISSECTOR_KEY_ENC_IPV6_ADDRS) | BIT(FLOW_DISSECTOR_KEY_ENC_PORTS) | BIT(FLOW_DISSECTOR_KEY_ENC_OPTS) | + BIT(FLOW_DISSECTOR_KEY_IP) | BIT(FLOW_DISSECTOR_KEY_ENC_IP) | BIT(FLOW_DISSECTOR_KEY_PORTS) | - BIT(FLOW_DISSECTOR_KEY_PPPOE))) { + BIT(FLOW_DISSECTOR_KEY_PPPOE) | + BIT(FLOW_DISSECTOR_KEY_L2TPV3))) { NL_SET_ERR_MSG_MOD(fltr->extack, "Unsupported key used"); return -EOPNOTSUPP; } @@ -1137,16 +1310,22 @@ ice_parse_cls_flower(struct net_device *filter_dev, struct ice_vsi *vsi, if (match.mask->vlan_id) { if (match.mask->vlan_id == VLAN_VID_MASK) { fltr->flags |= ICE_TC_FLWR_FIELD_VLAN; + headers->vlan_hdr.vlan_id = + cpu_to_be16(match.key->vlan_id & + VLAN_VID_MASK); } else { NL_SET_ERR_MSG_MOD(fltr->extack, "Bad VLAN mask"); return -EINVAL; } } - headers->vlan_hdr.vlan_id = - cpu_to_be16(match.key->vlan_id & VLAN_VID_MASK); - if (match.mask->vlan_priority) - headers->vlan_hdr.vlan_prio = match.key->vlan_priority; + if (match.mask->vlan_priority) { + fltr->flags |= ICE_TC_FLWR_FIELD_VLAN_PRIO; + headers->vlan_hdr.vlan_prio = + cpu_to_be16((match.key->vlan_priority << + VLAN_PRIO_SHIFT) & VLAN_PRIO_MASK); + } + if (match.mask->vlan_tpid) headers->vlan_hdr.vlan_tpid = match.key->vlan_tpid; } @@ -1164,6 +1343,9 @@ ice_parse_cls_flower(struct net_device *filter_dev, struct ice_vsi *vsi, if (match.mask->vlan_id) { if (match.mask->vlan_id == VLAN_VID_MASK) { fltr->flags |= ICE_TC_FLWR_FIELD_CVLAN; + headers->cvlan_hdr.vlan_id = + cpu_to_be16(match.key->vlan_id & + VLAN_VID_MASK); } else { NL_SET_ERR_MSG_MOD(fltr->extack, "Bad CVLAN mask"); @@ -1171,10 +1353,12 @@ ice_parse_cls_flower(struct net_device *filter_dev, struct ice_vsi *vsi, } } - headers->cvlan_hdr.vlan_id = - cpu_to_be16(match.key->vlan_id & VLAN_VID_MASK); - if (match.mask->vlan_priority) - headers->cvlan_hdr.vlan_prio = match.key->vlan_priority; + if (match.mask->vlan_priority) { + fltr->flags |= ICE_TC_FLWR_FIELD_CVLAN_PRIO; + headers->cvlan_hdr.vlan_prio = + cpu_to_be16((match.key->vlan_priority << + VLAN_PRIO_SHIFT) & VLAN_PRIO_MASK); + } } if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_PPPOE)) { @@ -1217,6 +1401,22 @@ ice_parse_cls_flower(struct net_device *filter_dev, struct ice_vsi *vsi, return -EINVAL; } + if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_IP)) { + struct flow_match_ip match; + + flow_rule_match_ip(rule, &match); + ice_tc_set_tos_ttl(&match, fltr, headers, false); + } + + if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_L2TPV3)) { + struct flow_match_l2tpv3 match; + + flow_rule_match_l2tpv3(rule, &match); + + fltr->flags |= ICE_TC_FLWR_FIELD_L2TPV3_SESSID; + headers->l2tpv3_hdr.session_id = match.key->session_id; + } + if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_PORTS)) { struct flow_match_ports match; diff --git a/drivers/net/ethernet/intel/ice/ice_tc_lib.h b/drivers/net/ethernet/intel/ice/ice_tc_lib.h index 91cd3d3778c7..92642faad595 100644 --- a/drivers/net/ethernet/intel/ice/ice_tc_lib.h +++ b/drivers/net/ethernet/intel/ice/ice_tc_lib.h @@ -26,9 +26,18 @@ #define ICE_TC_FLWR_FIELD_CVLAN BIT(19) #define ICE_TC_FLWR_FIELD_PPPOE_SESSID BIT(20) #define ICE_TC_FLWR_FIELD_PPP_PROTO BIT(21) +#define ICE_TC_FLWR_FIELD_IP_TOS BIT(22) +#define ICE_TC_FLWR_FIELD_IP_TTL BIT(23) +#define ICE_TC_FLWR_FIELD_ENC_IP_TOS BIT(24) +#define ICE_TC_FLWR_FIELD_ENC_IP_TTL BIT(25) +#define ICE_TC_FLWR_FIELD_L2TPV3_SESSID BIT(26) +#define ICE_TC_FLWR_FIELD_VLAN_PRIO BIT(27) +#define ICE_TC_FLWR_FIELD_CVLAN_PRIO BIT(28) #define ICE_TC_FLOWER_MASK_32 0xFFFFFFFF +#define ICE_IPV6_HDR_TC_MASK 0xFF00000 + struct ice_indr_block_priv { struct net_device *netdev; struct ice_netdev_priv *np; @@ -42,7 +51,7 @@ struct ice_tc_flower_action { struct ice_tc_vlan_hdr { __be16 vlan_id; /* Only last 12 bits valid */ - u16 vlan_prio; /* Only last 3 bits valid (valid values: 0..7) */ + __be16 vlan_prio; /* Only last 3 bits valid (valid values: 0..7) */ __be16 vlan_tpid; }; @@ -80,6 +89,10 @@ struct ice_tc_l3_hdr { u8 ttl; }; +struct ice_tc_l2tpv3_hdr { + __be32 session_id; +}; + struct ice_tc_l4_hdr { __be16 dst_port; __be16 src_port; @@ -92,6 +105,7 @@ struct ice_tc_flower_lyr_2_4_hdrs { struct ice_tc_vlan_hdr vlan_hdr; struct ice_tc_vlan_hdr cvlan_hdr; struct ice_tc_pppoe_hdr pppoe_hdr; + struct ice_tc_l2tpv3_hdr l2tpv3_hdr; /* L3 (IPv4[6]) layer fields with their mask */ struct ice_tc_l3_hdr l3_key; struct ice_tc_l3_hdr l3_mask; diff --git a/drivers/net/ethernet/intel/ice/ice_txrx.c b/drivers/net/ethernet/intel/ice/ice_txrx.c index dd2285d4bef4..dbe80e5053a8 100644 --- a/drivers/net/ethernet/intel/ice/ice_txrx.c +++ b/drivers/net/ethernet/intel/ice/ice_txrx.c @@ -2258,8 +2258,10 @@ ice_tstamp(struct ice_tx_ring *tx_ring, struct sk_buff *skb, /* Grab an open timestamp slot */ idx = ice_ptp_request_ts(tx_ring->tx_tstamps, skb); - if (idx < 0) + if (idx < 0) { + tx_ring->vsi->back->ptp.tx_hwtstamp_skipped++; return; + } off->cd_qw1 |= (u64)(ICE_TX_DESC_DTYPE_CTX | (ICE_TX_CTX_DESC_TSYN << ICE_TXD_CTX_QW1_CMD_S) | diff --git a/drivers/net/ethernet/intel/ice/ice_txrx.h b/drivers/net/ethernet/intel/ice/ice_txrx.h index ca902af54bb4..932b5661ec4d 100644 --- a/drivers/net/ethernet/intel/ice/ice_txrx.h +++ b/drivers/net/ethernet/intel/ice/ice_txrx.h @@ -295,10 +295,11 @@ struct ice_rx_ring { struct xsk_buff_pool *xsk_pool; struct sk_buff *skb; dma_addr_t dma; /* physical address of ring */ -#define ICE_RX_FLAGS_RING_BUILD_SKB BIT(1) u64 cached_phctime; u8 dcb_tc; /* Traffic class of ring */ u8 ptp_rx; +#define ICE_RX_FLAGS_RING_BUILD_SKB BIT(1) +#define ICE_RX_FLAGS_CRC_STRIP_DIS BIT(2) u8 flags; } ____cacheline_internodealigned_in_smp; diff --git a/drivers/net/ethernet/intel/ice/ice_type.h b/drivers/net/ethernet/intel/ice/ice_type.h index 861b64322959..e1abfcee96dc 100644 --- a/drivers/net/ethernet/intel/ice/ice_type.h +++ b/drivers/net/ethernet/intel/ice/ice_type.h @@ -347,6 +347,7 @@ struct ice_ts_func_info { #define ICE_TS_DEV_ENA_M BIT(24) #define ICE_TS_TMR0_ENA_M BIT(25) #define ICE_TS_TMR1_ENA_M BIT(26) +#define ICE_TS_LL_TX_TS_READ_M BIT(28) struct ice_ts_dev_info { /* Device specific info */ @@ -359,6 +360,7 @@ struct ice_ts_dev_info { u8 ena; u8 tmr0_ena; u8 tmr1_ena; + u8 ts_ll_read; }; /* Function specific capabilities */ @@ -564,6 +566,8 @@ enum ice_rl_type { #define ICE_SCHED_INVAL_PROF_ID 0xFFFF #define ICE_SCHED_DFLT_BURST_SIZE (15 * 1024) /* in bytes (15k) */ +#define ICE_MAX_PORT_PER_PCI_DEV 8 + /* Data structure for saving BW information */ enum ice_bw_type { ICE_BW_TYPE_PRIO, @@ -885,8 +889,6 @@ struct ice_hw { /* INTRL granularity in 1 us */ u8 intrl_gran; - u8 ucast_shared; /* true if VSIs can share unicast addr */ - #define ICE_PHY_PER_NAC 1 #define ICE_MAX_QUAD 2 #define ICE_NUM_QUAD_TYPE 2 diff --git a/drivers/net/ethernet/intel/igb/igb_ethtool.c b/drivers/net/ethernet/intel/igb/igb_ethtool.c index c14fc871dd41..e5f3e7680dc6 100644 --- a/drivers/net/ethernet/intel/igb/igb_ethtool.c +++ b/drivers/net/ethernet/intel/igb/igb_ethtool.c @@ -850,14 +850,14 @@ static void igb_get_drvinfo(struct net_device *netdev, { struct igb_adapter *adapter = netdev_priv(netdev); - strlcpy(drvinfo->driver, igb_driver_name, sizeof(drvinfo->driver)); + strscpy(drvinfo->driver, igb_driver_name, sizeof(drvinfo->driver)); /* EEPROM image version # is reported as firmware version # for * 82575 controllers */ - strlcpy(drvinfo->fw_version, adapter->fw_version, + strscpy(drvinfo->fw_version, adapter->fw_version, sizeof(drvinfo->fw_version)); - strlcpy(drvinfo->bus_info, pci_name(adapter->pdev), + strscpy(drvinfo->bus_info, pci_name(adapter->pdev), sizeof(drvinfo->bus_info)); drvinfo->n_priv_flags = IGB_PRIV_FLAGS_STR_LEN; diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c index 2796e81d2726..f8e32833226c 100644 --- a/drivers/net/ethernet/intel/igb/igb_main.c +++ b/drivers/net/ethernet/intel/igb/igb_main.c @@ -1211,8 +1211,7 @@ static int igb_alloc_q_vector(struct igb_adapter *adapter, return -ENOMEM; /* initialize NAPI */ - netif_napi_add(adapter->netdev, &q_vector->napi, - igb_poll, 64); + netif_napi_add(adapter->netdev, &q_vector->napi, igb_poll); /* tie q_vector and adapter together */ adapter->q_vector[v_idx] = q_vector; @@ -3138,7 +3137,7 @@ static s32 igb_init_i2c(struct igb_adapter *adapter) adapter->i2c_algo.data = adapter; adapter->i2c_adap.algo_data = &adapter->i2c_algo; adapter->i2c_adap.dev.parent = &adapter->pdev->dev; - strlcpy(adapter->i2c_adap.name, "igb BB", + strscpy(adapter->i2c_adap.name, "igb BB", sizeof(adapter->i2c_adap.name)); status = i2c_bit_add_bus(&adapter->i2c_adap); return status; diff --git a/drivers/net/ethernet/intel/igbvf/ethtool.c b/drivers/net/ethernet/intel/igbvf/ethtool.c index 9d4322b74163..83b97989a6bd 100644 --- a/drivers/net/ethernet/intel/igbvf/ethtool.c +++ b/drivers/net/ethernet/intel/igbvf/ethtool.c @@ -169,8 +169,8 @@ static void igbvf_get_drvinfo(struct net_device *netdev, { struct igbvf_adapter *adapter = netdev_priv(netdev); - strlcpy(drvinfo->driver, igbvf_driver_name, sizeof(drvinfo->driver)); - strlcpy(drvinfo->bus_info, pci_name(adapter->pdev), + strscpy(drvinfo->driver, igbvf_driver_name, sizeof(drvinfo->driver)); + strscpy(drvinfo->bus_info, pci_name(adapter->pdev), sizeof(drvinfo->bus_info)); } diff --git a/drivers/net/ethernet/intel/igbvf/netdev.c b/drivers/net/ethernet/intel/igbvf/netdev.c index f4e91db89fe5..3a32809510fc 100644 --- a/drivers/net/ethernet/intel/igbvf/netdev.c +++ b/drivers/net/ethernet/intel/igbvf/netdev.c @@ -1109,7 +1109,7 @@ static int igbvf_alloc_queues(struct igbvf_adapter *adapter) return -ENOMEM; } - netif_napi_add(netdev, &adapter->rx_ring->napi, igbvf_poll, 64); + netif_napi_add(netdev, &adapter->rx_ring->napi, igbvf_poll); return 0; } diff --git a/drivers/net/ethernet/intel/igc/igc_defines.h b/drivers/net/ethernet/intel/igc/igc_defines.h index 5c66b97c0cfa..4f9d7f013a95 100644 --- a/drivers/net/ethernet/intel/igc/igc_defines.h +++ b/drivers/net/ethernet/intel/igc/igc_defines.h @@ -610,7 +610,6 @@ #define IGC_MDIC_OP_WRITE 0x04000000 #define IGC_MDIC_OP_READ 0x08000000 #define IGC_MDIC_READY 0x10000000 -#define IGC_MDIC_INT_EN 0x20000000 #define IGC_MDIC_ERROR 0x40000000 #define IGC_N0_QUEUE -1 diff --git a/drivers/net/ethernet/intel/igc/igc_main.c b/drivers/net/ethernet/intel/igc/igc_main.c index ebff0e04045d..34889be63e78 100644 --- a/drivers/net/ethernet/intel/igc/igc_main.c +++ b/drivers/net/ethernet/intel/igc/igc_main.c @@ -2129,65 +2129,102 @@ static bool igc_alloc_rx_buffers_zc(struct igc_ring *ring, u16 count) return ok; } -static int igc_xdp_init_tx_buffer(struct igc_tx_buffer *buffer, - struct xdp_frame *xdpf, - struct igc_ring *ring) -{ - dma_addr_t dma; - - dma = dma_map_single(ring->dev, xdpf->data, xdpf->len, DMA_TO_DEVICE); - if (dma_mapping_error(ring->dev, dma)) { - netdev_err_once(ring->netdev, "Failed to map DMA for TX\n"); - return -ENOMEM; - } - - buffer->type = IGC_TX_BUFFER_TYPE_XDP; - buffer->xdpf = xdpf; - buffer->protocol = 0; - buffer->bytecount = xdpf->len; - buffer->gso_segs = 1; - buffer->time_stamp = jiffies; - dma_unmap_len_set(buffer, len, xdpf->len); - dma_unmap_addr_set(buffer, dma, dma); - return 0; -} - /* This function requires __netif_tx_lock is held by the caller. */ static int igc_xdp_init_tx_descriptor(struct igc_ring *ring, struct xdp_frame *xdpf) { - struct igc_tx_buffer *buffer; - union igc_adv_tx_desc *desc; - u32 cmd_type, olinfo_status; - int err; + struct skb_shared_info *sinfo = xdp_get_shared_info_from_frame(xdpf); + u8 nr_frags = unlikely(xdp_frame_has_frags(xdpf)) ? sinfo->nr_frags : 0; + u16 count, index = ring->next_to_use; + struct igc_tx_buffer *head = &ring->tx_buffer_info[index]; + struct igc_tx_buffer *buffer = head; + union igc_adv_tx_desc *desc = IGC_TX_DESC(ring, index); + u32 olinfo_status, len = xdpf->len, cmd_type; + void *data = xdpf->data; + u16 i; - if (!igc_desc_unused(ring)) - return -EBUSY; + count = TXD_USE_COUNT(len); + for (i = 0; i < nr_frags; i++) + count += TXD_USE_COUNT(skb_frag_size(&sinfo->frags[i])); - buffer = &ring->tx_buffer_info[ring->next_to_use]; - err = igc_xdp_init_tx_buffer(buffer, xdpf, ring); - if (err) - return err; + if (igc_maybe_stop_tx(ring, count + 3)) { + /* this is a hard error */ + return -EBUSY; + } - cmd_type = IGC_ADVTXD_DTYP_DATA | IGC_ADVTXD_DCMD_DEXT | - IGC_ADVTXD_DCMD_IFCS | IGC_TXD_DCMD | - buffer->bytecount; - olinfo_status = buffer->bytecount << IGC_ADVTXD_PAYLEN_SHIFT; + i = 0; + head->bytecount = xdp_get_frame_len(xdpf); + head->type = IGC_TX_BUFFER_TYPE_XDP; + head->gso_segs = 1; + head->xdpf = xdpf; - desc = IGC_TX_DESC(ring, ring->next_to_use); - desc->read.cmd_type_len = cpu_to_le32(cmd_type); + olinfo_status = head->bytecount << IGC_ADVTXD_PAYLEN_SHIFT; desc->read.olinfo_status = cpu_to_le32(olinfo_status); - desc->read.buffer_addr = cpu_to_le64(dma_unmap_addr(buffer, dma)); - netdev_tx_sent_queue(txring_txq(ring), buffer->bytecount); + for (;;) { + dma_addr_t dma; - buffer->next_to_watch = desc; + dma = dma_map_single(ring->dev, data, len, DMA_TO_DEVICE); + if (dma_mapping_error(ring->dev, dma)) { + netdev_err_once(ring->netdev, + "Failed to map DMA for TX\n"); + goto unmap; + } - ring->next_to_use++; - if (ring->next_to_use == ring->count) - ring->next_to_use = 0; + dma_unmap_len_set(buffer, len, len); + dma_unmap_addr_set(buffer, dma, dma); + + cmd_type = IGC_ADVTXD_DTYP_DATA | IGC_ADVTXD_DCMD_DEXT | + IGC_ADVTXD_DCMD_IFCS | len; + + desc->read.cmd_type_len = cpu_to_le32(cmd_type); + desc->read.buffer_addr = cpu_to_le64(dma); + + buffer->protocol = 0; + + if (++index == ring->count) + index = 0; + + if (i == nr_frags) + break; + + buffer = &ring->tx_buffer_info[index]; + desc = IGC_TX_DESC(ring, index); + desc->read.olinfo_status = 0; + + data = skb_frag_address(&sinfo->frags[i]); + len = skb_frag_size(&sinfo->frags[i]); + i++; + } + desc->read.cmd_type_len |= cpu_to_le32(IGC_TXD_DCMD); + + netdev_tx_sent_queue(txring_txq(ring), head->bytecount); + /* set the timestamp */ + head->time_stamp = jiffies; + /* set next_to_watch value indicating a packet is present */ + head->next_to_watch = desc; + ring->next_to_use = index; return 0; + +unmap: + for (;;) { + buffer = &ring->tx_buffer_info[index]; + if (dma_unmap_len(buffer, len)) + dma_unmap_page(ring->dev, + dma_unmap_addr(buffer, dma), + dma_unmap_len(buffer, len), + DMA_TO_DEVICE); + dma_unmap_len_set(buffer, len, 0); + if (buffer == head) + break; + + if (!index) + index += ring->count; + index--; + } + + return -ENOMEM; } static struct igc_ring *igc_xdp_get_tx_ring(struct igc_adapter *adapter, @@ -2369,6 +2406,7 @@ static int igc_clean_rx_irq(struct igc_q_vector *q_vector, const int budget) xdp_prepare_buff(&xdp, pktbuf - igc_rx_offset(rx_ring), igc_rx_offset(rx_ring) + pkt_offset, size, true); + xdp_buff_clear_frags_flag(&xdp); skb = igc_xdp_run_prog(adapter, &xdp); } @@ -4356,8 +4394,7 @@ static int igc_alloc_q_vector(struct igc_adapter *adapter, return -ENOMEM; /* initialize NAPI */ - netif_napi_add(adapter->netdev, &q_vector->napi, - igc_poll, 64); + netif_napi_add(adapter->netdev, &q_vector->napi, igc_poll); /* tie q_vector and adapter together */ adapter->q_vector[v_idx] = q_vector; diff --git a/drivers/net/ethernet/intel/ixgb/ixgb_ethtool.c b/drivers/net/ethernet/intel/ixgb/ixgb_ethtool.c index 46efcfab7234..efa980514944 100644 --- a/drivers/net/ethernet/intel/ixgb/ixgb_ethtool.c +++ b/drivers/net/ethernet/intel/ixgb/ixgb_ethtool.c @@ -456,9 +456,9 @@ ixgb_get_drvinfo(struct net_device *netdev, { struct ixgb_adapter *adapter = netdev_priv(netdev); - strlcpy(drvinfo->driver, ixgb_driver_name, + strscpy(drvinfo->driver, ixgb_driver_name, sizeof(drvinfo->driver)); - strlcpy(drvinfo->bus_info, pci_name(adapter->pdev), + strscpy(drvinfo->bus_info, pci_name(adapter->pdev), sizeof(drvinfo->bus_info)); } diff --git a/drivers/net/ethernet/intel/ixgb/ixgb_main.c b/drivers/net/ethernet/intel/ixgb/ixgb_main.c index 45be9a1ab6af..b4d47e7a76c8 100644 --- a/drivers/net/ethernet/intel/ixgb/ixgb_main.c +++ b/drivers/net/ethernet/intel/ixgb/ixgb_main.c @@ -414,7 +414,7 @@ ixgb_probe(struct pci_dev *pdev, const struct pci_device_id *ent) netdev->netdev_ops = &ixgb_netdev_ops; ixgb_set_ethtool_ops(netdev); netdev->watchdog_timeo = 5 * HZ; - netif_napi_add(netdev, &adapter->napi, ixgb_clean, 64); + netif_napi_add(netdev, &adapter->napi, ixgb_clean); strncpy(netdev->name, pci_name(pdev), sizeof(netdev->name) - 1); diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c index 04f453eabef6..e88e3dfac8c2 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c @@ -1106,12 +1106,12 @@ static void ixgbe_get_drvinfo(struct net_device *netdev, { struct ixgbe_adapter *adapter = netdev_priv(netdev); - strlcpy(drvinfo->driver, ixgbe_driver_name, sizeof(drvinfo->driver)); + strscpy(drvinfo->driver, ixgbe_driver_name, sizeof(drvinfo->driver)); - strlcpy(drvinfo->fw_version, adapter->eeprom_id, + strscpy(drvinfo->fw_version, adapter->eeprom_id, sizeof(drvinfo->fw_version)); - strlcpy(drvinfo->bus_info, pci_name(adapter->pdev), + strscpy(drvinfo->bus_info, pci_name(adapter->pdev), sizeof(drvinfo->bus_info)); drvinfo->n_priv_flags = IXGBE_PRIV_FLAGS_STR_LEN; @@ -1964,15 +1964,13 @@ static bool ixgbe_check_lbtest_frame(struct ixgbe_rx_buffer *rx_buffer, frame_size >>= 1; - data = kmap(rx_buffer->page) + rx_buffer->page_offset; + data = page_address(rx_buffer->page) + rx_buffer->page_offset; if (data[3] != 0xFF || data[frame_size + 10] != 0xBE || data[frame_size + 12] != 0xAF) match = false; - kunmap(rx_buffer->page); - return match; } diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_fcoe.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_fcoe.c index 0fcd82036d4e..7311bd545acf 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_fcoe.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_fcoe.c @@ -1004,7 +1004,7 @@ int ixgbe_fcoe_get_hbainfo(struct net_device *netdev, ixgbe_driver_name, UTS_RELEASE); /* Firmware Version */ - strlcpy(info->firmware_version, adapter->eeprom_id, + strscpy(info->firmware_version, adapter->eeprom_id, sizeof(info->firmware_version)); /* Model */ diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_lib.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_lib.c index 86b11164655e..f8156fe4b1dc 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_lib.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_lib.c @@ -874,8 +874,7 @@ static int ixgbe_alloc_q_vector(struct ixgbe_adapter *adapter, #endif /* initialize NAPI */ - netif_napi_add(adapter->netdev, &q_vector->napi, - ixgbe_poll, 64); + netif_napi_add(adapter->netdev, &q_vector->napi, ixgbe_poll); /* tie q_vector and adapter together */ adapter->q_vector[v_idx] = q_vector; diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index d1e430b8c8aa..298cfbfcb7b6 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -10849,7 +10849,7 @@ static int ixgbe_probe(struct pci_dev *pdev, const struct pci_device_id *ent) netdev->netdev_ops = &ixgbe_netdev_ops; ixgbe_set_ethtool_ops(netdev); netdev->watchdog_timeo = 5 * HZ; - strlcpy(netdev->name, pci_name(pdev), sizeof(netdev->name)); + strscpy(netdev->name, pci_name(pdev), sizeof(netdev->name)); /* Setup hw api */ hw->mac.ops = *ii->mac_ops; @@ -11140,7 +11140,7 @@ skip_sriov: err = ixgbe_read_pba_string_generic(hw, part_str, sizeof(part_str)); if (err) - strlcpy(part_str, "Unknown", sizeof(part_str)); + strscpy(part_str, "Unknown", sizeof(part_str)); if (ixgbe_is_sfp(hw) && hw->phy.sfp_type != ixgbe_sfp_type_not_present) e_dev_info("MAC: %d, PHY: %d, SFP+: %d, PBA No: %s\n", hw->mac.type, hw->phy.type, hw->phy.sfp_type, diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h b/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h index 7f7ea468ffa9..2b00db92b08f 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h @@ -3712,7 +3712,9 @@ struct ixgbe_info { #define IXGBE_KRM_LINK_S1(P) ((P) ? 0x8200 : 0x4200) #define IXGBE_KRM_LINK_CTRL_1(P) ((P) ? 0x820C : 0x420C) #define IXGBE_KRM_AN_CNTL_1(P) ((P) ? 0x822C : 0x422C) +#define IXGBE_KRM_AN_CNTL_4(P) ((P) ? 0x8238 : 0x4238) #define IXGBE_KRM_AN_CNTL_8(P) ((P) ? 0x8248 : 0x4248) +#define IXGBE_KRM_PCS_KX_AN(P) ((P) ? 0x9918 : 0x5918) #define IXGBE_KRM_SGMII_CTRL(P) ((P) ? 0x82A0 : 0x42A0) #define IXGBE_KRM_LP_BASE_PAGE_HIGH(P) ((P) ? 0x836C : 0x436C) #define IXGBE_KRM_DSP_TXFFE_STATE_4(P) ((P) ? 0x8634 : 0x4634) @@ -3722,6 +3724,7 @@ struct ixgbe_info { #define IXGBE_KRM_PMD_FLX_MASK_ST20(P) ((P) ? 0x9054 : 0x5054) #define IXGBE_KRM_TX_COEFF_CTRL_1(P) ((P) ? 0x9520 : 0x5520) #define IXGBE_KRM_RX_ANA_CTL(P) ((P) ? 0x9A00 : 0x5A00) +#define IXGBE_KRM_FLX_TMRS_CTRL_ST31(P) ((P) ? 0x9180 : 0x5180) #define IXGBE_KRM_PMD_FLX_MASK_ST20_SFI_10G_DA ~(0x3 << 20) #define IXGBE_KRM_PMD_FLX_MASK_ST20_SFI_10G_SR BIT(20) diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c index 35c2b9b8bd19..aa4bf6c9a2f7 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c @@ -1721,9 +1721,59 @@ static s32 ixgbe_setup_sfi_x550a(struct ixgbe_hw *hw, ixgbe_link_speed *speed) return IXGBE_ERR_LINK_SETUP; } - status = mac->ops.write_iosf_sb_reg(hw, - IXGBE_KRM_PMD_FLX_MASK_ST20(hw->bus.lan_id), - IXGBE_SB_IOSF_TARGET_KR_PHY, reg_val); + (void)mac->ops.write_iosf_sb_reg(hw, + IXGBE_KRM_PMD_FLX_MASK_ST20(hw->bus.lan_id), + IXGBE_SB_IOSF_TARGET_KR_PHY, reg_val); + + /* change mode enforcement rules to hybrid */ + (void)mac->ops.read_iosf_sb_reg(hw, + IXGBE_KRM_FLX_TMRS_CTRL_ST31(hw->bus.lan_id), + IXGBE_SB_IOSF_TARGET_KR_PHY, ®_val); + reg_val |= 0x0400; + + (void)mac->ops.write_iosf_sb_reg(hw, + IXGBE_KRM_FLX_TMRS_CTRL_ST31(hw->bus.lan_id), + IXGBE_SB_IOSF_TARGET_KR_PHY, reg_val); + + /* manually control the config */ + (void)mac->ops.read_iosf_sb_reg(hw, + IXGBE_KRM_LINK_CTRL_1(hw->bus.lan_id), + IXGBE_SB_IOSF_TARGET_KR_PHY, ®_val); + reg_val |= 0x20002240; + + (void)mac->ops.write_iosf_sb_reg(hw, + IXGBE_KRM_LINK_CTRL_1(hw->bus.lan_id), + IXGBE_SB_IOSF_TARGET_KR_PHY, reg_val); + + /* move the AN base page values */ + (void)mac->ops.read_iosf_sb_reg(hw, + IXGBE_KRM_PCS_KX_AN(hw->bus.lan_id), + IXGBE_SB_IOSF_TARGET_KR_PHY, ®_val); + reg_val |= 0x1; + + (void)mac->ops.write_iosf_sb_reg(hw, + IXGBE_KRM_PCS_KX_AN(hw->bus.lan_id), + IXGBE_SB_IOSF_TARGET_KR_PHY, reg_val); + + /* set the AN37 over CB mode */ + (void)mac->ops.read_iosf_sb_reg(hw, + IXGBE_KRM_AN_CNTL_4(hw->bus.lan_id), + IXGBE_SB_IOSF_TARGET_KR_PHY, ®_val); + reg_val |= 0x20000000; + + (void)mac->ops.write_iosf_sb_reg(hw, + IXGBE_KRM_AN_CNTL_4(hw->bus.lan_id), + IXGBE_SB_IOSF_TARGET_KR_PHY, reg_val); + + /* restart AN manually */ + (void)mac->ops.read_iosf_sb_reg(hw, + IXGBE_KRM_LINK_CTRL_1(hw->bus.lan_id), + IXGBE_SB_IOSF_TARGET_KR_PHY, ®_val); + reg_val |= IXGBE_KRM_LINK_CTRL_1_TETH_AN_RESTART; + + (void)mac->ops.write_iosf_sb_reg(hw, + IXGBE_KRM_LINK_CTRL_1(hw->bus.lan_id), + IXGBE_SB_IOSF_TARGET_KR_PHY, reg_val); /* Toggle port SW reset by AN reset. */ status = ixgbe_restart_an_internal_phy_x550em(hw); diff --git a/drivers/net/ethernet/intel/ixgbevf/ethtool.c b/drivers/net/ethernet/intel/ixgbevf/ethtool.c index fed46872af2b..ccfa6b91aac6 100644 --- a/drivers/net/ethernet/intel/ixgbevf/ethtool.c +++ b/drivers/net/ethernet/intel/ixgbevf/ethtool.c @@ -213,8 +213,8 @@ static void ixgbevf_get_drvinfo(struct net_device *netdev, { struct ixgbevf_adapter *adapter = netdev_priv(netdev); - strlcpy(drvinfo->driver, ixgbevf_driver_name, sizeof(drvinfo->driver)); - strlcpy(drvinfo->bus_info, pci_name(adapter->pdev), + strscpy(drvinfo->driver, ixgbevf_driver_name, sizeof(drvinfo->driver)); + strscpy(drvinfo->bus_info, pci_name(adapter->pdev), sizeof(drvinfo->bus_info)); drvinfo->n_priv_flags = IXGBEVF_PRIV_FLAGS_STR_LEN; diff --git a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c index 2f12fbe229c1..99933e89717a 100644 --- a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c +++ b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c @@ -2733,7 +2733,7 @@ static int ixgbevf_alloc_q_vector(struct ixgbevf_adapter *adapter, int v_idx, return -ENOMEM; /* initialize NAPI */ - netif_napi_add(adapter->netdev, &q_vector->napi, ixgbevf_poll, 64); + netif_napi_add(adapter->netdev, &q_vector->napi, ixgbevf_poll); /* tie q_vector and adapter together */ adapter->q_vector[v_idx] = q_vector; diff --git a/drivers/net/ethernet/jme.c b/drivers/net/ethernet/jme.c index f43d6616bc0d..1732ec3c3dbd 100644 --- a/drivers/net/ethernet/jme.c +++ b/drivers/net/ethernet/jme.c @@ -2332,9 +2332,9 @@ jme_get_drvinfo(struct net_device *netdev, { struct jme_adapter *jme = netdev_priv(netdev); - strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); - strlcpy(info->version, DRV_VERSION, sizeof(info->version)); - strlcpy(info->bus_info, pci_name(jme->pdev), sizeof(info->bus_info)); + strscpy(info->driver, DRV_NAME, sizeof(info->driver)); + strscpy(info->version, DRV_VERSION, sizeof(info->version)); + strscpy(info->bus_info, pci_name(jme->pdev), sizeof(info->bus_info)); } static int @@ -3009,7 +3009,7 @@ jme_init_one(struct pci_dev *pdev, jwrite32(jme, JME_APMC, apmc); } - netif_napi_add(netdev, &jme->napi, jme_poll, NAPI_POLL_WEIGHT); + netif_napi_add(netdev, &jme->napi, jme_poll); spin_lock_init(&jme->phy_lock); spin_lock_init(&jme->macaddr_lock); diff --git a/drivers/net/ethernet/korina.c b/drivers/net/ethernet/korina.c index df9a8eefa007..2b9335cb4bb3 100644 --- a/drivers/net/ethernet/korina.c +++ b/drivers/net/ethernet/korina.c @@ -416,7 +416,8 @@ static void korina_abort_rx(struct net_device *dev) } /* transmit packet */ -static int korina_send_packet(struct sk_buff *skb, struct net_device *dev) +static netdev_tx_t korina_send_packet(struct sk_buff *skb, + struct net_device *dev) { struct korina_private *lp = netdev_priv(dev); u32 chain_prev, chain_next; @@ -938,9 +939,9 @@ static void netdev_get_drvinfo(struct net_device *dev, { struct korina_private *lp = netdev_priv(dev); - strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); - strlcpy(info->version, DRV_VERSION, sizeof(info->version)); - strlcpy(info->bus_info, lp->dev->name, sizeof(info->bus_info)); + strscpy(info->driver, DRV_NAME, sizeof(info->driver)); + strscpy(info->version, DRV_VERSION, sizeof(info->version)); + strscpy(info->bus_info, lp->dev->name, sizeof(info->bus_info)); } static int netdev_get_link_ksettings(struct net_device *dev, @@ -1354,7 +1355,7 @@ static int korina_probe(struct platform_device *pdev) dev->netdev_ops = &korina_netdev_ops; dev->ethtool_ops = &netdev_ethtool_ops; dev->watchdog_timeo = TX_TIMEOUT; - netif_napi_add(dev, &lp->napi, korina_poll, NAPI_POLL_WEIGHT); + netif_napi_add(dev, &lp->napi, korina_poll); lp->mii_if.dev = dev; lp->mii_if.mdio_read = korina_mdio_read; diff --git a/drivers/net/ethernet/lantiq_etop.c b/drivers/net/ethernet/lantiq_etop.c index 7cedbe1fdfd7..59aab4086dcc 100644 --- a/drivers/net/ethernet/lantiq_etop.c +++ b/drivers/net/ethernet/lantiq_etop.c @@ -470,7 +470,7 @@ ltq_etop_stop(struct net_device *dev) return 0; } -static int +static netdev_tx_t ltq_etop_tx(struct sk_buff *skb, struct net_device *dev) { int queue = skb_get_queue_mapping(skb); diff --git a/drivers/net/ethernet/lantiq_xrx200.c b/drivers/net/ethernet/lantiq_xrx200.c index 57f27cc7724e..8d646c7f8c82 100644 --- a/drivers/net/ethernet/lantiq_xrx200.c +++ b/drivers/net/ethernet/lantiq_xrx200.c @@ -620,8 +620,7 @@ static int xrx200_probe(struct platform_device *pdev) PMAC_HD_CTL); /* setup NAPI */ - netif_napi_add(net_dev, &priv->chan_rx.napi, xrx200_poll_rx, - NAPI_POLL_WEIGHT); + netif_napi_add(net_dev, &priv->chan_rx.napi, xrx200_poll_rx); netif_napi_add_tx(net_dev, &priv->chan_tx.napi, xrx200_tx_housekeeping); diff --git a/drivers/net/ethernet/litex/litex_liteeth.c b/drivers/net/ethernet/litex/litex_liteeth.c index fdd99f0de424..35f24e0f0934 100644 --- a/drivers/net/ethernet/litex/litex_liteeth.c +++ b/drivers/net/ethernet/litex/litex_liteeth.c @@ -152,7 +152,8 @@ static int liteeth_stop(struct net_device *netdev) return 0; } -static int liteeth_start_xmit(struct sk_buff *skb, struct net_device *netdev) +static netdev_tx_t liteeth_start_xmit(struct sk_buff *skb, + struct net_device *netdev) { struct liteeth *priv = netdev_priv(netdev); void __iomem *txbuffer; diff --git a/drivers/net/ethernet/marvell/mv643xx_eth.c b/drivers/net/ethernet/marvell/mv643xx_eth.c index b6be0552a6c1..707993b445d1 100644 --- a/drivers/net/ethernet/marvell/mv643xx_eth.c +++ b/drivers/net/ethernet/marvell/mv643xx_eth.c @@ -1603,12 +1603,12 @@ mv643xx_eth_set_link_ksettings(struct net_device *dev, static void mv643xx_eth_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *drvinfo) { - strlcpy(drvinfo->driver, mv643xx_eth_driver_name, + strscpy(drvinfo->driver, mv643xx_eth_driver_name, sizeof(drvinfo->driver)); - strlcpy(drvinfo->version, mv643xx_eth_driver_version, + strscpy(drvinfo->version, mv643xx_eth_driver_version, sizeof(drvinfo->version)); - strlcpy(drvinfo->fw_version, "N/A", sizeof(drvinfo->fw_version)); - strlcpy(drvinfo->bus_info, "platform", sizeof(drvinfo->bus_info)); + strscpy(drvinfo->fw_version, "N/A", sizeof(drvinfo->fw_version)); + strscpy(drvinfo->bus_info, "platform", sizeof(drvinfo->bus_info)); } static int mv643xx_eth_get_coalesce(struct net_device *dev, @@ -3183,7 +3183,7 @@ static int mv643xx_eth_probe(struct platform_device *pdev) INIT_WORK(&mp->tx_timeout_task, tx_timeout_task); - netif_napi_add(dev, &mp->napi, mv643xx_eth_poll, NAPI_POLL_WEIGHT); + netif_napi_add(dev, &mp->napi, mv643xx_eth_poll); timer_setup(&mp->rx_oom, oom_timer_wrapper, 0); diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c index 0caa2df87c04..ff3e361e06e7 100644 --- a/drivers/net/ethernet/marvell/mvneta.c +++ b/drivers/net/ethernet/marvell/mvneta.c @@ -4656,11 +4656,11 @@ mvneta_ethtool_get_coalesce(struct net_device *dev, static void mvneta_ethtool_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *drvinfo) { - strlcpy(drvinfo->driver, MVNETA_DRIVER_NAME, + strscpy(drvinfo->driver, MVNETA_DRIVER_NAME, sizeof(drvinfo->driver)); - strlcpy(drvinfo->version, MVNETA_DRIVER_VERSION, + strscpy(drvinfo->version, MVNETA_DRIVER_VERSION, sizeof(drvinfo->version)); - strlcpy(drvinfo->bus_info, dev_name(&dev->dev), + strscpy(drvinfo->bus_info, dev_name(&dev->dev), sizeof(drvinfo->bus_info)); } @@ -5600,14 +5600,13 @@ static int mvneta_probe(struct platform_device *pdev) * operation, so only single NAPI should be initialized. */ if (pp->neta_armada3700) { - netif_napi_add(dev, &pp->napi, mvneta_poll, NAPI_POLL_WEIGHT); + netif_napi_add(dev, &pp->napi, mvneta_poll); } else { for_each_present_cpu(cpu) { struct mvneta_pcpu_port *port = per_cpu_ptr(pp->ports, cpu); - netif_napi_add(dev, &port->napi, mvneta_poll, - NAPI_POLL_WEIGHT); + netif_napi_add(dev, &port->napi, mvneta_poll); port->pp = pp; } } diff --git a/drivers/net/ethernet/marvell/mvpp2/mvpp2.h b/drivers/net/ethernet/marvell/mvpp2/mvpp2.h index ad73a488fc5f..11e603686a27 100644 --- a/drivers/net/ethernet/marvell/mvpp2/mvpp2.h +++ b/drivers/net/ethernet/marvell/mvpp2/mvpp2.h @@ -1530,6 +1530,7 @@ u32 mvpp2_read(struct mvpp2 *priv, u32 offset); void mvpp2_dbgfs_init(struct mvpp2 *priv, const char *name); void mvpp2_dbgfs_cleanup(struct mvpp2 *priv); +void mvpp2_dbgfs_exit(void); void mvpp23_rx_fifo_fc_en(struct mvpp2 *priv, int port, bool en); diff --git a/drivers/net/ethernet/marvell/mvpp2/mvpp2_debugfs.c b/drivers/net/ethernet/marvell/mvpp2/mvpp2_debugfs.c index 4a3baa7e0142..75e83ea2a926 100644 --- a/drivers/net/ethernet/marvell/mvpp2/mvpp2_debugfs.c +++ b/drivers/net/ethernet/marvell/mvpp2/mvpp2_debugfs.c @@ -691,6 +691,13 @@ static int mvpp2_dbgfs_port_init(struct dentry *parent, return 0; } +static struct dentry *mvpp2_root; + +void mvpp2_dbgfs_exit(void) +{ + debugfs_remove(mvpp2_root); +} + void mvpp2_dbgfs_cleanup(struct mvpp2 *priv) { debugfs_remove_recursive(priv->dbgfs_dir); @@ -700,10 +707,9 @@ void mvpp2_dbgfs_cleanup(struct mvpp2 *priv) void mvpp2_dbgfs_init(struct mvpp2 *priv, const char *name) { - struct dentry *mvpp2_dir, *mvpp2_root; + struct dentry *mvpp2_dir; int ret, i; - mvpp2_root = debugfs_lookup(MVPP2_DRIVER_NAME, NULL); if (!mvpp2_root) mvpp2_root = debugfs_create_dir(MVPP2_DRIVER_NAME, NULL); diff --git a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c index b84128b549b4..eb0fb8128096 100644 --- a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c +++ b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c @@ -5425,11 +5425,11 @@ mvpp2_ethtool_get_coalesce(struct net_device *dev, static void mvpp2_ethtool_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *drvinfo) { - strlcpy(drvinfo->driver, MVPP2_DRIVER_NAME, + strscpy(drvinfo->driver, MVPP2_DRIVER_NAME, sizeof(drvinfo->driver)); - strlcpy(drvinfo->version, MVPP2_DRIVER_VERSION, + strscpy(drvinfo->version, MVPP2_DRIVER_VERSION, sizeof(drvinfo->version)); - strlcpy(drvinfo->bus_info, dev_name(&dev->dev), + strscpy(drvinfo->bus_info, dev_name(&dev->dev), sizeof(drvinfo->bus_info)); } @@ -5770,8 +5770,7 @@ static int mvpp2_simple_queue_vectors_init(struct mvpp2_port *port, v->irq = irq_of_parse_and_map(port_node, 0); if (v->irq <= 0) return -EINVAL; - netif_napi_add(port->dev, &v->napi, mvpp2_poll, - NAPI_POLL_WEIGHT); + netif_napi_add(port->dev, &v->napi, mvpp2_poll); port->nqvecs = 1; @@ -5831,8 +5830,7 @@ static int mvpp2_multi_queue_vectors_init(struct mvpp2_port *port, goto err; } - netif_napi_add(port->dev, &v->napi, mvpp2_poll, - NAPI_POLL_WEIGHT); + netif_napi_add(port->dev, &v->napi, mvpp2_poll); } return 0; @@ -7706,7 +7704,18 @@ static struct platform_driver mvpp2_driver = { }, }; -module_platform_driver(mvpp2_driver); +static int __init mvpp2_driver_init(void) +{ + return platform_driver_register(&mvpp2_driver); +} +module_init(mvpp2_driver_init); + +static void __exit mvpp2_driver_exit(void) +{ + platform_driver_unregister(&mvpp2_driver); + mvpp2_dbgfs_exit(); +} +module_exit(mvpp2_driver_exit); MODULE_DESCRIPTION("Marvell PPv2 Ethernet Driver - www.marvell.com"); MODULE_AUTHOR("Marcin Wojtas <mw@semihalf.com>"); diff --git a/drivers/net/ethernet/marvell/octeon_ep/octep_main.c b/drivers/net/ethernet/marvell/octeon_ep/octep_main.c index 97f080c66dd4..9089adcb75f9 100644 --- a/drivers/net/ethernet/marvell/octeon_ep/octep_main.c +++ b/drivers/net/ethernet/marvell/octeon_ep/octep_main.c @@ -410,7 +410,7 @@ static void octep_napi_add(struct octep_device *oct) for (i = 0; i < oct->num_oqs; i++) { netdev_dbg(oct->netdev, "Adding NAPI on Q-%d\n", i); netif_napi_add(oct->netdev, &oct->ioq_vector[i]->napi, - octep_napi_poll, 64); + octep_napi_poll); oct->oq[i]->napi = &oct->ioq_vector[i]->napi; } } diff --git a/drivers/net/ethernet/marvell/octeon_ep/octep_rx.c b/drivers/net/ethernet/marvell/octeon_ep/octep_rx.c index d9ae0937d17a..392d9b0da0d7 100644 --- a/drivers/net/ethernet/marvell/octeon_ep/octep_rx.c +++ b/drivers/net/ethernet/marvell/octeon_ep/octep_rx.c @@ -158,8 +158,7 @@ static int octep_setup_oq(struct octep_device *oct, int q_no) goto desc_dma_alloc_err; } - oq->buff_info = (struct octep_rx_buffer *) - vzalloc(oq->max_count * OCTEP_OQ_RECVBUF_SIZE); + oq->buff_info = vzalloc(oq->max_count * OCTEP_OQ_RECVBUF_SIZE); if (unlikely(!oq->buff_info)) { dev_err(&oct->pdev->dev, "Failed to allocate buffer info for OQ-%d\n", q_no); diff --git a/drivers/net/ethernet/marvell/octeontx2/af/Makefile b/drivers/net/ethernet/marvell/octeontx2/af/Makefile index 40203560b291..3cf4c8285c90 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/Makefile +++ b/drivers/net/ethernet/marvell/octeontx2/af/Makefile @@ -11,4 +11,4 @@ rvu_mbox-y := mbox.o rvu_trace.o rvu_af-y := cgx.o rvu.o rvu_cgx.o rvu_npa.o rvu_nix.o \ rvu_reg.o rvu_npc.o rvu_debugfs.o ptp.o rvu_npc_fs.o \ rvu_cpt.o rvu_devlink.o rpm.o rvu_cn10k.o rvu_switch.o \ - rvu_sdp.o rvu_npc_hash.o + rvu_sdp.o rvu_npc_hash.o mcs.o mcs_rvu_if.o mcs_cnf10kb.o diff --git a/drivers/net/ethernet/marvell/octeontx2/af/mbox.h b/drivers/net/ethernet/marvell/octeontx2/af/mbox.h index d7762577e285..8d5d5a0f68c4 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/mbox.h +++ b/drivers/net/ethernet/marvell/octeontx2/af/mbox.h @@ -293,20 +293,74 @@ M(NIX_BANDPROF_ALLOC, 0x801d, nix_bandprof_alloc, nix_bandprof_alloc_req, \ M(NIX_BANDPROF_FREE, 0x801e, nix_bandprof_free, nix_bandprof_free_req, \ msg_rsp) \ M(NIX_BANDPROF_GET_HWINFO, 0x801f, nix_bandprof_get_hwinfo, msg_req, \ - nix_bandprof_get_hwinfo_rsp) - -/* Messages initiated by AF (range 0xC00 - 0xDFF) */ + nix_bandprof_get_hwinfo_rsp) \ +/* MCS mbox IDs (range 0xA000 - 0xBFFF) */ \ +M(MCS_ALLOC_RESOURCES, 0xa000, mcs_alloc_resources, mcs_alloc_rsrc_req, \ + mcs_alloc_rsrc_rsp) \ +M(MCS_FREE_RESOURCES, 0xa001, mcs_free_resources, mcs_free_rsrc_req, msg_rsp) \ +M(MCS_FLOWID_ENTRY_WRITE, 0xa002, mcs_flowid_entry_write, mcs_flowid_entry_write_req, \ + msg_rsp) \ +M(MCS_SECY_PLCY_WRITE, 0xa003, mcs_secy_plcy_write, mcs_secy_plcy_write_req, \ + msg_rsp) \ +M(MCS_RX_SC_CAM_WRITE, 0xa004, mcs_rx_sc_cam_write, mcs_rx_sc_cam_write_req, \ + msg_rsp) \ +M(MCS_SA_PLCY_WRITE, 0xa005, mcs_sa_plcy_write, mcs_sa_plcy_write_req, \ + msg_rsp) \ +M(MCS_TX_SC_SA_MAP_WRITE, 0xa006, mcs_tx_sc_sa_map_write, mcs_tx_sc_sa_map, \ + msg_rsp) \ +M(MCS_RX_SC_SA_MAP_WRITE, 0xa007, mcs_rx_sc_sa_map_write, mcs_rx_sc_sa_map, \ + msg_rsp) \ +M(MCS_FLOWID_ENA_ENTRY, 0xa008, mcs_flowid_ena_entry, mcs_flowid_ena_dis_entry, \ + msg_rsp) \ +M(MCS_PN_TABLE_WRITE, 0xa009, mcs_pn_table_write, mcs_pn_table_write_req, \ + msg_rsp) \ +M(MCS_SET_ACTIVE_LMAC, 0xa00a, mcs_set_active_lmac, mcs_set_active_lmac, \ + msg_rsp) \ +M(MCS_GET_HW_INFO, 0xa00b, mcs_get_hw_info, msg_req, mcs_hw_info) \ +M(MCS_GET_FLOWID_STATS, 0xa00c, mcs_get_flowid_stats, mcs_stats_req, \ + mcs_flowid_stats) \ +M(MCS_GET_SECY_STATS, 0xa00d, mcs_get_secy_stats, mcs_stats_req, \ + mcs_secy_stats) \ +M(MCS_GET_SC_STATS, 0xa00e, mcs_get_sc_stats, mcs_stats_req, mcs_sc_stats) \ +M(MCS_GET_SA_STATS, 0xa00f, mcs_get_sa_stats, mcs_stats_req, mcs_sa_stats) \ +M(MCS_GET_PORT_STATS, 0xa010, mcs_get_port_stats, mcs_stats_req, \ + mcs_port_stats) \ +M(MCS_CLEAR_STATS, 0xa011, mcs_clear_stats, mcs_clear_stats, msg_rsp) \ +M(MCS_INTR_CFG, 0xa012, mcs_intr_cfg, mcs_intr_cfg, msg_rsp) \ +M(MCS_SET_LMAC_MODE, 0xa013, mcs_set_lmac_mode, mcs_set_lmac_mode, msg_rsp) \ +M(MCS_SET_PN_THRESHOLD, 0xa014, mcs_set_pn_threshold, mcs_set_pn_threshold, \ + msg_rsp) \ +M(MCS_ALLOC_CTRL_PKT_RULE, 0xa015, mcs_alloc_ctrl_pkt_rule, \ + mcs_alloc_ctrl_pkt_rule_req, \ + mcs_alloc_ctrl_pkt_rule_rsp) \ +M(MCS_FREE_CTRL_PKT_RULE, 0xa016, mcs_free_ctrl_pkt_rule, \ + mcs_free_ctrl_pkt_rule_req, msg_rsp) \ +M(MCS_CTRL_PKT_RULE_WRITE, 0xa017, mcs_ctrl_pkt_rule_write, \ + mcs_ctrl_pkt_rule_write_req, msg_rsp) \ +M(MCS_PORT_RESET, 0xa018, mcs_port_reset, mcs_port_reset_req, msg_rsp) \ +M(MCS_PORT_CFG_SET, 0xa019, mcs_port_cfg_set, mcs_port_cfg_set_req, msg_rsp)\ +M(MCS_PORT_CFG_GET, 0xa020, mcs_port_cfg_get, mcs_port_cfg_get_req, \ + mcs_port_cfg_get_rsp) \ +M(MCS_CUSTOM_TAG_CFG_GET, 0xa021, mcs_custom_tag_cfg_get, \ + mcs_custom_tag_cfg_get_req, \ + mcs_custom_tag_cfg_get_rsp) + +/* Messages initiated by AF (range 0xC00 - 0xEFF) */ #define MBOX_UP_CGX_MESSAGES \ M(CGX_LINK_EVENT, 0xC00, cgx_link_event, cgx_link_info_msg, msg_rsp) #define MBOX_UP_CPT_MESSAGES \ M(CPT_INST_LMTST, 0xD00, cpt_inst_lmtst, cpt_inst_lmtst_req, msg_rsp) +#define MBOX_UP_MCS_MESSAGES \ +M(MCS_INTR_NOTIFY, 0xE00, mcs_intr_notify, mcs_intr_info, msg_rsp) + enum { #define M(_name, _id, _1, _2, _3) MBOX_MSG_ ## _name = _id, MBOX_MESSAGES MBOX_UP_CGX_MESSAGES MBOX_UP_CPT_MESSAGES +MBOX_UP_MCS_MESSAGES #undef M }; @@ -1471,6 +1525,7 @@ enum ptp_op { PTP_OP_GET_CLOCK = 1, PTP_OP_GET_TSTMP = 2, PTP_OP_SET_THRESH = 3, + PTP_OP_EXTTS_ON = 4, }; struct ptp_req { @@ -1478,6 +1533,7 @@ struct ptp_req { u8 op; s64 scaled_ppm; u64 thresh; + int extts_on; }; struct ptp_rsp { @@ -1655,4 +1711,415 @@ enum cgx_af_status { LMAC_AF_ERR_EXACT_MATCH_TBL_LOOK_UP_FAILED = -1110, }; +enum mcs_direction { + MCS_RX, + MCS_TX, +}; + +enum mcs_rsrc_type { + MCS_RSRC_TYPE_FLOWID, + MCS_RSRC_TYPE_SECY, + MCS_RSRC_TYPE_SC, + MCS_RSRC_TYPE_SA, +}; + +struct mcs_alloc_rsrc_req { + struct mbox_msghdr hdr; + u8 rsrc_type; + u8 rsrc_cnt; /* Resources count */ + u8 mcs_id; /* MCS block ID */ + u8 dir; /* Macsec ingress or egress side */ + u8 all; /* Allocate all resource type one each */ + u64 rsvd; +}; + +struct mcs_alloc_rsrc_rsp { + struct mbox_msghdr hdr; + u8 flow_ids[128]; /* Index of reserved entries */ + u8 secy_ids[128]; + u8 sc_ids[128]; + u8 sa_ids[256]; + u8 rsrc_type; + u8 rsrc_cnt; /* No of entries reserved */ + u8 mcs_id; + u8 dir; + u8 all; + u8 rsvd[256]; /* reserved fields for future expansion */ +}; + +struct mcs_free_rsrc_req { + struct mbox_msghdr hdr; + u8 rsrc_id; /* Index of the entry to be freed */ + u8 rsrc_type; + u8 mcs_id; + u8 dir; + u8 all; /* Free all the cam resources */ + u64 rsvd; +}; + +struct mcs_flowid_entry_write_req { + struct mbox_msghdr hdr; + u64 data[4]; + u64 mask[4]; + u64 sci; /* CNF10K-B for tx_secy_mem_map */ + u8 flow_id; + u8 secy_id; /* secyid for which flowid is mapped */ + u8 sc_id; /* Valid if dir = MCS_TX, SC_CAM id mapped to flowid */ + u8 ena; /* Enable tcam entry */ + u8 ctrl_pkt; + u8 mcs_id; + u8 dir; + u64 rsvd; +}; + +struct mcs_secy_plcy_write_req { + struct mbox_msghdr hdr; + u64 plcy; + u8 secy_id; + u8 mcs_id; + u8 dir; + u64 rsvd; +}; + +/* RX SC_CAM mapping */ +struct mcs_rx_sc_cam_write_req { + struct mbox_msghdr hdr; + u64 sci; /* SCI */ + u64 secy_id; /* secy index mapped to SC */ + u8 sc_id; /* SC CAM entry index */ + u8 mcs_id; + u64 rsvd; +}; + +struct mcs_sa_plcy_write_req { + struct mbox_msghdr hdr; + u64 plcy[2][9]; /* Support 2 SA policy */ + u8 sa_index[2]; + u8 sa_cnt; + u8 mcs_id; + u8 dir; + u64 rsvd; +}; + +struct mcs_tx_sc_sa_map { + struct mbox_msghdr hdr; + u8 sa_index0; + u8 sa_index1; + u8 rekey_ena; + u8 sa_index0_vld; + u8 sa_index1_vld; + u8 tx_sa_active; + u64 sectag_sci; + u8 sc_id; /* used as index for SA_MEM_MAP */ + u8 mcs_id; + u64 rsvd; +}; + +struct mcs_rx_sc_sa_map { + struct mbox_msghdr hdr; + u8 sa_index; + u8 sa_in_use; + u8 sc_id; + u8 an; /* value range 0-3, sc_id + an used as index SA_MEM_MAP */ + u8 mcs_id; + u64 rsvd; +}; + +struct mcs_flowid_ena_dis_entry { + struct mbox_msghdr hdr; + u8 flow_id; + u8 ena; + u8 mcs_id; + u8 dir; + u64 rsvd; +}; + +struct mcs_pn_table_write_req { + struct mbox_msghdr hdr; + u64 next_pn; + u8 pn_id; + u8 mcs_id; + u8 dir; + u64 rsvd; +}; + +struct mcs_hw_info { + struct mbox_msghdr hdr; + u8 num_mcs_blks; /* Number of MCS blocks */ + u8 tcam_entries; /* RX/TX Tcam entries per mcs block */ + u8 secy_entries; /* RX/TX SECY entries per mcs block */ + u8 sc_entries; /* RX/TX SC CAM entries per mcs block */ + u8 sa_entries; /* PN table entries = SA entries */ + u64 rsvd[16]; +}; + +struct mcs_set_active_lmac { + struct mbox_msghdr hdr; + u32 lmac_bmap; /* bitmap of active lmac per mcs block */ + u8 mcs_id; + u16 chan_base; /* MCS channel base */ + u64 rsvd; +}; + +struct mcs_set_lmac_mode { + struct mbox_msghdr hdr; + u8 mode; /* 1:Bypass 0:Operational */ + u8 lmac_id; + u8 mcs_id; + u64 rsvd; +}; + +struct mcs_port_reset_req { + struct mbox_msghdr hdr; + u8 reset; + u8 mcs_id; + u8 port_id; + u64 rsvd; +}; + +struct mcs_port_cfg_set_req { + struct mbox_msghdr hdr; + u8 cstm_tag_rel_mode_sel; + u8 custom_hdr_enb; + u8 fifo_skid; + u8 port_mode; + u8 port_id; + u8 mcs_id; + u64 rsvd; +}; + +struct mcs_port_cfg_get_req { + struct mbox_msghdr hdr; + u8 port_id; + u8 mcs_id; + u64 rsvd; +}; + +struct mcs_port_cfg_get_rsp { + struct mbox_msghdr hdr; + u8 cstm_tag_rel_mode_sel; + u8 custom_hdr_enb; + u8 fifo_skid; + u8 port_mode; + u8 port_id; + u8 mcs_id; + u64 rsvd; +}; + +struct mcs_custom_tag_cfg_get_req { + struct mbox_msghdr hdr; + u8 mcs_id; + u8 dir; + u64 rsvd; +}; + +struct mcs_custom_tag_cfg_get_rsp { + struct mbox_msghdr hdr; + u16 cstm_etype[8]; + u8 cstm_indx[8]; + u8 cstm_etype_en; + u8 mcs_id; + u8 dir; + u64 rsvd; +}; + +/* MCS mailbox error codes + * Range 1201 - 1300. + */ +enum mcs_af_status { + MCS_AF_ERR_INVALID_MCSID = -1201, + MCS_AF_ERR_NOT_MAPPED = -1202, +}; + +struct mcs_set_pn_threshold { + struct mbox_msghdr hdr; + u64 threshold; + u8 xpn; /* '1' for setting xpn threshold */ + u8 mcs_id; + u8 dir; + u64 rsvd; +}; + +enum mcs_ctrl_pkt_rulew_type { + MCS_CTRL_PKT_RULE_TYPE_ETH, + MCS_CTRL_PKT_RULE_TYPE_DA, + MCS_CTRL_PKT_RULE_TYPE_RANGE, + MCS_CTRL_PKT_RULE_TYPE_COMBO, + MCS_CTRL_PKT_RULE_TYPE_MAC, +}; + +struct mcs_alloc_ctrl_pkt_rule_req { + struct mbox_msghdr hdr; + u8 rule_type; + u8 mcs_id; /* MCS block ID */ + u8 dir; /* Macsec ingress or egress side */ + u64 rsvd; +}; + +struct mcs_alloc_ctrl_pkt_rule_rsp { + struct mbox_msghdr hdr; + u8 rule_idx; + u8 rule_type; + u8 mcs_id; + u8 dir; + u64 rsvd; +}; + +struct mcs_free_ctrl_pkt_rule_req { + struct mbox_msghdr hdr; + u8 rule_idx; + u8 rule_type; + u8 mcs_id; + u8 dir; + u8 all; + u64 rsvd; +}; + +struct mcs_ctrl_pkt_rule_write_req { + struct mbox_msghdr hdr; + u64 data0; + u64 data1; + u64 data2; + u8 rule_idx; + u8 rule_type; + u8 mcs_id; + u8 dir; + u64 rsvd; +}; + +struct mcs_stats_req { + struct mbox_msghdr hdr; + u8 id; + u8 mcs_id; + u8 dir; + u64 rsvd; +}; + +struct mcs_flowid_stats { + struct mbox_msghdr hdr; + u64 tcam_hit_cnt; + u64 rsvd; +}; + +struct mcs_secy_stats { + struct mbox_msghdr hdr; + u64 ctl_pkt_bcast_cnt; + u64 ctl_pkt_mcast_cnt; + u64 ctl_pkt_ucast_cnt; + u64 ctl_octet_cnt; + u64 unctl_pkt_bcast_cnt; + u64 unctl_pkt_mcast_cnt; + u64 unctl_pkt_ucast_cnt; + u64 unctl_octet_cnt; + /* Valid only for RX */ + u64 octet_decrypted_cnt; + u64 octet_validated_cnt; + u64 pkt_port_disabled_cnt; + u64 pkt_badtag_cnt; + u64 pkt_nosa_cnt; + u64 pkt_nosaerror_cnt; + u64 pkt_tagged_ctl_cnt; + u64 pkt_untaged_cnt; + u64 pkt_ctl_cnt; /* CN10K-B */ + u64 pkt_notag_cnt; /* CNF10K-B */ + /* Valid only for TX */ + u64 octet_encrypted_cnt; + u64 octet_protected_cnt; + u64 pkt_noactivesa_cnt; + u64 pkt_toolong_cnt; + u64 pkt_untagged_cnt; + u64 rsvd[4]; +}; + +struct mcs_port_stats { + struct mbox_msghdr hdr; + u64 tcam_miss_cnt; + u64 parser_err_cnt; + u64 preempt_err_cnt; /* CNF10K-B */ + u64 sectag_insert_err_cnt; + u64 rsvd[4]; +}; + +/* Only for CN10K-B */ +struct mcs_sa_stats { + struct mbox_msghdr hdr; + /* RX */ + u64 pkt_invalid_cnt; + u64 pkt_nosaerror_cnt; + u64 pkt_notvalid_cnt; + u64 pkt_ok_cnt; + u64 pkt_nosa_cnt; + /* TX */ + u64 pkt_encrypt_cnt; + u64 pkt_protected_cnt; + u64 rsvd[4]; +}; + +struct mcs_sc_stats { + struct mbox_msghdr hdr; + /* RX */ + u64 hit_cnt; + u64 pkt_invalid_cnt; + u64 pkt_late_cnt; + u64 pkt_notvalid_cnt; + u64 pkt_unchecked_cnt; + u64 pkt_delay_cnt; /* CNF10K-B */ + u64 pkt_ok_cnt; /* CNF10K-B */ + u64 octet_decrypt_cnt; /* CN10K-B */ + u64 octet_validate_cnt; /* CN10K-B */ + /* TX */ + u64 pkt_encrypt_cnt; + u64 pkt_protected_cnt; + u64 octet_encrypt_cnt; /* CN10K-B */ + u64 octet_protected_cnt; /* CN10K-B */ + u64 rsvd[4]; +}; + +struct mcs_clear_stats { + struct mbox_msghdr hdr; +#define MCS_FLOWID_STATS 0 +#define MCS_SECY_STATS 1 +#define MCS_SC_STATS 2 +#define MCS_SA_STATS 3 +#define MCS_PORT_STATS 4 + u8 type; /* FLOWID, SECY, SC, SA, PORT */ + u8 id; /* type = PORT, If id = FF(invalid) port no is derived from pcifunc */ + u8 mcs_id; + u8 dir; + u8 all; /* All resources stats mapped to PF are cleared */ +}; + +struct mcs_intr_cfg { + struct mbox_msghdr hdr; +#define MCS_CPM_RX_SECTAG_V_EQ1_INT BIT_ULL(0) +#define MCS_CPM_RX_SECTAG_E_EQ0_C_EQ1_INT BIT_ULL(1) +#define MCS_CPM_RX_SECTAG_SL_GTE48_INT BIT_ULL(2) +#define MCS_CPM_RX_SECTAG_ES_EQ1_SC_EQ1_INT BIT_ULL(3) +#define MCS_CPM_RX_SECTAG_SC_EQ1_SCB_EQ1_INT BIT_ULL(4) +#define MCS_CPM_RX_PACKET_XPN_EQ0_INT BIT_ULL(5) +#define MCS_CPM_RX_PN_THRESH_REACHED_INT BIT_ULL(6) +#define MCS_CPM_TX_PACKET_XPN_EQ0_INT BIT_ULL(7) +#define MCS_CPM_TX_PN_THRESH_REACHED_INT BIT_ULL(8) +#define MCS_CPM_TX_SA_NOT_VALID_INT BIT_ULL(9) +#define MCS_BBE_RX_DFIFO_OVERFLOW_INT BIT_ULL(10) +#define MCS_BBE_RX_PLFIFO_OVERFLOW_INT BIT_ULL(11) +#define MCS_BBE_TX_DFIFO_OVERFLOW_INT BIT_ULL(12) +#define MCS_BBE_TX_PLFIFO_OVERFLOW_INT BIT_ULL(13) +#define MCS_PAB_RX_CHAN_OVERFLOW_INT BIT_ULL(14) +#define MCS_PAB_TX_CHAN_OVERFLOW_INT BIT_ULL(15) + u64 intr_mask; /* Interrupt enable mask */ + u8 mcs_id; + u8 lmac_id; + u64 rsvd; +}; + +struct mcs_intr_info { + struct mbox_msghdr hdr; + u64 intr_mask; + int sa_id; + u8 mcs_id; + u8 lmac_id; + u64 rsvd; +}; + #endif /* MBOX_H */ diff --git a/drivers/net/ethernet/marvell/octeontx2/af/mcs.c b/drivers/net/ethernet/marvell/octeontx2/af/mcs.c new file mode 100644 index 000000000000..5ba618aed6ad --- /dev/null +++ b/drivers/net/ethernet/marvell/octeontx2/af/mcs.c @@ -0,0 +1,1601 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Marvell MCS driver + * + * Copyright (C) 2022 Marvell. + */ + +#include <linux/bitfield.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/module.h> +#include <linux/pci.h> + +#include "mcs.h" +#include "mcs_reg.h" + +#define DRV_NAME "Marvell MCS Driver" + +#define PCI_CFG_REG_BAR_NUM 0 + +static const struct pci_device_id mcs_id_table[] = { + { PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, PCI_DEVID_CN10K_MCS) }, + { 0, } /* end of table */ +}; + +static LIST_HEAD(mcs_list); + +void mcs_get_tx_secy_stats(struct mcs *mcs, struct mcs_secy_stats *stats, int id) +{ + u64 reg; + + reg = MCSX_CSE_TX_MEM_SLAVE_IFOUTCTLBCPKTSX(id); + stats->ctl_pkt_bcast_cnt = mcs_reg_read(mcs, reg); + + reg = MCSX_CSE_TX_MEM_SLAVE_IFOUTCTLMCPKTSX(id); + stats->ctl_pkt_mcast_cnt = mcs_reg_read(mcs, reg); + + reg = MCSX_CSE_TX_MEM_SLAVE_IFOUTCTLOCTETSX(id); + stats->ctl_octet_cnt = mcs_reg_read(mcs, reg); + + reg = MCSX_CSE_TX_MEM_SLAVE_IFOUTCTLUCPKTSX(id); + stats->ctl_pkt_ucast_cnt = mcs_reg_read(mcs, reg); + + reg = MCSX_CSE_TX_MEM_SLAVE_IFOUTUNCTLBCPKTSX(id); + stats->unctl_pkt_bcast_cnt = mcs_reg_read(mcs, reg); + + reg = MCSX_CSE_TX_MEM_SLAVE_IFOUTUNCTLMCPKTSX(id); + stats->unctl_pkt_mcast_cnt = mcs_reg_read(mcs, reg); + + reg = MCSX_CSE_TX_MEM_SLAVE_IFOUTUNCTLOCTETSX(id); + stats->unctl_octet_cnt = mcs_reg_read(mcs, reg); + + reg = MCSX_CSE_TX_MEM_SLAVE_IFOUTUNCTLUCPKTSX(id); + stats->unctl_pkt_ucast_cnt = mcs_reg_read(mcs, reg); + + reg = MCSX_CSE_TX_MEM_SLAVE_OUTOCTETSSECYENCRYPTEDX(id); + stats->octet_encrypted_cnt = mcs_reg_read(mcs, reg); + + reg = MCSX_CSE_TX_MEM_SLAVE_OUTOCTETSSECYPROTECTEDX(id); + stats->octet_protected_cnt = mcs_reg_read(mcs, reg); + + reg = MCSX_CSE_TX_MEM_SLAVE_OUTPKTSSECYNOACTIVESAX(id); + stats->pkt_noactivesa_cnt = mcs_reg_read(mcs, reg); + + reg = MCSX_CSE_TX_MEM_SLAVE_OUTPKTSSECYTOOLONGX(id); + stats->pkt_toolong_cnt = mcs_reg_read(mcs, reg); + + reg = MCSX_CSE_TX_MEM_SLAVE_OUTPKTSSECYUNTAGGEDX(id); + stats->pkt_untagged_cnt = mcs_reg_read(mcs, reg); +} + +void mcs_get_rx_secy_stats(struct mcs *mcs, struct mcs_secy_stats *stats, int id) +{ + u64 reg; + + reg = MCSX_CSE_RX_MEM_SLAVE_IFINCTLBCPKTSX(id); + stats->ctl_pkt_bcast_cnt = mcs_reg_read(mcs, reg); + + reg = MCSX_CSE_RX_MEM_SLAVE_IFINCTLMCPKTSX(id); + stats->ctl_pkt_mcast_cnt = mcs_reg_read(mcs, reg); + + reg = MCSX_CSE_RX_MEM_SLAVE_IFINCTLOCTETSX(id); + stats->ctl_octet_cnt = mcs_reg_read(mcs, reg); + + reg = MCSX_CSE_RX_MEM_SLAVE_IFINCTLUCPKTSX(id); + stats->ctl_pkt_ucast_cnt = mcs_reg_read(mcs, reg); + + reg = MCSX_CSE_RX_MEM_SLAVE_IFINUNCTLBCPKTSX(id); + stats->unctl_pkt_bcast_cnt = mcs_reg_read(mcs, reg); + + reg = MCSX_CSE_RX_MEM_SLAVE_IFINUNCTLMCPKTSX(id); + stats->unctl_pkt_mcast_cnt = mcs_reg_read(mcs, reg); + + reg = MCSX_CSE_RX_MEM_SLAVE_IFINUNCTLOCTETSX(id); + stats->unctl_octet_cnt = mcs_reg_read(mcs, reg); + + reg = MCSX_CSE_RX_MEM_SLAVE_IFINUNCTLUCPKTSX(id); + stats->unctl_pkt_ucast_cnt = mcs_reg_read(mcs, reg); + + reg = MCSX_CSE_RX_MEM_SLAVE_INOCTETSSECYDECRYPTEDX(id); + stats->octet_decrypted_cnt = mcs_reg_read(mcs, reg); + + reg = MCSX_CSE_RX_MEM_SLAVE_INOCTETSSECYVALIDATEX(id); + stats->octet_validated_cnt = mcs_reg_read(mcs, reg); + + reg = MCSX_CSE_RX_MEM_SLAVE_INPKTSCTRLPORTDISABLEDX(id); + stats->pkt_port_disabled_cnt = mcs_reg_read(mcs, reg); + + reg = MCSX_CSE_RX_MEM_SLAVE_INPKTSSECYBADTAGX(id); + stats->pkt_badtag_cnt = mcs_reg_read(mcs, reg); + + reg = MCSX_CSE_RX_MEM_SLAVE_INPKTSSECYNOSAX(id); + stats->pkt_nosa_cnt = mcs_reg_read(mcs, reg); + + reg = MCSX_CSE_RX_MEM_SLAVE_INPKTSSECYNOSAERRORX(id); + stats->pkt_nosaerror_cnt = mcs_reg_read(mcs, reg); + + reg = MCSX_CSE_RX_MEM_SLAVE_INPKTSSECYTAGGEDCTLX(id); + stats->pkt_tagged_ctl_cnt = mcs_reg_read(mcs, reg); + + reg = MCSX_CSE_RX_MEM_SLAVE_INPKTSSECYUNTAGGEDORNOTAGX(id); + stats->pkt_untaged_cnt = mcs_reg_read(mcs, reg); + + reg = MCSX_CSE_RX_MEM_SLAVE_INPKTSSECYCTLX(id); + stats->pkt_ctl_cnt = mcs_reg_read(mcs, reg); + + if (mcs->hw->mcs_blks > 1) { + reg = MCSX_CSE_RX_MEM_SLAVE_INPKTSSECYNOTAGX(id); + stats->pkt_notag_cnt = mcs_reg_read(mcs, reg); + } +} + +void mcs_get_flowid_stats(struct mcs *mcs, struct mcs_flowid_stats *stats, + int id, int dir) +{ + u64 reg; + + if (dir == MCS_RX) + reg = MCSX_CSE_RX_MEM_SLAVE_INPKTSFLOWIDTCAMHITX(id); + else + reg = MCSX_CSE_TX_MEM_SLAVE_OUTPKTSFLOWIDTCAMHITX(id); + + stats->tcam_hit_cnt = mcs_reg_read(mcs, reg); +} + +void mcs_get_port_stats(struct mcs *mcs, struct mcs_port_stats *stats, + int id, int dir) +{ + u64 reg; + + if (dir == MCS_RX) { + reg = MCSX_CSE_RX_MEM_SLAVE_INPKTSFLOWIDTCAMMISSX(id); + stats->tcam_miss_cnt = mcs_reg_read(mcs, reg); + + reg = MCSX_CSE_RX_MEM_SLAVE_INPKTSPARSEERRX(id); + stats->parser_err_cnt = mcs_reg_read(mcs, reg); + if (mcs->hw->mcs_blks > 1) { + reg = MCSX_CSE_RX_MEM_SLAVE_INPKTSEARLYPREEMPTERRX(id); + stats->preempt_err_cnt = mcs_reg_read(mcs, reg); + } + } else { + reg = MCSX_CSE_TX_MEM_SLAVE_OUTPKTSFLOWIDTCAMMISSX(id); + stats->tcam_miss_cnt = mcs_reg_read(mcs, reg); + + reg = MCSX_CSE_TX_MEM_SLAVE_OUTPKTSPARSEERRX(id); + stats->parser_err_cnt = mcs_reg_read(mcs, reg); + + reg = MCSX_CSE_TX_MEM_SLAVE_OUTPKTSSECTAGINSERTIONERRX(id); + stats->sectag_insert_err_cnt = mcs_reg_read(mcs, reg); + } +} + +void mcs_get_sa_stats(struct mcs *mcs, struct mcs_sa_stats *stats, int id, int dir) +{ + u64 reg; + + if (dir == MCS_RX) { + reg = MCSX_CSE_RX_MEM_SLAVE_INPKTSSAINVALIDX(id); + stats->pkt_invalid_cnt = mcs_reg_read(mcs, reg); + + reg = MCSX_CSE_RX_MEM_SLAVE_INPKTSSANOTUSINGSAERRORX(id); + stats->pkt_nosaerror_cnt = mcs_reg_read(mcs, reg); + + reg = MCSX_CSE_RX_MEM_SLAVE_INPKTSSANOTVALIDX(id); + stats->pkt_notvalid_cnt = mcs_reg_read(mcs, reg); + + reg = MCSX_CSE_RX_MEM_SLAVE_INPKTSSAOKX(id); + stats->pkt_ok_cnt = mcs_reg_read(mcs, reg); + + reg = MCSX_CSE_RX_MEM_SLAVE_INPKTSSAUNUSEDSAX(id); + stats->pkt_nosa_cnt = mcs_reg_read(mcs, reg); + } else { + reg = MCSX_CSE_TX_MEM_SLAVE_OUTPKTSSAENCRYPTEDX(id); + stats->pkt_encrypt_cnt = mcs_reg_read(mcs, reg); + + reg = MCSX_CSE_TX_MEM_SLAVE_OUTPKTSSAPROTECTEDX(id); + stats->pkt_protected_cnt = mcs_reg_read(mcs, reg); + } +} + +void mcs_get_sc_stats(struct mcs *mcs, struct mcs_sc_stats *stats, + int id, int dir) +{ + u64 reg; + + if (dir == MCS_RX) { + reg = MCSX_CSE_RX_MEM_SLAVE_INPKTSSCCAMHITX(id); + stats->hit_cnt = mcs_reg_read(mcs, reg); + + reg = MCSX_CSE_RX_MEM_SLAVE_INPKTSSCINVALIDX(id); + stats->pkt_invalid_cnt = mcs_reg_read(mcs, reg); + + reg = MCSX_CSE_RX_MEM_SLAVE_INPKTSSCLATEORDELAYEDX(id); + stats->pkt_late_cnt = mcs_reg_read(mcs, reg); + + reg = MCSX_CSE_RX_MEM_SLAVE_INPKTSSCNOTVALIDX(id); + stats->pkt_notvalid_cnt = mcs_reg_read(mcs, reg); + + reg = MCSX_CSE_RX_MEM_SLAVE_INPKTSSCUNCHECKEDOROKX(id); + stats->pkt_unchecked_cnt = mcs_reg_read(mcs, reg); + + if (mcs->hw->mcs_blks > 1) { + reg = MCSX_CSE_RX_MEM_SLAVE_INPKTSSCDELAYEDX(id); + stats->pkt_delay_cnt = mcs_reg_read(mcs, reg); + + reg = MCSX_CSE_RX_MEM_SLAVE_INPKTSSCOKX(id); + stats->pkt_ok_cnt = mcs_reg_read(mcs, reg); + } + if (mcs->hw->mcs_blks == 1) { + reg = MCSX_CSE_RX_MEM_SLAVE_INOCTETSSCDECRYPTEDX(id); + stats->octet_decrypt_cnt = mcs_reg_read(mcs, reg); + + reg = MCSX_CSE_RX_MEM_SLAVE_INOCTETSSCVALIDATEX(id); + stats->octet_validate_cnt = mcs_reg_read(mcs, reg); + } + } else { + reg = MCSX_CSE_TX_MEM_SLAVE_OUTPKTSSCENCRYPTEDX(id); + stats->pkt_encrypt_cnt = mcs_reg_read(mcs, reg); + + reg = MCSX_CSE_TX_MEM_SLAVE_OUTPKTSSCPROTECTEDX(id); + stats->pkt_protected_cnt = mcs_reg_read(mcs, reg); + + if (mcs->hw->mcs_blks == 1) { + reg = MCSX_CSE_TX_MEM_SLAVE_OUTOCTETSSCENCRYPTEDX(id); + stats->octet_encrypt_cnt = mcs_reg_read(mcs, reg); + + reg = MCSX_CSE_TX_MEM_SLAVE_OUTOCTETSSCPROTECTEDX(id); + stats->octet_protected_cnt = mcs_reg_read(mcs, reg); + } + } +} + +void mcs_clear_stats(struct mcs *mcs, u8 type, u8 id, int dir) +{ + struct mcs_flowid_stats flowid_st; + struct mcs_port_stats port_st; + struct mcs_secy_stats secy_st; + struct mcs_sc_stats sc_st; + struct mcs_sa_stats sa_st; + u64 reg; + + if (dir == MCS_RX) + reg = MCSX_CSE_RX_SLAVE_CTRL; + else + reg = MCSX_CSE_TX_SLAVE_CTRL; + + mcs_reg_write(mcs, reg, BIT_ULL(0)); + + switch (type) { + case MCS_FLOWID_STATS: + mcs_get_flowid_stats(mcs, &flowid_st, id, dir); + break; + case MCS_SECY_STATS: + if (dir == MCS_RX) + mcs_get_rx_secy_stats(mcs, &secy_st, id); + else + mcs_get_tx_secy_stats(mcs, &secy_st, id); + break; + case MCS_SC_STATS: + mcs_get_sc_stats(mcs, &sc_st, id, dir); + break; + case MCS_SA_STATS: + mcs_get_sa_stats(mcs, &sa_st, id, dir); + break; + case MCS_PORT_STATS: + mcs_get_port_stats(mcs, &port_st, id, dir); + break; + } + + mcs_reg_write(mcs, reg, 0x0); +} + +int mcs_clear_all_stats(struct mcs *mcs, u16 pcifunc, int dir) +{ + struct mcs_rsrc_map *map; + int id; + + if (dir == MCS_RX) + map = &mcs->rx; + else + map = &mcs->tx; + + /* Clear FLOWID stats */ + for (id = 0; id < map->flow_ids.max; id++) { + if (map->flowid2pf_map[id] != pcifunc) + continue; + mcs_clear_stats(mcs, MCS_FLOWID_STATS, id, dir); + } + + /* Clear SECY stats */ + for (id = 0; id < map->secy.max; id++) { + if (map->secy2pf_map[id] != pcifunc) + continue; + mcs_clear_stats(mcs, MCS_SECY_STATS, id, dir); + } + + /* Clear SC stats */ + for (id = 0; id < map->secy.max; id++) { + if (map->sc2pf_map[id] != pcifunc) + continue; + mcs_clear_stats(mcs, MCS_SC_STATS, id, dir); + } + + /* Clear SA stats */ + for (id = 0; id < map->sa.max; id++) { + if (map->sa2pf_map[id] != pcifunc) + continue; + mcs_clear_stats(mcs, MCS_SA_STATS, id, dir); + } + return 0; +} + +void mcs_pn_table_write(struct mcs *mcs, u8 pn_id, u64 next_pn, u8 dir) +{ + u64 reg; + + if (dir == MCS_RX) + reg = MCSX_CPM_RX_SLAVE_SA_PN_TABLE_MEMX(pn_id); + else + reg = MCSX_CPM_TX_SLAVE_SA_PN_TABLE_MEMX(pn_id); + mcs_reg_write(mcs, reg, next_pn); +} + +void cn10kb_mcs_tx_sa_mem_map_write(struct mcs *mcs, struct mcs_tx_sc_sa_map *map) +{ + u64 reg, val; + + val = (map->sa_index0 & 0xFF) | + (map->sa_index1 & 0xFF) << 9 | + (map->rekey_ena & 0x1) << 18 | + (map->sa_index0_vld & 0x1) << 19 | + (map->sa_index1_vld & 0x1) << 20 | + (map->tx_sa_active & 0x1) << 21 | + map->sectag_sci << 22; + reg = MCSX_CPM_TX_SLAVE_SA_MAP_MEM_0X(map->sc_id); + mcs_reg_write(mcs, reg, val); + + val = map->sectag_sci >> 42; + reg = MCSX_CPM_TX_SLAVE_SA_MAP_MEM_1X(map->sc_id); + mcs_reg_write(mcs, reg, val); +} + +void cn10kb_mcs_rx_sa_mem_map_write(struct mcs *mcs, struct mcs_rx_sc_sa_map *map) +{ + u64 val, reg; + + val = (map->sa_index & 0xFF) | map->sa_in_use << 9; + + reg = MCSX_CPM_RX_SLAVE_SA_MAP_MEMX((4 * map->sc_id) + map->an); + mcs_reg_write(mcs, reg, val); +} + +void mcs_sa_plcy_write(struct mcs *mcs, u64 *plcy, int sa_id, int dir) +{ + int reg_id; + u64 reg; + + if (dir == MCS_RX) { + for (reg_id = 0; reg_id < 8; reg_id++) { + reg = MCSX_CPM_RX_SLAVE_SA_PLCY_MEMX(reg_id, sa_id); + mcs_reg_write(mcs, reg, plcy[reg_id]); + } + } else { + for (reg_id = 0; reg_id < 9; reg_id++) { + reg = MCSX_CPM_TX_SLAVE_SA_PLCY_MEMX(reg_id, sa_id); + mcs_reg_write(mcs, reg, plcy[reg_id]); + } + } +} + +void mcs_ena_dis_sc_cam_entry(struct mcs *mcs, int sc_id, int ena) +{ + u64 reg, val; + + reg = MCSX_CPM_RX_SLAVE_SC_CAM_ENA(0); + if (sc_id > 63) + reg = MCSX_CPM_RX_SLAVE_SC_CAM_ENA(1); + + if (ena) + val = mcs_reg_read(mcs, reg) | BIT_ULL(sc_id); + else + val = mcs_reg_read(mcs, reg) & ~BIT_ULL(sc_id); + + mcs_reg_write(mcs, reg, val); +} + +void mcs_rx_sc_cam_write(struct mcs *mcs, u64 sci, u64 secy, int sc_id) +{ + mcs_reg_write(mcs, MCSX_CPM_RX_SLAVE_SC_CAMX(0, sc_id), sci); + mcs_reg_write(mcs, MCSX_CPM_RX_SLAVE_SC_CAMX(1, sc_id), secy); + /* Enable SC CAM */ + mcs_ena_dis_sc_cam_entry(mcs, sc_id, true); +} + +void mcs_secy_plcy_write(struct mcs *mcs, u64 plcy, int secy_id, int dir) +{ + u64 reg; + + if (dir == MCS_RX) + reg = MCSX_CPM_RX_SLAVE_SECY_PLCY_MEM_0X(secy_id); + else + reg = MCSX_CPM_TX_SLAVE_SECY_PLCY_MEMX(secy_id); + + mcs_reg_write(mcs, reg, plcy); + + if (mcs->hw->mcs_blks == 1 && dir == MCS_RX) + mcs_reg_write(mcs, MCSX_CPM_RX_SLAVE_SECY_PLCY_MEM_1X(secy_id), 0x0ull); +} + +void cn10kb_mcs_flowid_secy_map(struct mcs *mcs, struct secy_mem_map *map, int dir) +{ + u64 reg, val; + + val = (map->secy & 0x7F) | (map->ctrl_pkt & 0x1) << 8; + if (dir == MCS_RX) { + reg = MCSX_CPM_RX_SLAVE_SECY_MAP_MEMX(map->flow_id); + } else { + val |= (map->sc & 0x7F) << 9; + reg = MCSX_CPM_TX_SLAVE_SECY_MAP_MEM_0X(map->flow_id); + } + + mcs_reg_write(mcs, reg, val); +} + +void mcs_ena_dis_flowid_entry(struct mcs *mcs, int flow_id, int dir, int ena) +{ + u64 reg, val; + + if (dir == MCS_RX) { + reg = MCSX_CPM_RX_SLAVE_FLOWID_TCAM_ENA_0; + if (flow_id > 63) + reg = MCSX_CPM_RX_SLAVE_FLOWID_TCAM_ENA_1; + } else { + reg = MCSX_CPM_TX_SLAVE_FLOWID_TCAM_ENA_0; + if (flow_id > 63) + reg = MCSX_CPM_TX_SLAVE_FLOWID_TCAM_ENA_1; + } + + /* Enable/Disable the tcam entry */ + if (ena) + val = mcs_reg_read(mcs, reg) | BIT_ULL(flow_id); + else + val = mcs_reg_read(mcs, reg) & ~BIT_ULL(flow_id); + + mcs_reg_write(mcs, reg, val); +} + +void mcs_flowid_entry_write(struct mcs *mcs, u64 *data, u64 *mask, int flow_id, int dir) +{ + int reg_id; + u64 reg; + + if (dir == MCS_RX) { + for (reg_id = 0; reg_id < 4; reg_id++) { + reg = MCSX_CPM_RX_SLAVE_FLOWID_TCAM_DATAX(reg_id, flow_id); + mcs_reg_write(mcs, reg, data[reg_id]); + reg = MCSX_CPM_RX_SLAVE_FLOWID_TCAM_MASKX(reg_id, flow_id); + mcs_reg_write(mcs, reg, mask[reg_id]); + } + } else { + for (reg_id = 0; reg_id < 4; reg_id++) { + reg = MCSX_CPM_TX_SLAVE_FLOWID_TCAM_DATAX(reg_id, flow_id); + mcs_reg_write(mcs, reg, data[reg_id]); + reg = MCSX_CPM_TX_SLAVE_FLOWID_TCAM_MASKX(reg_id, flow_id); + mcs_reg_write(mcs, reg, mask[reg_id]); + } + } +} + +int mcs_install_flowid_bypass_entry(struct mcs *mcs) +{ + int flow_id, secy_id, reg_id; + struct secy_mem_map map; + u64 reg, plcy = 0; + + /* Flow entry */ + flow_id = mcs->hw->tcam_entries - MCS_RSRC_RSVD_CNT; + for (reg_id = 0; reg_id < 4; reg_id++) { + reg = MCSX_CPM_RX_SLAVE_FLOWID_TCAM_MASKX(reg_id, flow_id); + mcs_reg_write(mcs, reg, GENMASK_ULL(63, 0)); + } + for (reg_id = 0; reg_id < 4; reg_id++) { + reg = MCSX_CPM_TX_SLAVE_FLOWID_TCAM_MASKX(reg_id, flow_id); + mcs_reg_write(mcs, reg, GENMASK_ULL(63, 0)); + } + /* secy */ + secy_id = mcs->hw->secy_entries - MCS_RSRC_RSVD_CNT; + + /* Set validate frames to NULL and enable control port */ + plcy = 0x7ull; + if (mcs->hw->mcs_blks > 1) + plcy = BIT_ULL(0) | 0x3ull << 4; + mcs_secy_plcy_write(mcs, plcy, secy_id, MCS_RX); + + /* Enable control port and set mtu to max */ + plcy = BIT_ULL(0) | GENMASK_ULL(43, 28); + if (mcs->hw->mcs_blks > 1) + plcy = BIT_ULL(0) | GENMASK_ULL(63, 48); + mcs_secy_plcy_write(mcs, plcy, secy_id, MCS_TX); + + /* Map flowid to secy */ + map.secy = secy_id; + map.ctrl_pkt = 0; + map.flow_id = flow_id; + mcs->mcs_ops->mcs_flowid_secy_map(mcs, &map, MCS_RX); + map.sc = secy_id; + mcs->mcs_ops->mcs_flowid_secy_map(mcs, &map, MCS_TX); + + /* Enable Flowid entry */ + mcs_ena_dis_flowid_entry(mcs, flow_id, MCS_RX, true); + mcs_ena_dis_flowid_entry(mcs, flow_id, MCS_TX, true); + return 0; +} + +void mcs_clear_secy_plcy(struct mcs *mcs, int secy_id, int dir) +{ + struct mcs_rsrc_map *map; + int flow_id; + + if (dir == MCS_RX) + map = &mcs->rx; + else + map = &mcs->tx; + + /* Clear secy memory to zero */ + mcs_secy_plcy_write(mcs, 0, secy_id, dir); + + /* Disable the tcam entry using this secy */ + for (flow_id = 0; flow_id < map->flow_ids.max; flow_id++) { + if (map->flowid2secy_map[flow_id] != secy_id) + continue; + mcs_ena_dis_flowid_entry(mcs, flow_id, dir, false); + } +} + +int mcs_alloc_ctrlpktrule(struct rsrc_bmap *rsrc, u16 *pf_map, u16 offset, u16 pcifunc) +{ + int rsrc_id; + + if (!rsrc->bmap) + return -EINVAL; + + rsrc_id = bitmap_find_next_zero_area(rsrc->bmap, rsrc->max, offset, 1, 0); + if (rsrc_id >= rsrc->max) + return -ENOSPC; + + bitmap_set(rsrc->bmap, rsrc_id, 1); + pf_map[rsrc_id] = pcifunc; + + return rsrc_id; +} + +int mcs_free_ctrlpktrule(struct mcs *mcs, struct mcs_free_ctrl_pkt_rule_req *req) +{ + u16 pcifunc = req->hdr.pcifunc; + struct mcs_rsrc_map *map; + u64 dis, reg; + int id, rc; + + reg = (req->dir == MCS_RX) ? MCSX_PEX_RX_SLAVE_RULE_ENABLE : MCSX_PEX_TX_SLAVE_RULE_ENABLE; + map = (req->dir == MCS_RX) ? &mcs->rx : &mcs->tx; + + if (req->all) { + for (id = 0; id < map->ctrlpktrule.max; id++) { + if (map->ctrlpktrule2pf_map[id] != pcifunc) + continue; + mcs_free_rsrc(&map->ctrlpktrule, map->ctrlpktrule2pf_map, id, pcifunc); + dis = mcs_reg_read(mcs, reg); + dis &= ~BIT_ULL(id); + mcs_reg_write(mcs, reg, dis); + } + return 0; + } + + rc = mcs_free_rsrc(&map->ctrlpktrule, map->ctrlpktrule2pf_map, req->rule_idx, pcifunc); + dis = mcs_reg_read(mcs, reg); + dis &= ~BIT_ULL(req->rule_idx); + mcs_reg_write(mcs, reg, dis); + + return rc; +} + +int mcs_ctrlpktrule_write(struct mcs *mcs, struct mcs_ctrl_pkt_rule_write_req *req) +{ + u64 reg, enb; + u64 idx; + + switch (req->rule_type) { + case MCS_CTRL_PKT_RULE_TYPE_ETH: + req->data0 &= GENMASK(15, 0); + if (req->data0 != ETH_P_PAE) + return -EINVAL; + + idx = req->rule_idx - MCS_CTRLPKT_ETYPE_RULE_OFFSET; + reg = (req->dir == MCS_RX) ? MCSX_PEX_RX_SLAVE_RULE_ETYPE_CFGX(idx) : + MCSX_PEX_TX_SLAVE_RULE_ETYPE_CFGX(idx); + + mcs_reg_write(mcs, reg, req->data0); + break; + case MCS_CTRL_PKT_RULE_TYPE_DA: + if (!(req->data0 & BIT_ULL(40))) + return -EINVAL; + + idx = req->rule_idx - MCS_CTRLPKT_DA_RULE_OFFSET; + reg = (req->dir == MCS_RX) ? MCSX_PEX_RX_SLAVE_RULE_DAX(idx) : + MCSX_PEX_TX_SLAVE_RULE_DAX(idx); + + mcs_reg_write(mcs, reg, req->data0 & GENMASK_ULL(47, 0)); + break; + case MCS_CTRL_PKT_RULE_TYPE_RANGE: + if (!(req->data0 & BIT_ULL(40)) || !(req->data1 & BIT_ULL(40))) + return -EINVAL; + + idx = req->rule_idx - MCS_CTRLPKT_DA_RANGE_RULE_OFFSET; + if (req->dir == MCS_RX) { + reg = MCSX_PEX_RX_SLAVE_RULE_DA_RANGE_MINX(idx); + mcs_reg_write(mcs, reg, req->data0 & GENMASK_ULL(47, 0)); + reg = MCSX_PEX_RX_SLAVE_RULE_DA_RANGE_MAXX(idx); + mcs_reg_write(mcs, reg, req->data1 & GENMASK_ULL(47, 0)); + } else { + reg = MCSX_PEX_TX_SLAVE_RULE_DA_RANGE_MINX(idx); + mcs_reg_write(mcs, reg, req->data0 & GENMASK_ULL(47, 0)); + reg = MCSX_PEX_TX_SLAVE_RULE_DA_RANGE_MAXX(idx); + mcs_reg_write(mcs, reg, req->data1 & GENMASK_ULL(47, 0)); + } + break; + case MCS_CTRL_PKT_RULE_TYPE_COMBO: + req->data2 &= GENMASK(15, 0); + if (req->data2 != ETH_P_PAE || !(req->data0 & BIT_ULL(40)) || + !(req->data1 & BIT_ULL(40))) + return -EINVAL; + + idx = req->rule_idx - MCS_CTRLPKT_COMBO_RULE_OFFSET; + if (req->dir == MCS_RX) { + reg = MCSX_PEX_RX_SLAVE_RULE_COMBO_MINX(idx); + mcs_reg_write(mcs, reg, req->data0 & GENMASK_ULL(47, 0)); + reg = MCSX_PEX_RX_SLAVE_RULE_COMBO_MAXX(idx); + mcs_reg_write(mcs, reg, req->data1 & GENMASK_ULL(47, 0)); + reg = MCSX_PEX_RX_SLAVE_RULE_COMBO_ETX(idx); + mcs_reg_write(mcs, reg, req->data2); + } else { + reg = MCSX_PEX_TX_SLAVE_RULE_COMBO_MINX(idx); + mcs_reg_write(mcs, reg, req->data0 & GENMASK_ULL(47, 0)); + reg = MCSX_PEX_TX_SLAVE_RULE_COMBO_MAXX(idx); + mcs_reg_write(mcs, reg, req->data1 & GENMASK_ULL(47, 0)); + reg = MCSX_PEX_TX_SLAVE_RULE_COMBO_ETX(idx); + mcs_reg_write(mcs, reg, req->data2); + } + break; + case MCS_CTRL_PKT_RULE_TYPE_MAC: + if (!(req->data0 & BIT_ULL(40))) + return -EINVAL; + + idx = req->rule_idx - MCS_CTRLPKT_MAC_EN_RULE_OFFSET; + reg = (req->dir == MCS_RX) ? MCSX_PEX_RX_SLAVE_RULE_MAC : + MCSX_PEX_TX_SLAVE_RULE_MAC; + + mcs_reg_write(mcs, reg, req->data0 & GENMASK_ULL(47, 0)); + break; + } + + reg = (req->dir == MCS_RX) ? MCSX_PEX_RX_SLAVE_RULE_ENABLE : MCSX_PEX_TX_SLAVE_RULE_ENABLE; + + enb = mcs_reg_read(mcs, reg); + enb |= BIT_ULL(req->rule_idx); + mcs_reg_write(mcs, reg, enb); + + return 0; +} + +int mcs_free_rsrc(struct rsrc_bmap *rsrc, u16 *pf_map, int rsrc_id, u16 pcifunc) +{ + /* Check if the rsrc_id is mapped to PF/VF */ + if (pf_map[rsrc_id] != pcifunc) + return -EINVAL; + + rvu_free_rsrc(rsrc, rsrc_id); + pf_map[rsrc_id] = 0; + return 0; +} + +/* Free all the cam resources mapped to pf */ +int mcs_free_all_rsrc(struct mcs *mcs, int dir, u16 pcifunc) +{ + struct mcs_rsrc_map *map; + int id; + + if (dir == MCS_RX) + map = &mcs->rx; + else + map = &mcs->tx; + + /* free tcam entries */ + for (id = 0; id < map->flow_ids.max; id++) { + if (map->flowid2pf_map[id] != pcifunc) + continue; + mcs_free_rsrc(&map->flow_ids, map->flowid2pf_map, + id, pcifunc); + mcs_ena_dis_flowid_entry(mcs, id, dir, false); + } + + /* free secy entries */ + for (id = 0; id < map->secy.max; id++) { + if (map->secy2pf_map[id] != pcifunc) + continue; + mcs_free_rsrc(&map->secy, map->secy2pf_map, + id, pcifunc); + mcs_clear_secy_plcy(mcs, id, dir); + } + + /* free sc entries */ + for (id = 0; id < map->secy.max; id++) { + if (map->sc2pf_map[id] != pcifunc) + continue; + mcs_free_rsrc(&map->sc, map->sc2pf_map, id, pcifunc); + + /* Disable SC CAM only on RX side */ + if (dir == MCS_RX) + mcs_ena_dis_sc_cam_entry(mcs, id, false); + } + + /* free sa entries */ + for (id = 0; id < map->sa.max; id++) { + if (map->sa2pf_map[id] != pcifunc) + continue; + mcs_free_rsrc(&map->sa, map->sa2pf_map, id, pcifunc); + } + return 0; +} + +int mcs_alloc_rsrc(struct rsrc_bmap *rsrc, u16 *pf_map, u16 pcifunc) +{ + int rsrc_id; + + rsrc_id = rvu_alloc_rsrc(rsrc); + if (rsrc_id < 0) + return -ENOMEM; + pf_map[rsrc_id] = pcifunc; + return rsrc_id; +} + +int mcs_alloc_all_rsrc(struct mcs *mcs, u8 *flow_id, u8 *secy_id, + u8 *sc_id, u8 *sa1_id, u8 *sa2_id, u16 pcifunc, int dir) +{ + struct mcs_rsrc_map *map; + int id; + + if (dir == MCS_RX) + map = &mcs->rx; + else + map = &mcs->tx; + + id = mcs_alloc_rsrc(&map->flow_ids, map->flowid2pf_map, pcifunc); + if (id < 0) + return -ENOMEM; + *flow_id = id; + + id = mcs_alloc_rsrc(&map->secy, map->secy2pf_map, pcifunc); + if (id < 0) + return -ENOMEM; + *secy_id = id; + + id = mcs_alloc_rsrc(&map->sc, map->sc2pf_map, pcifunc); + if (id < 0) + return -ENOMEM; + *sc_id = id; + + id = mcs_alloc_rsrc(&map->sa, map->sa2pf_map, pcifunc); + if (id < 0) + return -ENOMEM; + *sa1_id = id; + + id = mcs_alloc_rsrc(&map->sa, map->sa2pf_map, pcifunc); + if (id < 0) + return -ENOMEM; + *sa2_id = id; + + return 0; +} + +static void cn10kb_mcs_tx_pn_wrapped_handler(struct mcs *mcs) +{ + struct mcs_intr_event event = { 0 }; + struct rsrc_bmap *sc_bmap; + u64 val; + int sc; + + sc_bmap = &mcs->tx.sc; + + event.mcs_id = mcs->mcs_id; + event.intr_mask = MCS_CPM_TX_PACKET_XPN_EQ0_INT; + + for_each_set_bit(sc, sc_bmap->bmap, mcs->hw->sc_entries) { + val = mcs_reg_read(mcs, MCSX_CPM_TX_SLAVE_SA_MAP_MEM_0X(sc)); + + if (mcs->tx_sa_active[sc]) + /* SA_index1 was used and got expired */ + event.sa_id = (val >> 9) & 0xFF; + else + /* SA_index0 was used and got expired */ + event.sa_id = val & 0xFF; + + event.pcifunc = mcs->tx.sa2pf_map[event.sa_id]; + mcs_add_intr_wq_entry(mcs, &event); + } +} + +static void cn10kb_mcs_tx_pn_thresh_reached_handler(struct mcs *mcs) +{ + struct mcs_intr_event event = { 0 }; + struct rsrc_bmap *sc_bmap; + u64 val, status; + int sc; + + sc_bmap = &mcs->tx.sc; + + event.mcs_id = mcs->mcs_id; + event.intr_mask = MCS_CPM_TX_PN_THRESH_REACHED_INT; + + /* TX SA interrupt is raised only if autorekey is enabled. + * MCS_CPM_TX_SLAVE_SA_MAP_MEM_0X[sc].tx_sa_active bit gets toggled if + * one of two SAs mapped to SC gets expired. If tx_sa_active=0 implies + * SA in SA_index1 got expired else SA in SA_index0 got expired. + */ + for_each_set_bit(sc, sc_bmap->bmap, mcs->hw->sc_entries) { + val = mcs_reg_read(mcs, MCSX_CPM_TX_SLAVE_SA_MAP_MEM_0X(sc)); + /* Auto rekey is enable */ + if (!((val >> 18) & 0x1)) + continue; + + status = (val >> 21) & 0x1; + + /* Check if tx_sa_active status had changed */ + if (status == mcs->tx_sa_active[sc]) + continue; + /* SA_index0 is expired */ + if (status) + event.sa_id = val & 0xFF; + else + event.sa_id = (val >> 9) & 0xFF; + + event.pcifunc = mcs->tx.sa2pf_map[event.sa_id]; + mcs_add_intr_wq_entry(mcs, &event); + } +} + +static void mcs_rx_pn_thresh_reached_handler(struct mcs *mcs) +{ + struct mcs_intr_event event = { 0 }; + int sa, reg; + u64 intr; + + /* Check expired SAs */ + for (reg = 0; reg < (mcs->hw->sa_entries / 64); reg++) { + /* Bit high in *PN_THRESH_REACHEDX implies + * corresponding SAs are expired. + */ + intr = mcs_reg_read(mcs, MCSX_CPM_RX_SLAVE_PN_THRESH_REACHEDX(reg)); + for (sa = 0; sa < 64; sa++) { + if (!(intr & BIT_ULL(sa))) + continue; + + event.mcs_id = mcs->mcs_id; + event.intr_mask = MCS_CPM_RX_PN_THRESH_REACHED_INT; + event.sa_id = sa + (reg * 64); + event.pcifunc = mcs->rx.sa2pf_map[event.sa_id]; + mcs_add_intr_wq_entry(mcs, &event); + } + } +} + +static void mcs_rx_misc_intr_handler(struct mcs *mcs, u64 intr) +{ + struct mcs_intr_event event = { 0 }; + + event.mcs_id = mcs->mcs_id; + event.pcifunc = mcs->pf_map[0]; + + if (intr & MCS_CPM_RX_INT_SECTAG_V_EQ1) + event.intr_mask = MCS_CPM_RX_SECTAG_V_EQ1_INT; + if (intr & MCS_CPM_RX_INT_SECTAG_E_EQ0_C_EQ1) + event.intr_mask |= MCS_CPM_RX_SECTAG_E_EQ0_C_EQ1_INT; + if (intr & MCS_CPM_RX_INT_SL_GTE48) + event.intr_mask |= MCS_CPM_RX_SECTAG_SL_GTE48_INT; + if (intr & MCS_CPM_RX_INT_ES_EQ1_SC_EQ1) + event.intr_mask |= MCS_CPM_RX_SECTAG_ES_EQ1_SC_EQ1_INT; + if (intr & MCS_CPM_RX_INT_SC_EQ1_SCB_EQ1) + event.intr_mask |= MCS_CPM_RX_SECTAG_SC_EQ1_SCB_EQ1_INT; + if (intr & MCS_CPM_RX_INT_PACKET_XPN_EQ0) + event.intr_mask |= MCS_CPM_RX_PACKET_XPN_EQ0_INT; + + mcs_add_intr_wq_entry(mcs, &event); +} + +static void mcs_tx_misc_intr_handler(struct mcs *mcs, u64 intr) +{ + struct mcs_intr_event event = { 0 }; + + if (!(intr & MCS_CPM_TX_INT_SA_NOT_VALID)) + return; + + event.mcs_id = mcs->mcs_id; + event.pcifunc = mcs->pf_map[0]; + + event.intr_mask = MCS_CPM_TX_SA_NOT_VALID_INT; + + mcs_add_intr_wq_entry(mcs, &event); +} + +static void mcs_bbe_intr_handler(struct mcs *mcs, u64 intr, enum mcs_direction dir) +{ + struct mcs_intr_event event = { 0 }; + int i; + + if (!(intr & MCS_BBE_INT_MASK)) + return; + + event.mcs_id = mcs->mcs_id; + event.pcifunc = mcs->pf_map[0]; + + for (i = 0; i < MCS_MAX_BBE_INT; i++) { + if (!(intr & BIT_ULL(i))) + continue; + + /* Lower nibble denotes data fifo overflow interrupts and + * upper nibble indicates policy fifo overflow interrupts. + */ + if (intr & 0xFULL) + event.intr_mask = (dir == MCS_RX) ? + MCS_BBE_RX_DFIFO_OVERFLOW_INT : + MCS_BBE_TX_DFIFO_OVERFLOW_INT; + else + event.intr_mask = (dir == MCS_RX) ? + MCS_BBE_RX_PLFIFO_OVERFLOW_INT : + MCS_BBE_RX_PLFIFO_OVERFLOW_INT; + + /* Notify the lmac_id info which ran into BBE fatal error */ + event.lmac_id = i & 0x3ULL; + mcs_add_intr_wq_entry(mcs, &event); + } +} + +static void mcs_pab_intr_handler(struct mcs *mcs, u64 intr, enum mcs_direction dir) +{ + struct mcs_intr_event event = { 0 }; + int i; + + if (!(intr & MCS_PAB_INT_MASK)) + return; + + event.mcs_id = mcs->mcs_id; + event.pcifunc = mcs->pf_map[0]; + + for (i = 0; i < MCS_MAX_PAB_INT; i++) { + if (!(intr & BIT_ULL(i))) + continue; + + event.intr_mask = (dir == MCS_RX) ? MCS_PAB_RX_CHAN_OVERFLOW_INT : + MCS_PAB_TX_CHAN_OVERFLOW_INT; + + /* Notify the lmac_id info which ran into PAB fatal error */ + event.lmac_id = i; + mcs_add_intr_wq_entry(mcs, &event); + } +} + +static irqreturn_t mcs_ip_intr_handler(int irq, void *mcs_irq) +{ + struct mcs *mcs = (struct mcs *)mcs_irq; + u64 intr, cpm_intr, bbe_intr, pab_intr; + + /* Disable and clear the interrupt */ + mcs_reg_write(mcs, MCSX_IP_INT_ENA_W1C, BIT_ULL(0)); + mcs_reg_write(mcs, MCSX_IP_INT, BIT_ULL(0)); + + /* Check which block has interrupt*/ + intr = mcs_reg_read(mcs, MCSX_TOP_SLAVE_INT_SUM); + + /* CPM RX */ + if (intr & MCS_CPM_RX_INT_ENA) { + /* Check for PN thresh interrupt bit */ + cpm_intr = mcs_reg_read(mcs, MCSX_CPM_RX_SLAVE_RX_INT); + + if (cpm_intr & MCS_CPM_RX_INT_PN_THRESH_REACHED) + mcs_rx_pn_thresh_reached_handler(mcs); + + if (cpm_intr & MCS_CPM_RX_INT_ALL) + mcs_rx_misc_intr_handler(mcs, cpm_intr); + + /* Clear the interrupt */ + mcs_reg_write(mcs, MCSX_CPM_RX_SLAVE_RX_INT, cpm_intr); + } + + /* CPM TX */ + if (intr & MCS_CPM_TX_INT_ENA) { + cpm_intr = mcs_reg_read(mcs, MCSX_CPM_TX_SLAVE_TX_INT); + + if (cpm_intr & MCS_CPM_TX_INT_PN_THRESH_REACHED) { + if (mcs->hw->mcs_blks > 1) + cnf10kb_mcs_tx_pn_thresh_reached_handler(mcs); + else + cn10kb_mcs_tx_pn_thresh_reached_handler(mcs); + } + + if (cpm_intr & MCS_CPM_TX_INT_SA_NOT_VALID) + mcs_tx_misc_intr_handler(mcs, cpm_intr); + + if (cpm_intr & MCS_CPM_TX_INT_PACKET_XPN_EQ0) { + if (mcs->hw->mcs_blks > 1) + cnf10kb_mcs_tx_pn_wrapped_handler(mcs); + else + cn10kb_mcs_tx_pn_wrapped_handler(mcs); + } + /* Clear the interrupt */ + mcs_reg_write(mcs, MCSX_CPM_TX_SLAVE_TX_INT, cpm_intr); + } + + /* BBE RX */ + if (intr & MCS_BBE_RX_INT_ENA) { + bbe_intr = mcs_reg_read(mcs, MCSX_BBE_RX_SLAVE_BBE_INT); + mcs_bbe_intr_handler(mcs, bbe_intr, MCS_RX); + + /* Clear the interrupt */ + mcs_reg_write(mcs, MCSX_BBE_RX_SLAVE_BBE_INT_INTR_RW, 0); + mcs_reg_write(mcs, MCSX_BBE_RX_SLAVE_BBE_INT, bbe_intr); + } + + /* BBE TX */ + if (intr & MCS_BBE_TX_INT_ENA) { + bbe_intr = mcs_reg_read(mcs, MCSX_BBE_TX_SLAVE_BBE_INT); + mcs_bbe_intr_handler(mcs, bbe_intr, MCS_TX); + + /* Clear the interrupt */ + mcs_reg_write(mcs, MCSX_BBE_TX_SLAVE_BBE_INT_INTR_RW, 0); + mcs_reg_write(mcs, MCSX_BBE_TX_SLAVE_BBE_INT, bbe_intr); + } + + /* PAB RX */ + if (intr & MCS_PAB_RX_INT_ENA) { + pab_intr = mcs_reg_read(mcs, MCSX_PAB_RX_SLAVE_PAB_INT); + mcs_pab_intr_handler(mcs, pab_intr, MCS_RX); + + /* Clear the interrupt */ + mcs_reg_write(mcs, MCSX_PAB_RX_SLAVE_PAB_INT_INTR_RW, 0); + mcs_reg_write(mcs, MCSX_PAB_RX_SLAVE_PAB_INT, pab_intr); + } + + /* PAB TX */ + if (intr & MCS_PAB_TX_INT_ENA) { + pab_intr = mcs_reg_read(mcs, MCSX_PAB_TX_SLAVE_PAB_INT); + mcs_pab_intr_handler(mcs, pab_intr, MCS_TX); + + /* Clear the interrupt */ + mcs_reg_write(mcs, MCSX_PAB_TX_SLAVE_PAB_INT_INTR_RW, 0); + mcs_reg_write(mcs, MCSX_PAB_TX_SLAVE_PAB_INT, pab_intr); + } + + /* Enable the interrupt */ + mcs_reg_write(mcs, MCSX_IP_INT_ENA_W1S, BIT_ULL(0)); + + return IRQ_HANDLED; +} + +static void *alloc_mem(struct mcs *mcs, int n) +{ + return devm_kcalloc(mcs->dev, n, sizeof(u16), GFP_KERNEL); +} + +static int mcs_alloc_struct_mem(struct mcs *mcs, struct mcs_rsrc_map *res) +{ + struct hwinfo *hw = mcs->hw; + int err; + + res->flowid2pf_map = alloc_mem(mcs, hw->tcam_entries); + if (!res->flowid2pf_map) + return -ENOMEM; + + res->secy2pf_map = alloc_mem(mcs, hw->secy_entries); + if (!res->secy2pf_map) + return -ENOMEM; + + res->sc2pf_map = alloc_mem(mcs, hw->sc_entries); + if (!res->sc2pf_map) + return -ENOMEM; + + res->sa2pf_map = alloc_mem(mcs, hw->sa_entries); + if (!res->sa2pf_map) + return -ENOMEM; + + res->flowid2secy_map = alloc_mem(mcs, hw->tcam_entries); + if (!res->flowid2secy_map) + return -ENOMEM; + + res->ctrlpktrule2pf_map = alloc_mem(mcs, MCS_MAX_CTRLPKT_RULES); + if (!res->ctrlpktrule2pf_map) + return -ENOMEM; + + res->flow_ids.max = hw->tcam_entries - MCS_RSRC_RSVD_CNT; + err = rvu_alloc_bitmap(&res->flow_ids); + if (err) + return err; + + res->secy.max = hw->secy_entries - MCS_RSRC_RSVD_CNT; + err = rvu_alloc_bitmap(&res->secy); + if (err) + return err; + + res->sc.max = hw->sc_entries; + err = rvu_alloc_bitmap(&res->sc); + if (err) + return err; + + res->sa.max = hw->sa_entries; + err = rvu_alloc_bitmap(&res->sa); + if (err) + return err; + + res->ctrlpktrule.max = MCS_MAX_CTRLPKT_RULES; + err = rvu_alloc_bitmap(&res->ctrlpktrule); + if (err) + return err; + + return 0; +} + +static int mcs_register_interrupts(struct mcs *mcs) +{ + int ret = 0; + + mcs->num_vec = pci_msix_vec_count(mcs->pdev); + + ret = pci_alloc_irq_vectors(mcs->pdev, mcs->num_vec, + mcs->num_vec, PCI_IRQ_MSIX); + if (ret < 0) { + dev_err(mcs->dev, "MCS Request for %d msix vector failed err:%d\n", + mcs->num_vec, ret); + return ret; + } + + ret = request_irq(pci_irq_vector(mcs->pdev, MCS_INT_VEC_IP), + mcs_ip_intr_handler, 0, "MCS_IP", mcs); + if (ret) { + dev_err(mcs->dev, "MCS IP irq registration failed\n"); + goto exit; + } + + /* MCS enable IP interrupts */ + mcs_reg_write(mcs, MCSX_IP_INT_ENA_W1S, BIT_ULL(0)); + + /* Enable CPM Rx/Tx interrupts */ + mcs_reg_write(mcs, MCSX_TOP_SLAVE_INT_SUM_ENB, + MCS_CPM_RX_INT_ENA | MCS_CPM_TX_INT_ENA | + MCS_BBE_RX_INT_ENA | MCS_BBE_TX_INT_ENA | + MCS_PAB_RX_INT_ENA | MCS_PAB_TX_INT_ENA); + + mcs_reg_write(mcs, MCSX_CPM_TX_SLAVE_TX_INT_ENB, 0x7ULL); + mcs_reg_write(mcs, MCSX_CPM_RX_SLAVE_RX_INT_ENB, 0x7FULL); + + mcs_reg_write(mcs, MCSX_BBE_RX_SLAVE_BBE_INT_ENB, 0xff); + mcs_reg_write(mcs, MCSX_BBE_TX_SLAVE_BBE_INT_ENB, 0xff); + + mcs_reg_write(mcs, MCSX_PAB_RX_SLAVE_PAB_INT_ENB, 0xff); + mcs_reg_write(mcs, MCSX_PAB_TX_SLAVE_PAB_INT_ENB, 0xff); + + mcs->tx_sa_active = alloc_mem(mcs, mcs->hw->sc_entries); + if (!mcs->tx_sa_active) + goto exit; + + return ret; +exit: + pci_free_irq_vectors(mcs->pdev); + mcs->num_vec = 0; + return ret; +} + +int mcs_get_blkcnt(void) +{ + struct mcs *mcs; + int idmax = -ENODEV; + + /* Check MCS block is present in hardware */ + if (!pci_dev_present(mcs_id_table)) + return 0; + + list_for_each_entry(mcs, &mcs_list, mcs_list) + if (mcs->mcs_id > idmax) + idmax = mcs->mcs_id; + + if (idmax < 0) + return 0; + + return idmax + 1; +} + +struct mcs *mcs_get_pdata(int mcs_id) +{ + struct mcs *mcs_dev; + + list_for_each_entry(mcs_dev, &mcs_list, mcs_list) { + if (mcs_dev->mcs_id == mcs_id) + return mcs_dev; + } + return NULL; +} + +void mcs_set_port_cfg(struct mcs *mcs, struct mcs_port_cfg_set_req *req) +{ + u64 val = 0; + + mcs_reg_write(mcs, MCSX_PAB_RX_SLAVE_PORT_CFGX(req->port_id), + req->port_mode & MCS_PORT_MODE_MASK); + + req->cstm_tag_rel_mode_sel &= 0x3; + + if (mcs->hw->mcs_blks > 1) { + req->fifo_skid &= MCS_PORT_FIFO_SKID_MASK; + val = (u32)req->fifo_skid << 0x10; + val |= req->fifo_skid; + mcs_reg_write(mcs, MCSX_PAB_RX_SLAVE_FIFO_SKID_CFGX(req->port_id), val); + mcs_reg_write(mcs, MCSX_PEX_TX_SLAVE_CUSTOM_TAG_REL_MODE_SEL(req->port_id), + req->cstm_tag_rel_mode_sel); + val = mcs_reg_read(mcs, MCSX_PEX_RX_SLAVE_PEX_CONFIGURATION); + + if (req->custom_hdr_enb) + val |= BIT_ULL(req->port_id); + else + val &= ~BIT_ULL(req->port_id); + + mcs_reg_write(mcs, MCSX_PEX_RX_SLAVE_PEX_CONFIGURATION, val); + } else { + val = mcs_reg_read(mcs, MCSX_PEX_TX_SLAVE_PORT_CONFIG(req->port_id)); + val |= (req->cstm_tag_rel_mode_sel << 2); + mcs_reg_write(mcs, MCSX_PEX_TX_SLAVE_PORT_CONFIG(req->port_id), val); + } +} + +void mcs_get_port_cfg(struct mcs *mcs, struct mcs_port_cfg_get_req *req, + struct mcs_port_cfg_get_rsp *rsp) +{ + u64 reg = 0; + + rsp->port_mode = mcs_reg_read(mcs, MCSX_PAB_RX_SLAVE_PORT_CFGX(req->port_id)) & + MCS_PORT_MODE_MASK; + + if (mcs->hw->mcs_blks > 1) { + reg = MCSX_PAB_RX_SLAVE_FIFO_SKID_CFGX(req->port_id); + rsp->fifo_skid = mcs_reg_read(mcs, reg) & MCS_PORT_FIFO_SKID_MASK; + reg = MCSX_PEX_TX_SLAVE_CUSTOM_TAG_REL_MODE_SEL(req->port_id); + rsp->cstm_tag_rel_mode_sel = mcs_reg_read(mcs, reg) & 0x3; + if (mcs_reg_read(mcs, MCSX_PEX_RX_SLAVE_PEX_CONFIGURATION) & BIT_ULL(req->port_id)) + rsp->custom_hdr_enb = 1; + } else { + reg = MCSX_PEX_TX_SLAVE_PORT_CONFIG(req->port_id); + rsp->cstm_tag_rel_mode_sel = mcs_reg_read(mcs, reg) >> 2; + } + + rsp->port_id = req->port_id; + rsp->mcs_id = req->mcs_id; +} + +void mcs_get_custom_tag_cfg(struct mcs *mcs, struct mcs_custom_tag_cfg_get_req *req, + struct mcs_custom_tag_cfg_get_rsp *rsp) +{ + u64 reg = 0, val = 0; + u8 idx; + + for (idx = 0; idx < MCS_MAX_CUSTOM_TAGS; idx++) { + if (mcs->hw->mcs_blks > 1) + reg = (req->dir == MCS_RX) ? MCSX_PEX_RX_SLAVE_CUSTOM_TAGX(idx) : + MCSX_PEX_TX_SLAVE_CUSTOM_TAGX(idx); + else + reg = (req->dir == MCS_RX) ? MCSX_PEX_RX_SLAVE_VLAN_CFGX(idx) : + MCSX_PEX_TX_SLAVE_VLAN_CFGX(idx); + + val = mcs_reg_read(mcs, reg); + if (mcs->hw->mcs_blks > 1) { + rsp->cstm_etype[idx] = val & GENMASK(15, 0); + rsp->cstm_indx[idx] = (val >> 0x16) & 0x3; + reg = (req->dir == MCS_RX) ? MCSX_PEX_RX_SLAVE_ETYPE_ENABLE : + MCSX_PEX_TX_SLAVE_ETYPE_ENABLE; + rsp->cstm_etype_en = mcs_reg_read(mcs, reg) & 0xFF; + } else { + rsp->cstm_etype[idx] = (val >> 0x1) & GENMASK(15, 0); + rsp->cstm_indx[idx] = (val >> 0x11) & 0x3; + rsp->cstm_etype_en |= (val & 0x1) << idx; + } + } + + rsp->mcs_id = req->mcs_id; + rsp->dir = req->dir; +} + +void mcs_reset_port(struct mcs *mcs, u8 port_id, u8 reset) +{ + u64 reg = MCSX_MCS_TOP_SLAVE_PORT_RESET(port_id); + + mcs_reg_write(mcs, reg, reset & 0x1); +} + +/* Set lmac to bypass/operational mode */ +void mcs_set_lmac_mode(struct mcs *mcs, int lmac_id, u8 mode) +{ + u64 reg; + + reg = MCSX_MCS_TOP_SLAVE_CHANNEL_CFG(lmac_id * 2); + mcs_reg_write(mcs, reg, (u64)mode); +} + +void mcs_pn_threshold_set(struct mcs *mcs, struct mcs_set_pn_threshold *pn) +{ + u64 reg; + + if (pn->dir == MCS_RX) + reg = pn->xpn ? MCSX_CPM_RX_SLAVE_XPN_THRESHOLD : MCSX_CPM_RX_SLAVE_PN_THRESHOLD; + else + reg = pn->xpn ? MCSX_CPM_TX_SLAVE_XPN_THRESHOLD : MCSX_CPM_TX_SLAVE_PN_THRESHOLD; + + mcs_reg_write(mcs, reg, pn->threshold); +} + +void cn10kb_mcs_parser_cfg(struct mcs *mcs) +{ + u64 reg, val; + + /* VLAN CTag */ + val = BIT_ULL(0) | (0x8100ull & 0xFFFF) << 1 | BIT_ULL(17); + /* RX */ + reg = MCSX_PEX_RX_SLAVE_VLAN_CFGX(0); + mcs_reg_write(mcs, reg, val); + + /* TX */ + reg = MCSX_PEX_TX_SLAVE_VLAN_CFGX(0); + mcs_reg_write(mcs, reg, val); + + /* VLAN STag */ + val = BIT_ULL(0) | (0x88a8ull & 0xFFFF) << 1 | BIT_ULL(18); + /* RX */ + reg = MCSX_PEX_RX_SLAVE_VLAN_CFGX(1); + mcs_reg_write(mcs, reg, val); + + /* TX */ + reg = MCSX_PEX_TX_SLAVE_VLAN_CFGX(1); + mcs_reg_write(mcs, reg, val); +} + +static void mcs_lmac_init(struct mcs *mcs, int lmac_id) +{ + u64 reg; + + /* Port mode 25GB */ + reg = MCSX_PAB_RX_SLAVE_PORT_CFGX(lmac_id); + mcs_reg_write(mcs, reg, 0); + + if (mcs->hw->mcs_blks > 1) { + reg = MCSX_PAB_RX_SLAVE_FIFO_SKID_CFGX(lmac_id); + mcs_reg_write(mcs, reg, 0xe000e); + return; + } + + reg = MCSX_PAB_TX_SLAVE_PORT_CFGX(lmac_id); + mcs_reg_write(mcs, reg, 0); +} + +int mcs_set_lmac_channels(int mcs_id, u16 base) +{ + struct mcs *mcs; + int lmac; + u64 cfg; + + mcs = mcs_get_pdata(mcs_id); + if (!mcs) + return -ENODEV; + for (lmac = 0; lmac < mcs->hw->lmac_cnt; lmac++) { + cfg = mcs_reg_read(mcs, MCSX_LINK_LMACX_CFG(lmac)); + cfg &= ~(MCSX_LINK_LMAC_BASE_MASK | MCSX_LINK_LMAC_RANGE_MASK); + cfg |= FIELD_PREP(MCSX_LINK_LMAC_RANGE_MASK, ilog2(16)); + cfg |= FIELD_PREP(MCSX_LINK_LMAC_BASE_MASK, base); + mcs_reg_write(mcs, MCSX_LINK_LMACX_CFG(lmac), cfg); + base += 16; + } + return 0; +} + +static int mcs_x2p_calibration(struct mcs *mcs) +{ + unsigned long timeout = jiffies + usecs_to_jiffies(20000); + int i, err = 0; + u64 val; + + /* set X2P calibration */ + val = mcs_reg_read(mcs, MCSX_MIL_GLOBAL); + val |= BIT_ULL(5); + mcs_reg_write(mcs, MCSX_MIL_GLOBAL, val); + + /* Wait for calibration to complete */ + while (!(mcs_reg_read(mcs, MCSX_MIL_RX_GBL_STATUS) & BIT_ULL(0))) { + if (time_before(jiffies, timeout)) { + usleep_range(80, 100); + continue; + } else { + err = -EBUSY; + dev_err(mcs->dev, "MCS X2P calibration failed..ignoring\n"); + return err; + } + } + + val = mcs_reg_read(mcs, MCSX_MIL_RX_GBL_STATUS); + for (i = 0; i < mcs->hw->mcs_x2p_intf; i++) { + if (val & BIT_ULL(1 + i)) + continue; + err = -EBUSY; + dev_err(mcs->dev, "MCS:%d didn't respond to X2P calibration\n", i); + } + /* Clear X2P calibrate */ + mcs_reg_write(mcs, MCSX_MIL_GLOBAL, mcs_reg_read(mcs, MCSX_MIL_GLOBAL) & ~BIT_ULL(5)); + + return err; +} + +static void mcs_set_external_bypass(struct mcs *mcs, u8 bypass) +{ + u64 val; + + /* Set MCS to external bypass */ + val = mcs_reg_read(mcs, MCSX_MIL_GLOBAL); + if (bypass) + val |= BIT_ULL(6); + else + val &= ~BIT_ULL(6); + mcs_reg_write(mcs, MCSX_MIL_GLOBAL, val); +} + +static void mcs_global_cfg(struct mcs *mcs) +{ + /* Disable external bypass */ + mcs_set_external_bypass(mcs, false); + + /* Reset TX/RX stats memory */ + mcs_reg_write(mcs, MCSX_CSE_RX_SLAVE_STATS_CLEAR, 0x1F); + mcs_reg_write(mcs, MCSX_CSE_TX_SLAVE_STATS_CLEAR, 0x1F); + + /* Set MCS to perform standard IEEE802.1AE macsec processing */ + if (mcs->hw->mcs_blks == 1) { + mcs_reg_write(mcs, MCSX_IP_MODE, BIT_ULL(3)); + return; + } + + mcs_reg_write(mcs, MCSX_BBE_RX_SLAVE_CAL_ENTRY, 0xe4); + mcs_reg_write(mcs, MCSX_BBE_RX_SLAVE_CAL_LEN, 4); +} + +void cn10kb_mcs_set_hw_capabilities(struct mcs *mcs) +{ + struct hwinfo *hw = mcs->hw; + + hw->tcam_entries = 128; /* TCAM entries */ + hw->secy_entries = 128; /* SecY entries */ + hw->sc_entries = 128; /* SC CAM entries */ + hw->sa_entries = 256; /* SA entries */ + hw->lmac_cnt = 20; /* lmacs/ports per mcs block */ + hw->mcs_x2p_intf = 5; /* x2p clabration intf */ + hw->mcs_blks = 1; /* MCS blocks */ +} + +static struct mcs_ops cn10kb_mcs_ops = { + .mcs_set_hw_capabilities = cn10kb_mcs_set_hw_capabilities, + .mcs_parser_cfg = cn10kb_mcs_parser_cfg, + .mcs_tx_sa_mem_map_write = cn10kb_mcs_tx_sa_mem_map_write, + .mcs_rx_sa_mem_map_write = cn10kb_mcs_rx_sa_mem_map_write, + .mcs_flowid_secy_map = cn10kb_mcs_flowid_secy_map, +}; + +static int mcs_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + struct device *dev = &pdev->dev; + int lmac, err = 0; + struct mcs *mcs; + + mcs = devm_kzalloc(dev, sizeof(*mcs), GFP_KERNEL); + if (!mcs) + return -ENOMEM; + + mcs->hw = devm_kzalloc(dev, sizeof(struct hwinfo), GFP_KERNEL); + if (!mcs->hw) + return -ENOMEM; + + err = pci_enable_device(pdev); + if (err) { + dev_err(dev, "Failed to enable PCI device\n"); + pci_set_drvdata(pdev, NULL); + return err; + } + + err = pci_request_regions(pdev, DRV_NAME); + if (err) { + dev_err(dev, "PCI request regions failed 0x%x\n", err); + goto exit; + } + + mcs->reg_base = pcim_iomap(pdev, PCI_CFG_REG_BAR_NUM, 0); + if (!mcs->reg_base) { + dev_err(dev, "mcs: Cannot map CSR memory space, aborting\n"); + err = -ENOMEM; + goto exit; + } + + pci_set_drvdata(pdev, mcs); + mcs->pdev = pdev; + mcs->dev = &pdev->dev; + + if (pdev->subsystem_device == PCI_SUBSYS_DEVID_CN10K_B) + mcs->mcs_ops = &cn10kb_mcs_ops; + else + mcs->mcs_ops = cnf10kb_get_mac_ops(); + + /* Set hardware capabilities */ + mcs->mcs_ops->mcs_set_hw_capabilities(mcs); + + mcs_global_cfg(mcs); + + /* Perform X2P clibration */ + err = mcs_x2p_calibration(mcs); + if (err) + goto err_x2p; + + mcs->mcs_id = (pci_resource_start(pdev, PCI_CFG_REG_BAR_NUM) >> 24) + & MCS_ID_MASK; + + /* Set mcs tx side resources */ + err = mcs_alloc_struct_mem(mcs, &mcs->tx); + if (err) + goto err_x2p; + + /* Set mcs rx side resources */ + err = mcs_alloc_struct_mem(mcs, &mcs->rx); + if (err) + goto err_x2p; + + /* per port config */ + for (lmac = 0; lmac < mcs->hw->lmac_cnt; lmac++) + mcs_lmac_init(mcs, lmac); + + /* Parser configuration */ + mcs->mcs_ops->mcs_parser_cfg(mcs); + + err = mcs_register_interrupts(mcs); + if (err) + goto exit; + + list_add(&mcs->mcs_list, &mcs_list); + mutex_init(&mcs->stats_lock); + + return 0; + +err_x2p: + /* Enable external bypass */ + mcs_set_external_bypass(mcs, true); +exit: + pci_release_regions(pdev); + pci_disable_device(pdev); + pci_set_drvdata(pdev, NULL); + return err; +} + +static void mcs_remove(struct pci_dev *pdev) +{ + struct mcs *mcs = pci_get_drvdata(pdev); + + /* Set MCS to external bypass */ + mcs_set_external_bypass(mcs, true); + pci_free_irq_vectors(pdev); + pci_release_regions(pdev); + pci_disable_device(pdev); + pci_set_drvdata(pdev, NULL); +} + +struct pci_driver mcs_driver = { + .name = DRV_NAME, + .id_table = mcs_id_table, + .probe = mcs_probe, + .remove = mcs_remove, +}; diff --git a/drivers/net/ethernet/marvell/octeontx2/af/mcs.h b/drivers/net/ethernet/marvell/octeontx2/af/mcs.h new file mode 100644 index 000000000000..64dc2b80e15d --- /dev/null +++ b/drivers/net/ethernet/marvell/octeontx2/af/mcs.h @@ -0,0 +1,246 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Marvell CN10K MCS driver + * + * Copyright (C) 2022 Marvell. + */ + +#ifndef MCS_H +#define MCS_H + +#include <linux/bits.h> +#include "rvu.h" + +#define PCI_DEVID_CN10K_MCS 0xA096 + +#define MCSX_LINK_LMAC_RANGE_MASK GENMASK_ULL(19, 16) +#define MCSX_LINK_LMAC_BASE_MASK GENMASK_ULL(11, 0) + +#define MCS_ID_MASK 0x7 +#define MCS_MAX_PFS 128 + +#define MCS_PORT_MODE_MASK 0x3 +#define MCS_PORT_FIFO_SKID_MASK 0x3F +#define MCS_MAX_CUSTOM_TAGS 0x8 + +#define MCS_CTRLPKT_ETYPE_RULE_MAX 8 +#define MCS_CTRLPKT_DA_RULE_MAX 8 +#define MCS_CTRLPKT_DA_RANGE_RULE_MAX 4 +#define MCS_CTRLPKT_COMBO_RULE_MAX 4 +#define MCS_CTRLPKT_MAC_RULE_MAX 1 + +#define MCS_MAX_CTRLPKT_RULES (MCS_CTRLPKT_ETYPE_RULE_MAX + \ + MCS_CTRLPKT_DA_RULE_MAX + \ + MCS_CTRLPKT_DA_RANGE_RULE_MAX + \ + MCS_CTRLPKT_COMBO_RULE_MAX + \ + MCS_CTRLPKT_MAC_RULE_MAX) + +#define MCS_CTRLPKT_ETYPE_RULE_OFFSET 0 +#define MCS_CTRLPKT_DA_RULE_OFFSET 8 +#define MCS_CTRLPKT_DA_RANGE_RULE_OFFSET 16 +#define MCS_CTRLPKT_COMBO_RULE_OFFSET 20 +#define MCS_CTRLPKT_MAC_EN_RULE_OFFSET 24 + +/* Reserved resources for default bypass entry */ +#define MCS_RSRC_RSVD_CNT 1 + +/* MCS Interrupt Vector Enumeration */ +enum mcs_int_vec_e { + MCS_INT_VEC_MIL_RX_GBL = 0x0, + MCS_INT_VEC_MIL_RX_LMACX = 0x1, + MCS_INT_VEC_MIL_TX_LMACX = 0x5, + MCS_INT_VEC_HIL_RX_GBL = 0x9, + MCS_INT_VEC_HIL_RX_LMACX = 0xa, + MCS_INT_VEC_HIL_TX_GBL = 0xe, + MCS_INT_VEC_HIL_TX_LMACX = 0xf, + MCS_INT_VEC_IP = 0x13, + MCS_INT_VEC_CNT = 0x14, +}; + +#define MCS_MAX_BBE_INT 8ULL +#define MCS_BBE_INT_MASK 0xFFULL + +#define MCS_MAX_PAB_INT 4ULL +#define MCS_PAB_INT_MASK 0xFULL + +#define MCS_BBE_RX_INT_ENA BIT_ULL(0) +#define MCS_BBE_TX_INT_ENA BIT_ULL(1) +#define MCS_CPM_RX_INT_ENA BIT_ULL(2) +#define MCS_CPM_TX_INT_ENA BIT_ULL(3) +#define MCS_PAB_RX_INT_ENA BIT_ULL(4) +#define MCS_PAB_TX_INT_ENA BIT_ULL(5) + +#define MCS_CPM_TX_INT_PACKET_XPN_EQ0 BIT_ULL(0) +#define MCS_CPM_TX_INT_PN_THRESH_REACHED BIT_ULL(1) +#define MCS_CPM_TX_INT_SA_NOT_VALID BIT_ULL(2) + +#define MCS_CPM_RX_INT_SECTAG_V_EQ1 BIT_ULL(0) +#define MCS_CPM_RX_INT_SECTAG_E_EQ0_C_EQ1 BIT_ULL(1) +#define MCS_CPM_RX_INT_SL_GTE48 BIT_ULL(2) +#define MCS_CPM_RX_INT_ES_EQ1_SC_EQ1 BIT_ULL(3) +#define MCS_CPM_RX_INT_SC_EQ1_SCB_EQ1 BIT_ULL(4) +#define MCS_CPM_RX_INT_PACKET_XPN_EQ0 BIT_ULL(5) +#define MCS_CPM_RX_INT_PN_THRESH_REACHED BIT_ULL(6) + +#define MCS_CPM_RX_INT_ALL (MCS_CPM_RX_INT_SECTAG_V_EQ1 | \ + MCS_CPM_RX_INT_SECTAG_E_EQ0_C_EQ1 | \ + MCS_CPM_RX_INT_SL_GTE48 | \ + MCS_CPM_RX_INT_ES_EQ1_SC_EQ1 | \ + MCS_CPM_RX_INT_SC_EQ1_SCB_EQ1 | \ + MCS_CPM_RX_INT_PACKET_XPN_EQ0 | \ + MCS_CPM_RX_INT_PN_THRESH_REACHED) + +struct mcs_pfvf { + u64 intr_mask; /* Enabled Interrupt mask */ +}; + +struct mcs_intr_event { + u16 pcifunc; + u64 intr_mask; + u64 sa_id; + u8 mcs_id; + u8 lmac_id; +}; + +struct mcs_intrq_entry { + struct list_head node; + struct mcs_intr_event intr_event; +}; + +struct secy_mem_map { + u8 flow_id; + u8 secy; + u8 ctrl_pkt; + u8 sc; + u64 sci; +}; + +struct mcs_rsrc_map { + u16 *flowid2pf_map; + u16 *secy2pf_map; + u16 *sc2pf_map; + u16 *sa2pf_map; + u16 *flowid2secy_map; /* bitmap flowid mapped to secy*/ + u16 *ctrlpktrule2pf_map; + struct rsrc_bmap flow_ids; + struct rsrc_bmap secy; + struct rsrc_bmap sc; + struct rsrc_bmap sa; + struct rsrc_bmap ctrlpktrule; +}; + +struct hwinfo { + u8 tcam_entries; + u8 secy_entries; + u8 sc_entries; + u16 sa_entries; + u8 mcs_x2p_intf; + u8 lmac_cnt; + u8 mcs_blks; + unsigned long lmac_bmap; /* bitmap of enabled mcs lmac */ +}; + +struct mcs { + void __iomem *reg_base; + struct pci_dev *pdev; + struct device *dev; + struct hwinfo *hw; + struct mcs_rsrc_map tx; + struct mcs_rsrc_map rx; + u16 pf_map[MCS_MAX_PFS]; /* List of PCIFUNC mapped to MCS */ + u8 mcs_id; + struct mcs_ops *mcs_ops; + struct list_head mcs_list; + /* Lock for mcs stats */ + struct mutex stats_lock; + struct mcs_pfvf *pf; + struct mcs_pfvf *vf; + u16 num_vec; + void *rvu; + u16 *tx_sa_active; +}; + +struct mcs_ops { + void (*mcs_set_hw_capabilities)(struct mcs *mcs); + void (*mcs_parser_cfg)(struct mcs *mcs); + void (*mcs_tx_sa_mem_map_write)(struct mcs *mcs, struct mcs_tx_sc_sa_map *map); + void (*mcs_rx_sa_mem_map_write)(struct mcs *mcs, struct mcs_rx_sc_sa_map *map); + void (*mcs_flowid_secy_map)(struct mcs *mcs, struct secy_mem_map *map, int dir); +}; + +extern struct pci_driver mcs_driver; + +static inline void mcs_reg_write(struct mcs *mcs, u64 offset, u64 val) +{ + writeq(val, mcs->reg_base + offset); +} + +static inline u64 mcs_reg_read(struct mcs *mcs, u64 offset) +{ + return readq(mcs->reg_base + offset); +} + +/* MCS APIs */ +struct mcs *mcs_get_pdata(int mcs_id); +int mcs_get_blkcnt(void); +int mcs_set_lmac_channels(int mcs_id, u16 base); +int mcs_alloc_rsrc(struct rsrc_bmap *rsrc, u16 *pf_map, u16 pcifunc); +int mcs_free_rsrc(struct rsrc_bmap *rsrc, u16 *pf_map, int rsrc_id, u16 pcifunc); +int mcs_alloc_all_rsrc(struct mcs *mcs, u8 *flowid, u8 *secy_id, + u8 *sc_id, u8 *sa1_id, u8 *sa2_id, u16 pcifunc, int dir); +int mcs_free_all_rsrc(struct mcs *mcs, int dir, u16 pcifunc); +void mcs_clear_secy_plcy(struct mcs *mcs, int secy_id, int dir); +void mcs_ena_dis_flowid_entry(struct mcs *mcs, int id, int dir, int ena); +void mcs_ena_dis_sc_cam_entry(struct mcs *mcs, int id, int ena); +void mcs_flowid_entry_write(struct mcs *mcs, u64 *data, u64 *mask, int id, int dir); +void mcs_secy_plcy_write(struct mcs *mcs, u64 plcy, int id, int dir); +void mcs_rx_sc_cam_write(struct mcs *mcs, u64 sci, u64 secy, int sc_id); +void mcs_sa_plcy_write(struct mcs *mcs, u64 *plcy, int sa, int dir); +void mcs_map_sc_to_sa(struct mcs *mcs, u64 *sa_map, int sc, int dir); +void mcs_pn_table_write(struct mcs *mcs, u8 pn_id, u64 next_pn, u8 dir); +void mcs_tx_sa_mem_map_write(struct mcs *mcs, struct mcs_tx_sc_sa_map *map); +void mcs_flowid_secy_map(struct mcs *mcs, struct secy_mem_map *map, int dir); +void mcs_rx_sa_mem_map_write(struct mcs *mcs, struct mcs_rx_sc_sa_map *map); +void mcs_pn_threshold_set(struct mcs *mcs, struct mcs_set_pn_threshold *pn); +int mcs_install_flowid_bypass_entry(struct mcs *mcs); +void mcs_set_lmac_mode(struct mcs *mcs, int lmac_id, u8 mode); +void mcs_reset_port(struct mcs *mcs, u8 port_id, u8 reset); +void mcs_set_port_cfg(struct mcs *mcs, struct mcs_port_cfg_set_req *req); +void mcs_get_port_cfg(struct mcs *mcs, struct mcs_port_cfg_get_req *req, + struct mcs_port_cfg_get_rsp *rsp); +void mcs_get_custom_tag_cfg(struct mcs *mcs, struct mcs_custom_tag_cfg_get_req *req, + struct mcs_custom_tag_cfg_get_rsp *rsp); +int mcs_alloc_ctrlpktrule(struct rsrc_bmap *rsrc, u16 *pf_map, u16 offset, u16 pcifunc); +int mcs_free_ctrlpktrule(struct mcs *mcs, struct mcs_free_ctrl_pkt_rule_req *req); +int mcs_ctrlpktrule_write(struct mcs *mcs, struct mcs_ctrl_pkt_rule_write_req *req); + +/* CN10K-B APIs */ +void cn10kb_mcs_set_hw_capabilities(struct mcs *mcs); +void cn10kb_mcs_tx_sa_mem_map_write(struct mcs *mcs, struct mcs_tx_sc_sa_map *map); +void cn10kb_mcs_flowid_secy_map(struct mcs *mcs, struct secy_mem_map *map, int dir); +void cn10kb_mcs_rx_sa_mem_map_write(struct mcs *mcs, struct mcs_rx_sc_sa_map *map); +void cn10kb_mcs_parser_cfg(struct mcs *mcs); + +/* CNF10K-B APIs */ +struct mcs_ops *cnf10kb_get_mac_ops(void); +void cnf10kb_mcs_set_hw_capabilities(struct mcs *mcs); +void cnf10kb_mcs_tx_sa_mem_map_write(struct mcs *mcs, struct mcs_tx_sc_sa_map *map); +void cnf10kb_mcs_flowid_secy_map(struct mcs *mcs, struct secy_mem_map *map, int dir); +void cnf10kb_mcs_rx_sa_mem_map_write(struct mcs *mcs, struct mcs_rx_sc_sa_map *map); +void cnf10kb_mcs_parser_cfg(struct mcs *mcs); +void cnf10kb_mcs_tx_pn_thresh_reached_handler(struct mcs *mcs); +void cnf10kb_mcs_tx_pn_wrapped_handler(struct mcs *mcs); + +/* Stats APIs */ +void mcs_get_sc_stats(struct mcs *mcs, struct mcs_sc_stats *stats, int id, int dir); +void mcs_get_sa_stats(struct mcs *mcs, struct mcs_sa_stats *stats, int id, int dir); +void mcs_get_port_stats(struct mcs *mcs, struct mcs_port_stats *stats, int id, int dir); +void mcs_get_flowid_stats(struct mcs *mcs, struct mcs_flowid_stats *stats, int id, int dir); +void mcs_get_rx_secy_stats(struct mcs *mcs, struct mcs_secy_stats *stats, int id); +void mcs_get_tx_secy_stats(struct mcs *mcs, struct mcs_secy_stats *stats, int id); +void mcs_clear_stats(struct mcs *mcs, u8 type, u8 id, int dir); +int mcs_clear_all_stats(struct mcs *mcs, u16 pcifunc, int dir); +int mcs_set_force_clk_en(struct mcs *mcs, bool set); + +int mcs_add_intr_wq_entry(struct mcs *mcs, struct mcs_intr_event *event); + +#endif /* MCS_H */ diff --git a/drivers/net/ethernet/marvell/octeontx2/af/mcs_cnf10kb.c b/drivers/net/ethernet/marvell/octeontx2/af/mcs_cnf10kb.c new file mode 100644 index 000000000000..7b6205414428 --- /dev/null +++ b/drivers/net/ethernet/marvell/octeontx2/af/mcs_cnf10kb.c @@ -0,0 +1,214 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Marvell MCS driver + * + * Copyright (C) 2022 Marvell. + */ + +#include "mcs.h" +#include "mcs_reg.h" + +static struct mcs_ops cnf10kb_mcs_ops = { + .mcs_set_hw_capabilities = cnf10kb_mcs_set_hw_capabilities, + .mcs_parser_cfg = cnf10kb_mcs_parser_cfg, + .mcs_tx_sa_mem_map_write = cnf10kb_mcs_tx_sa_mem_map_write, + .mcs_rx_sa_mem_map_write = cnf10kb_mcs_rx_sa_mem_map_write, + .mcs_flowid_secy_map = cnf10kb_mcs_flowid_secy_map, +}; + +struct mcs_ops *cnf10kb_get_mac_ops(void) +{ + return &cnf10kb_mcs_ops; +} + +void cnf10kb_mcs_set_hw_capabilities(struct mcs *mcs) +{ + struct hwinfo *hw = mcs->hw; + + hw->tcam_entries = 64; /* TCAM entries */ + hw->secy_entries = 64; /* SecY entries */ + hw->sc_entries = 64; /* SC CAM entries */ + hw->sa_entries = 128; /* SA entries */ + hw->lmac_cnt = 4; /* lmacs/ports per mcs block */ + hw->mcs_x2p_intf = 1; /* x2p clabration intf */ + hw->mcs_blks = 7; /* MCS blocks */ +} + +void cnf10kb_mcs_parser_cfg(struct mcs *mcs) +{ + u64 reg, val; + + /* VLAN Ctag */ + val = (0x8100ull & 0xFFFF) | BIT_ULL(20) | BIT_ULL(22); + + reg = MCSX_PEX_RX_SLAVE_CUSTOM_TAGX(0); + mcs_reg_write(mcs, reg, val); + + reg = MCSX_PEX_TX_SLAVE_CUSTOM_TAGX(0); + mcs_reg_write(mcs, reg, val); + + /* VLAN STag */ + val = (0x88a8ull & 0xFFFF) | BIT_ULL(20) | BIT_ULL(23); + + /* RX */ + reg = MCSX_PEX_RX_SLAVE_CUSTOM_TAGX(1); + mcs_reg_write(mcs, reg, val); + + /* TX */ + reg = MCSX_PEX_TX_SLAVE_CUSTOM_TAGX(1); + mcs_reg_write(mcs, reg, val); + + /* Enable custom tage 0 and 1 and sectag */ + val = BIT_ULL(0) | BIT_ULL(1) | BIT_ULL(12); + + reg = MCSX_PEX_RX_SLAVE_ETYPE_ENABLE; + mcs_reg_write(mcs, reg, val); + + reg = MCSX_PEX_TX_SLAVE_ETYPE_ENABLE; + mcs_reg_write(mcs, reg, val); +} + +void cnf10kb_mcs_flowid_secy_map(struct mcs *mcs, struct secy_mem_map *map, int dir) +{ + u64 reg, val; + + val = (map->secy & 0x3F) | (map->ctrl_pkt & 0x1) << 6; + if (dir == MCS_RX) { + reg = MCSX_CPM_RX_SLAVE_SECY_MAP_MEMX(map->flow_id); + } else { + reg = MCSX_CPM_TX_SLAVE_SECY_MAP_MEM_0X(map->flow_id); + mcs_reg_write(mcs, reg, map->sci); + val |= (map->sc & 0x3F) << 7; + reg = MCSX_CPM_TX_SLAVE_SECY_MAP_MEM_1X(map->flow_id); + } + + mcs_reg_write(mcs, reg, val); +} + +void cnf10kb_mcs_tx_sa_mem_map_write(struct mcs *mcs, struct mcs_tx_sc_sa_map *map) +{ + u64 reg, val; + + val = (map->sa_index0 & 0x7F) | (map->sa_index1 & 0x7F) << 7; + + reg = MCSX_CPM_TX_SLAVE_SA_MAP_MEM_0X(map->sc_id); + mcs_reg_write(mcs, reg, val); + + reg = MCSX_CPM_TX_SLAVE_AUTO_REKEY_ENABLE_0; + val = mcs_reg_read(mcs, reg); + + if (map->rekey_ena) + val |= BIT_ULL(map->sc_id); + else + val &= ~BIT_ULL(map->sc_id); + + mcs_reg_write(mcs, reg, val); + + mcs_reg_write(mcs, MCSX_CPM_TX_SLAVE_SA_INDEX0_VLDX(map->sc_id), map->sa_index0_vld); + mcs_reg_write(mcs, MCSX_CPM_TX_SLAVE_SA_INDEX1_VLDX(map->sc_id), map->sa_index1_vld); + + mcs_reg_write(mcs, MCSX_CPM_TX_SLAVE_TX_SA_ACTIVEX(map->sc_id), map->tx_sa_active); +} + +void cnf10kb_mcs_rx_sa_mem_map_write(struct mcs *mcs, struct mcs_rx_sc_sa_map *map) +{ + u64 val, reg; + + val = (map->sa_index & 0x7F) | (map->sa_in_use << 7); + + reg = MCSX_CPM_RX_SLAVE_SA_MAP_MEMX((4 * map->sc_id) + map->an); + mcs_reg_write(mcs, reg, val); +} + +int mcs_set_force_clk_en(struct mcs *mcs, bool set) +{ + unsigned long timeout = jiffies + usecs_to_jiffies(2000); + u64 val; + + val = mcs_reg_read(mcs, MCSX_MIL_GLOBAL); + + if (set) { + val |= BIT_ULL(4); + mcs_reg_write(mcs, MCSX_MIL_GLOBAL, val); + + /* Poll till mcsx_mil_ip_gbl_status.mcs_ip_stats_ready value is 1 */ + while (!(mcs_reg_read(mcs, MCSX_MIL_IP_GBL_STATUS) & BIT_ULL(0))) { + if (time_after(jiffies, timeout)) { + dev_err(mcs->dev, "MCS set force clk enable failed\n"); + break; + } + } + } else { + val &= ~BIT_ULL(4); + mcs_reg_write(mcs, MCSX_MIL_GLOBAL, val); + } + + return 0; +} + +/* TX SA interrupt is raised only if autorekey is enabled. + * MCS_CPM_TX_SLAVE_SA_MAP_MEM_0X[sc].tx_sa_active bit gets toggled if + * one of two SAs mapped to SC gets expired. If tx_sa_active=0 implies + * SA in SA_index1 got expired else SA in SA_index0 got expired. + */ +void cnf10kb_mcs_tx_pn_thresh_reached_handler(struct mcs *mcs) +{ + struct mcs_intr_event event; + struct rsrc_bmap *sc_bmap; + unsigned long rekey_ena; + u64 val, sa_status; + int sc; + + sc_bmap = &mcs->tx.sc; + + event.mcs_id = mcs->mcs_id; + event.intr_mask = MCS_CPM_TX_PN_THRESH_REACHED_INT; + + rekey_ena = mcs_reg_read(mcs, MCSX_CPM_TX_SLAVE_AUTO_REKEY_ENABLE_0); + + for_each_set_bit(sc, sc_bmap->bmap, mcs->hw->sc_entries) { + /* Auto rekey is enable */ + if (!test_bit(sc, &rekey_ena)) + continue; + sa_status = mcs_reg_read(mcs, MCSX_CPM_TX_SLAVE_TX_SA_ACTIVEX(sc)); + /* Check if tx_sa_active status had changed */ + if (sa_status == mcs->tx_sa_active[sc]) + continue; + + /* SA_index0 is expired */ + val = mcs_reg_read(mcs, MCSX_CPM_TX_SLAVE_SA_MAP_MEM_0X(sc)); + if (sa_status) + event.sa_id = val & 0x7F; + else + event.sa_id = (val >> 7) & 0x7F; + + event.pcifunc = mcs->tx.sa2pf_map[event.sa_id]; + mcs_add_intr_wq_entry(mcs, &event); + } +} + +void cnf10kb_mcs_tx_pn_wrapped_handler(struct mcs *mcs) +{ + struct mcs_intr_event event = { 0 }; + struct rsrc_bmap *sc_bmap; + u64 val; + int sc; + + sc_bmap = &mcs->tx.sc; + + event.mcs_id = mcs->mcs_id; + event.intr_mask = MCS_CPM_TX_PACKET_XPN_EQ0_INT; + + for_each_set_bit(sc, sc_bmap->bmap, mcs->hw->sc_entries) { + val = mcs_reg_read(mcs, MCSX_CPM_TX_SLAVE_SA_MAP_MEM_0X(sc)); + + if (mcs->tx_sa_active[sc]) + /* SA_index1 was used and got expired */ + event.sa_id = (val >> 7) & 0x7F; + else + /* SA_index0 was used and got expired */ + event.sa_id = val & 0x7F; + + event.pcifunc = mcs->tx.sa2pf_map[event.sa_id]; + mcs_add_intr_wq_entry(mcs, &event); + } +} diff --git a/drivers/net/ethernet/marvell/octeontx2/af/mcs_reg.h b/drivers/net/ethernet/marvell/octeontx2/af/mcs_reg.h new file mode 100644 index 000000000000..c95a8b8f5eaf --- /dev/null +++ b/drivers/net/ethernet/marvell/octeontx2/af/mcs_reg.h @@ -0,0 +1,1102 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Marvell MCS driver + * + * Copyright (C) 2022 Marvell. + */ + +#ifndef MCS_REG_H +#define MCS_REG_H + +#include <linux/bits.h> + +/* Registers */ +#define MCSX_IP_MODE 0x900c8ull +#define MCSX_MCS_TOP_SLAVE_PORT_RESET(a) ({ \ + u64 offset; \ + \ + offset = 0x408ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0xa28ull; \ + offset += (a) * 0x8ull; \ + offset; }) + + +#define MCSX_MCS_TOP_SLAVE_CHANNEL_CFG(a) ({ \ + u64 offset; \ + \ + offset = 0x808ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0xa68ull; \ + offset += (a) * 0x8ull; \ + offset; }) + +#define MCSX_MIL_GLOBAL ({ \ + u64 offset; \ + \ + offset = 0x80000ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0x60000ull; \ + offset; }) + +#define MCSX_MIL_RX_LMACX_CFG(a) ({ \ + u64 offset; \ + \ + offset = 0x900a8ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0x700a8ull; \ + offset += (a) * 0x800ull; \ + offset; }) + +#define MCSX_HIL_GLOBAL ({ \ + u64 offset; \ + \ + offset = 0xc0000ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0xa0000ull; \ + offset; }) + +#define MCSX_LINK_LMACX_CFG(a) ({ \ + u64 offset; \ + \ + offset = 0x90000ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0x70000ull; \ + offset += (a) * 0x800ull; \ + offset; }) + +#define MCSX_MIL_RX_GBL_STATUS ({ \ + u64 offset; \ + \ + offset = 0x800c8ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0x600c8ull; \ + offset; }) + +#define MCSX_MIL_IP_GBL_STATUS ({ \ + u64 offset; \ + \ + offset = 0x800d0ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0x600d0ull; \ + offset; }) + +/* PAB */ +#define MCSX_PAB_RX_SLAVE_PORT_CFGX(a) ({ \ + u64 offset; \ + \ + offset = 0x1718ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0x280ull; \ + offset += (a) * 0x40ull; \ + offset; }) + +#define MCSX_PAB_TX_SLAVE_PORT_CFGX(a) (0x2930ull + (a) * 0x40ull) + +/* PEX registers */ +#define MCSX_PEX_RX_SLAVE_VLAN_CFGX(a) (0x3b58ull + (a) * 0x8ull) +#define MCSX_PEX_TX_SLAVE_VLAN_CFGX(a) (0x46f8ull + (a) * 0x8ull) +#define MCSX_PEX_TX_SLAVE_CUSTOM_TAG_REL_MODE_SEL(a) (0x788ull + (a) * 0x8ull) +#define MCSX_PEX_TX_SLAVE_PORT_CONFIG(a) (0x4738ull + (a) * 0x8ull) +#define MCSX_PEX_RX_SLAVE_RULE_ETYPE_CFGX(a) ({ \ + u64 offset; \ + \ + offset = 0x3fc0ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0x558ull; \ + offset += (a) * 0x8ull; \ + offset; }) + +#define MCSX_PEX_RX_SLAVE_RULE_DAX(a) ({ \ + u64 offset; \ + \ + offset = 0x4000ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0x598ull; \ + offset += (a) * 0x8ull; \ + offset; }) + +#define MCSX_PEX_RX_SLAVE_RULE_DA_RANGE_MINX(a) ({ \ + u64 offset; \ + \ + offset = 0x4040ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0x5d8ull; \ + offset += (a) * 0x8ull; \ + offset; }) + +#define MCSX_PEX_RX_SLAVE_RULE_DA_RANGE_MAXX(a) ({ \ + u64 offset; \ + \ + offset = 0x4048ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0x5e0ull; \ + offset += (a) * 0x8ull; \ + offset; }) + +#define MCSX_PEX_RX_SLAVE_RULE_COMBO_MINX(a) ({ \ + u64 offset; \ + \ + offset = 0x4080ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0x648ull; \ + offset += (a) * 0x8ull; \ + offset; }) + +#define MCSX_PEX_RX_SLAVE_RULE_COMBO_MAXX(a) ({ \ + u64 offset; \ + \ + offset = 0x4088ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0x650ull; \ + offset += (a) * 0x8ull; \ + offset; }) + +#define MCSX_PEX_RX_SLAVE_RULE_COMBO_ETX(a) ({ \ + u64 offset; \ + \ + offset = 0x4090ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0x658ull; \ + offset += (a) * 0x8ull; \ + offset; }) + +#define MCSX_PEX_RX_SLAVE_RULE_MAC ({ \ + u64 offset; \ + \ + offset = 0x40e0ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0x6d8ull; \ + offset; }) + +#define MCSX_PEX_RX_SLAVE_RULE_ENABLE ({ \ + u64 offset; \ + \ + offset = 0x40e8ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0x6e0ull; \ + offset; }) + +#define MCSX_PEX_TX_SLAVE_RULE_ETYPE_CFGX(a) ({ \ + u64 offset; \ + \ + offset = 0x4b60ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0x7d8ull; \ + offset += (a) * 0x8ull; \ + offset; }) + +#define MCSX_PEX_TX_SLAVE_RULE_DAX(a) ({ \ + u64 offset; \ + \ + offset = 0x4ba0ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0x818ull; \ + offset += (a) * 0x8ull; \ + offset; }) + +#define MCSX_PEX_TX_SLAVE_RULE_DA_RANGE_MINX(a) ({ \ + u64 offset; \ + \ + offset = 0x4be0ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0x858ull; \ + offset += (a) * 0x8ull; \ + offset; }) + +#define MCSX_PEX_TX_SLAVE_RULE_DA_RANGE_MAXX(a) ({ \ + u64 offset; \ + \ + offset = 0x4be8ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0x860ull; \ + offset += (a) * 0x8ull; \ + offset; }) + +#define MCSX_PEX_TX_SLAVE_RULE_COMBO_MINX(a) ({ \ + u64 offset; \ + \ + offset = 0x4c20ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0x8c8ull; \ + offset += (a) * 0x8ull; \ + offset; }) + +#define MCSX_PEX_TX_SLAVE_RULE_COMBO_MAXX(a) ({ \ + u64 offset; \ + \ + offset = 0x4c28ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0x8d0ull; \ + offset += (a) * 0x8ull; \ + offset; }) + +#define MCSX_PEX_TX_SLAVE_RULE_COMBO_ETX(a) ({ \ + u64 offset; \ + \ + offset = 0x4c30ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0x8d8ull; \ + offset += (a) * 0x8ull; \ + offset; }) + +#define MCSX_PEX_TX_SLAVE_RULE_MAC ({ \ + u64 offset; \ + \ + offset = 0x4c80ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0x958ull; \ + offset; }) + +#define MCSX_PEX_TX_SLAVE_RULE_ENABLE ({ \ + u64 offset; \ + \ + offset = 0x4c88ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0x960ull; \ + offset; }) + +#define MCSX_PEX_RX_SLAVE_PEX_CONFIGURATION ({ \ + u64 offset; \ + \ + offset = 0x3b50ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0x4c0ull; \ + offset; }) + +/* CNF10K-B */ +#define MCSX_PEX_RX_SLAVE_CUSTOM_TAGX(a) (0x4c8ull + (a) * 0x8ull) +#define MCSX_PEX_TX_SLAVE_CUSTOM_TAGX(a) (0x748ull + (a) * 0x8ull) +#define MCSX_PEX_RX_SLAVE_ETYPE_ENABLE 0x6e8ull +#define MCSX_PEX_TX_SLAVE_ETYPE_ENABLE 0x968ull + +/* BEE */ +#define MCSX_BBE_RX_SLAVE_PADDING_CTL 0xe08ull +#define MCSX_BBE_TX_SLAVE_PADDING_CTL 0x12f8ull +#define MCSX_BBE_RX_SLAVE_CAL_ENTRY 0x180ull +#define MCSX_BBE_RX_SLAVE_CAL_LEN 0x188ull +#define MCSX_PAB_RX_SLAVE_FIFO_SKID_CFGX(a) (0x290ull + (a) * 0x40ull) + +#define MCSX_BBE_RX_SLAVE_BBE_INT ({ \ + u64 offset; \ + \ + offset = 0xe00ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0x160ull; \ + offset; }) + +#define MCSX_BBE_RX_SLAVE_BBE_INT_ENB ({ \ + u64 offset; \ + \ + offset = 0xe08ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0x168ull; \ + offset; }) + +#define MCSX_BBE_RX_SLAVE_BBE_INT_INTR_RW ({ \ + u64 offset; \ + \ + offset = 0xe08ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0x178ull; \ + offset; }) + +#define MCSX_BBE_TX_SLAVE_BBE_INT ({ \ + u64 offset; \ + \ + offset = 0x1278ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0x1e0ull; \ + offset; }) + +#define MCSX_BBE_TX_SLAVE_BBE_INT_INTR_RW ({ \ + u64 offset; \ + \ + offset = 0x1278ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0x1f8ull; \ + offset; }) + +#define MCSX_BBE_TX_SLAVE_BBE_INT_ENB ({ \ + u64 offset; \ + \ + offset = 0x1280ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0x1e8ull; \ + offset; }) + +#define MCSX_PAB_RX_SLAVE_PAB_INT ({ \ + u64 offset; \ + \ + offset = 0x16f0ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0x260ull; \ + offset; }) + +#define MCSX_PAB_RX_SLAVE_PAB_INT_ENB ({ \ + u64 offset; \ + \ + offset = 0x16f8ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0x268ull; \ + offset; }) + +#define MCSX_PAB_RX_SLAVE_PAB_INT_INTR_RW ({ \ + u64 offset; \ + \ + offset = 0x16f8ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0x278ull; \ + offset; }) + +#define MCSX_PAB_TX_SLAVE_PAB_INT ({ \ + u64 offset; \ + \ + offset = 0x2908ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0x380ull; \ + offset; }) + +#define MCSX_PAB_TX_SLAVE_PAB_INT_ENB ({ \ + u64 offset; \ + \ + offset = 0x2910ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0x388ull; \ + offset; }) + +#define MCSX_PAB_TX_SLAVE_PAB_INT_INTR_RW ({ \ + u64 offset; \ + \ + offset = 0x16f8ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0x398ull; \ + offset; }) + +/* CPM registers */ +#define MCSX_CPM_RX_SLAVE_FLOWID_TCAM_DATAX(a, b) ({ \ + u64 offset; \ + \ + offset = 0x30740ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0x3bf8ull; \ + offset += (a) * 0x8ull + (b) * 0x20ull; \ + offset; }) + +#define MCSX_CPM_RX_SLAVE_FLOWID_TCAM_MASKX(a, b) ({ \ + u64 offset; \ + \ + offset = 0x34740ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0x43f8ull; \ + offset += (a) * 0x8ull + (b) * 0x20ull; \ + offset; }) + +#define MCSX_CPM_RX_SLAVE_FLOWID_TCAM_ENA_0 ({ \ + u64 offset; \ + \ + offset = 0x30700ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0x3bd8ull; \ + offset; }) + +#define MCSX_CPM_RX_SLAVE_SC_CAMX(a, b) ({ \ + u64 offset; \ + \ + offset = 0x38780ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0x4c08ull; \ + offset += (a) * 0x8ull + (b) * 0x10ull; \ + offset; }) + +#define MCSX_CPM_RX_SLAVE_SC_CAM_ENA(a) ({ \ + u64 offset; \ + \ + offset = 0x38740ull + (a) * 0x8ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0x4bf8ull; \ + offset; }) + +#define MCSX_CPM_RX_SLAVE_SECY_MAP_MEMX(a) ({ \ + u64 offset; \ + \ + offset = 0x23ee0ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0xbd0ull; \ + offset += (a) * 0x8ull; \ + offset; }) + +#define MCSX_CPM_RX_SLAVE_SECY_PLCY_MEM_0X(a) ({ \ + u64 offset; \ + \ + offset = (0x246e0ull + (a) * 0x10ull); \ + if (mcs->hw->mcs_blks > 1) \ + offset = (0xdd0ull + (a) * 0x8ull); \ + offset; }) + +#define MCSX_CPM_RX_SLAVE_SA_KEY_LOCKOUTX(a) ({ \ + u64 offset; \ + \ + offset = 0x23E90ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0xbb0ull; \ + offset += (a) * 0x8ull; \ + offset; }) + +#define MCSX_CPM_RX_SLAVE_SA_MAP_MEMX(a) ({ \ + u64 offset; \ + \ + offset = 0x256e0ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0xfd0ull; \ + offset += (a) * 0x8ull; \ + offset; }) + +#define MCSX_CPM_RX_SLAVE_SA_PLCY_MEMX(a, b) ({ \ + u64 offset; \ + \ + offset = 0x27700ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0x17d8ull; \ + offset += (a) * 0x8ull + (b) * 0x40ull; \ + offset; }) + +#define MCSX_CPM_RX_SLAVE_SA_PN_TABLE_MEMX(a) ({ \ + u64 offset; \ + \ + offset = 0x2f700ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0x37d8; \ + offset += (a) * 0x8ull; \ + offset; }) + +#define MCSX_CPM_RX_SLAVE_XPN_THRESHOLD ({ \ + u64 offset; \ + \ + offset = 0x23e40ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0xb90ull; \ + offset; }) + +#define MCSX_CPM_RX_SLAVE_PN_THRESHOLD ({ \ + u64 offset; \ + \ + offset = 0x23e48ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0xb98ull; \ + offset; }) + +#define MCSX_CPM_RX_SLAVE_PN_THRESH_REACHEDX(a) ({ \ + u64 offset; \ + \ + offset = 0x23e50ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0xba0ull; \ + offset += (a) * 0x8ull; \ + offset; }) + +#define MCSX_CPM_RX_SLAVE_FLOWID_TCAM_ENA_1 0x30708ull +#define MCSX_CPM_RX_SLAVE_SECY_PLCY_MEM_1X(a) (0x246e8ull + (a) * 0x10ull) + +/* TX registers */ +#define MCSX_CPM_TX_SLAVE_FLOWID_TCAM_DATAX(a, b) ({ \ + u64 offset; \ + \ + offset = 0x51d50ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0xa7c0ull; \ + offset += (a) * 0x8ull + (b) * 0x20ull; \ + offset; }) + +#define MCSX_CPM_TX_SLAVE_FLOWID_TCAM_MASKX(a, b) ({ \ + u64 offset; \ + \ + offset = 0x55d50ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0xafc0ull; \ + offset += (a) * 0x8ull + (b) * 0x20ull; \ + offset; }) + +#define MCSX_CPM_TX_SLAVE_FLOWID_TCAM_ENA_0 ({ \ + u64 offset; \ + \ + offset = 0x51d10ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0xa7a0ull; \ + offset; }) + +#define MCSX_CPM_TX_SLAVE_SECY_MAP_MEM_0X(a) ({ \ + u64 offset; \ + \ + offset = 0x3e508ull + (a) * 0x8ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0x5550ull + (a) * 0x10ull; \ + offset; }) + +#define MCSX_CPM_TX_SLAVE_SECY_PLCY_MEMX(a) ({ \ + u64 offset; \ + \ + offset = 0x3ed08ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0x5950ull; \ + offset += (a) * 0x8ull; \ + offset; }) + +#define MCSX_CPM_TX_SLAVE_SA_KEY_LOCKOUTX(a) ({ \ + u64 offset; \ + \ + offset = 0x3e4c0ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0x5538ull; \ + offset += (a) * 0x8ull; \ + offset; }) + +#define MCSX_CPM_TX_SLAVE_SA_MAP_MEM_0X(a) ({ \ + u64 offset; \ + \ + offset = 0x3fd10ull + (a) * 0x10ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0x6150ull + (a) * 0x8ull; \ + offset; }) + +#define MCSX_CPM_TX_SLAVE_SA_PLCY_MEMX(a, b) ({ \ + u64 offset; \ + \ + offset = 0x40d10ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0x63a0ull; \ + offset += (a) * 0x8ull + (b) * 0x80ull; \ + offset; }) + +#define MCSX_CPM_TX_SLAVE_SA_PN_TABLE_MEMX(a) ({ \ + u64 offset; \ + \ + offset = 0x50d10ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0xa3a0ull; \ + offset += (a) * 0x8ull; \ + offset; }) + +#define MCSX_CPM_TX_SLAVE_XPN_THRESHOLD ({ \ + u64 offset; \ + \ + offset = 0x3e4b0ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0x5528ull; \ + offset; }) + +#define MCSX_CPM_TX_SLAVE_PN_THRESHOLD ({ \ + u64 offset; \ + \ + offset = 0x3e4b8ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0x5530ull; \ + offset; }) + +#define MCSX_CPM_TX_SLAVE_SA_MAP_MEM_1X(a) (0x3fd18ull + (a) * 0x10ull) +#define MCSX_CPM_TX_SLAVE_SECY_MAP_MEM_1X(a) (0x5558ull + (a) * 0x10ull) +#define MCSX_CPM_TX_SLAVE_FLOWID_TCAM_ENA_1 0x51d18ull +#define MCSX_CPM_TX_SLAVE_TX_SA_ACTIVEX(a) (0x5b50 + (a) * 0x8ull) +#define MCSX_CPM_TX_SLAVE_SA_INDEX0_VLDX(a) (0x5d50 + (a) * 0x8ull) +#define MCSX_CPM_TX_SLAVE_SA_INDEX1_VLDX(a) (0x5f50 + (a) * 0x8ull) +#define MCSX_CPM_TX_SLAVE_AUTO_REKEY_ENABLE_0 0x5500ull + +/* CSE */ +#define MCSX_CSE_RX_MEM_SLAVE_IFINCTLBCPKTSX(a) ({ \ + u64 offset; \ + \ + offset = 0x9e80ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0xc218ull; \ + offset += (a) * 0x8ull; \ + offset; }) + +#define MCSX_CSE_RX_MEM_SLAVE_IFINCTLMCPKTSX(a) ({ \ + u64 offset; \ + \ + offset = 0x9680ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0xc018ull; \ + offset += (a) * 0x8ull; \ + offset; }) + +#define MCSX_CSE_RX_MEM_SLAVE_IFINCTLOCTETSX(a) ({ \ + u64 offset; \ + \ + offset = 0x6e80ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0xbc18ull; \ + offset += (a) * 0x8ull; \ + offset; }) + +#define MCSX_CSE_RX_MEM_SLAVE_IFINCTLUCPKTSX(a) ({ \ + u64 offset; \ + \ + offset = 0x8e80ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0xbe18ull; \ + offset += (a) * 0x8ull; \ + offset; }) + +#define MCSX_CSE_RX_MEM_SLAVE_IFINUNCTLBCPKTSX(a) ({ \ + u64 offset; \ + \ + offset = 0x8680ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0xca18ull; \ + offset += (a) * 0x8ull; \ + offset; }) + +#define MCSX_CSE_RX_MEM_SLAVE_IFINUNCTLMCPKTSX(a) ({ \ + u64 offset; \ + \ + offset = 0x7e80ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0xc818ull; \ + offset += (a) * 0x8ull; \ + offset; }) + +#define MCSX_CSE_RX_MEM_SLAVE_IFINUNCTLOCTETSX(a) ({ \ + u64 offset; \ + \ + offset = 0x6680ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0xc418ull; \ + offset += (a) * 0x8ull; \ + offset; }) + +#define MCSX_CSE_RX_MEM_SLAVE_IFINUNCTLUCPKTSX(a) ({ \ + u64 offset; \ + \ + offset = 0x7680ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0xc618ull; \ + offset += (a) * 0x8ull; \ + offset; }) + +#define MCSX_CSE_RX_MEM_SLAVE_INOCTETSSECYDECRYPTEDX(a) ({ \ + u64 offset; \ + \ + offset = 0x5e80ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0xdc18ull; \ + offset += (a) * 0x8ull; \ + offset; }) + +#define MCSX_CSE_RX_MEM_SLAVE_INOCTETSSECYVALIDATEX(a)({ \ + u64 offset; \ + \ + offset = 0x5680ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0xda18ull; \ + offset += (a) * 0x8ull; \ + offset; }) + +#define MCSX_CSE_RX_MEM_SLAVE_INPKTSCTRLPORTDISABLEDX(a) ({ \ + u64 offset; \ + \ + offset = 0xd680ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0xce18ull; \ + offset += (a) * 0x8ull; \ + offset; }) + +#define MCSX_CSE_RX_MEM_SLAVE_INPKTSFLOWIDTCAMHITX(a) ({ \ + u64 offset; \ + \ + offset = 0x16a80ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0xec78ull; \ + offset += (a) * 0x8ull; \ + offset; }) + +#define MCSX_CSE_RX_MEM_SLAVE_INPKTSFLOWIDTCAMMISSX(a) ({ \ + u64 offset; \ + \ + offset = 0x16680ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0xec38ull; \ + offset += (a) * 0x8ull; \ + offset; }) + +#define MCSX_CSE_RX_MEM_SLAVE_INPKTSPARSEERRX(a) ({ \ + u64 offset; \ + \ + offset = 0x16880ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0xec18ull; \ + offset += (a) * 0x8ull; \ + offset; }) + +#define MCSX_CSE_RX_MEM_SLAVE_INPKTSSCCAMHITX(a) ({ \ + u64 offset; \ + \ + offset = 0xfe80ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0xde18ull; \ + offset += (a) * 0x8ull; \ + offset; }) + +#define MCSX_CSE_RX_MEM_SLAVE_INPKTSSCINVALIDX(a) ({ \ + u64 offset; \ + \ + offset = 0x10680ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0xe418ull; \ + offset += (a) * 0x8ull; \ + offset; }) + +#define MCSX_CSE_RX_MEM_SLAVE_INPKTSSCNOTVALIDX(a) ({ \ + u64 offset; \ + \ + offset = 0x10e80ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0xe218ull; \ + offset += (a) * 0x8ull; \ + offset; }) + +#define MCSX_CSE_RX_MEM_SLAVE_INPKTSSECYBADTAGX(a) ({ \ + u64 offset; \ + \ + offset = 0xae80ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0xd418ull; \ + offset += (a) * 0x8ull; \ + offset; }) + +#define MCSX_CSE_RX_MEM_SLAVE_INPKTSSECYNOSAX(a) ({ \ + u64 offset; \ + \ + offset = 0xc680ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0xd618ull; \ + offset += (a) * 0x8ull; \ + offset; }) + +#define MCSX_CSE_RX_MEM_SLAVE_INPKTSSECYNOSAERRORX(a) ({ \ + u64 offset; \ + \ + offset = 0xce80ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0xd818ull; \ + offset += (a) * 0x8ull; \ + offset; }) + +#define MCSX_CSE_RX_MEM_SLAVE_INPKTSSECYTAGGEDCTLX(a) ({ \ + u64 offset; \ + \ + offset = 0xbe80ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0xcc18ull; \ + offset += (a) * 0x8ull; \ + offset; }) + +#define MCSX_CSE_RX_SLAVE_CTRL ({ \ + u64 offset; \ + \ + offset = 0x52a0ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0x9c0ull; \ + offset; }) + +#define MCSX_CSE_RX_SLAVE_STATS_CLEAR ({ \ + u64 offset; \ + \ + offset = 0x52b8ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0x9d8ull; \ + offset; }) + +#define MCSX_CSE_RX_MEM_SLAVE_INOCTETSSCDECRYPTEDX(a) (0xe680ull + (a) * 0x8ull) +#define MCSX_CSE_RX_MEM_SLAVE_INOCTETSSCVALIDATEX(a) (0xde80ull + (a) * 0x8ull) +#define MCSX_CSE_RX_MEM_SLAVE_INPKTSSECYUNTAGGEDORNOTAGX(a) (0xa680ull + (a) * 0x8ull) +#define MCSX_CSE_RX_MEM_SLAVE_INPKTSSECYNOTAGX(a) (0xd218 + (a) * 0x8ull) +#define MCSX_CSE_RX_MEM_SLAVE_INPKTSSECYUNTAGGEDX(a) (0xd018ull + (a) * 0x8ull) +#define MCSX_CSE_RX_MEM_SLAVE_INPKTSSCUNCHECKEDOROKX(a) (0xee80ull + (a) * 0x8ull) +#define MCSX_CSE_RX_MEM_SLAVE_INPKTSSECYCTLX(a) (0xb680ull + (a) * 0x8ull) +#define MCSX_CSE_RX_MEM_SLAVE_INPKTSSCLATEORDELAYEDX(a) (0xf680ull + (a) * 0x8ull) +#define MCSX_CSE_RX_MEM_SLAVE_INPKTSSAINVALIDX(a) (0x12680ull + (a) * 0x8ull) +#define MCSX_CSE_RX_MEM_SLAVE_INPKTSSANOTUSINGSAERRORX(a) (0x15680ull + (a) * 0x8ull) +#define MCSX_CSE_RX_MEM_SLAVE_INPKTSSANOTVALIDX(a) (0x13680ull + (a) * 0x8ull) +#define MCSX_CSE_RX_MEM_SLAVE_INPKTSSAOKX(a) (0x11680ull + (a) * 0x8ull) +#define MCSX_CSE_RX_MEM_SLAVE_INPKTSSAUNUSEDSAX(a) (0x14680ull + (a) * 0x8ull) +#define MCSX_CSE_RX_MEM_SLAVE_INPKTSEARLYPREEMPTERRX(a) (0xec58ull + (a) * 0x8ull) +#define MCSX_CSE_RX_MEM_SLAVE_INPKTSSCOKX(a) (0xea18ull + (a) * 0x8ull) +#define MCSX_CSE_RX_MEM_SLAVE_INPKTSSCDELAYEDX(a) (0xe618ull + (a) * 0x8ull) + +/* CSE TX */ +#define MCSX_CSE_TX_MEM_SLAVE_IFOUTCOMMONOCTETSX(a) (0x18440ull + (a) * 0x8ull) +#define MCSX_CSE_TX_MEM_SLAVE_IFOUTCTLBCPKTSX(a) ({ \ + u64 offset; \ + \ + offset = 0x1c440ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0xf478ull; \ + offset += (a) * 0x8ull; \ + offset; }) + +#define MCSX_CSE_TX_MEM_SLAVE_IFOUTCTLMCPKTSX(a) ({ \ + u64 offset; \ + \ + offset = 0x1bc40ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0xf278ull; \ + offset += (a) * 0x8ull; \ + offset; }) + +#define MCSX_CSE_TX_MEM_SLAVE_IFOUTCTLOCTETSX(a) ({ \ + u64 offset; \ + \ + offset = 0x19440ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0xee78ull; \ + offset += (a) * 0x8ull; \ + offset; }) + +#define MCSX_CSE_TX_MEM_SLAVE_IFOUTCTLUCPKTSX(a) ({ \ + u64 offset; \ + \ + offset = 0x1b440ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0xf078ull; \ + offset += (a) * 0x8ull; \ + offset; }) + +#define MCSX_CSE_TX_MEM_SLAVE_IFOUTUNCTLBCPKTSX(a) ({ \ + u64 offset; \ + \ + offset = 0x1ac40ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0xfc78ull; \ + offset += (a) * 0x8ull; \ + offset; }) + +#define MCSX_CSE_TX_MEM_SLAVE_IFOUTUNCTLMCPKTSX(a) ({ \ + u64 offset; \ + \ + offset = 0x1a440ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0xfa78ull; \ + offset += (a) * 0x8ull; \ + offset; }) + +#define MCSX_CSE_TX_MEM_SLAVE_IFOUTUNCTLOCTETSX(a) ({ \ + u64 offset; \ + \ + offset = 0x18c40ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0xf678ull; \ + offset += (a) * 0x8ull; \ + offset; }) + +#define MCSX_CSE_TX_MEM_SLAVE_IFOUTUNCTLUCPKTSX(a) ({ \ + u64 offset; \ + \ + offset = 0x19c40ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0xf878ull; \ + offset += (a) * 0x8ull; \ + offset; }) + +#define MCSX_CSE_TX_MEM_SLAVE_OUTOCTETSSECYENCRYPTEDX(a) ({ \ + u64 offset; \ + \ + offset = 0x17c40ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0x10878ull; \ + offset += (a) * 0x8ull; \ + offset; }) + +#define MCSX_CSE_TX_MEM_SLAVE_OUTOCTETSSECYPROTECTEDX(a) ({ \ + u64 offset; \ + \ + offset = 0x17440ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0x10678ull; \ + offset += (a) * 0x8ull; \ + offset; }) + +#define MCSX_CSE_TX_MEM_SLAVE_OUTPKTSCTRLPORTDISABLEDX(a) ({ \ + u64 offset; \ + \ + offset = 0x1e440ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0xfe78ull; \ + offset += (a) * 0x8ull; \ + offset; }) + +#define MCSX_CSE_TX_MEM_SLAVE_OUTPKTSFLOWIDTCAMHITX(a) ({ \ + u64 offset; \ + \ + offset = 0x23240ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0x10ed8ull; \ + offset += (a) * 0x8ull; \ + offset; }) + +#define MCSX_CSE_TX_MEM_SLAVE_OUTPKTSFLOWIDTCAMMISSX(a) ({ \ + u64 offset; \ + \ + offset = 0x22c40ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0x10e98ull; \ + offset += (a) * 0x8ull; \ + offset; }) + +#define MCSX_CSE_TX_MEM_SLAVE_OUTPKTSPARSEERRX(a) ({ \ + u64 offset; \ + \ + offset = 0x22e40ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0x10e78ull; \ + offset += (a) * 0x8ull; \ + offset; }) + +#define MCSX_CSE_TX_MEM_SLAVE_OUTPKTSSCENCRYPTEDX(a) ({ \ + u64 offset; \ + \ + offset = 0x20440ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0x10c78ull; \ + offset += (a) * 0x8ull; \ + offset; }) + +#define MCSX_CSE_TX_MEM_SLAVE_OUTPKTSSCPROTECTEDX(a) ({ \ + u64 offset; \ + \ + offset = 0x1fc40ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0x10a78ull; \ + offset += (a) * 0x8ull; \ + offset; }) + +#define MCSX_CSE_TX_MEM_SLAVE_OUTPKTSSECTAGINSERTIONERRX(a) ({ \ + u64 offset; \ + \ + offset = 0x23040ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0x110d8ull; \ + offset += (a) * 0x8ull; \ + offset; }) + +#define MCSX_CSE_TX_MEM_SLAVE_OUTPKTSSECYNOACTIVESAX(a) ({ \ + u64 offset; \ + \ + offset = 0x1dc40ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0x10278ull; \ + offset += (a) * 0x8ull; \ + offset; }) + +#define MCSX_CSE_TX_MEM_SLAVE_OUTPKTSSECYTOOLONGX(a) ({ \ + u64 offset; \ + \ + offset = 0x1d440ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0x10478ull; \ + offset += (a) * 0x8ull; \ + offset; }) + +#define MCSX_CSE_TX_MEM_SLAVE_OUTPKTSSECYUNTAGGEDX(a) ({ \ + u64 offset; \ + \ + offset = 0x1cc40ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0x10078ull; \ + offset += (a) * 0x8ull; \ + offset; }) + +#define MCSX_CSE_TX_SLAVE_CTRL ({ \ + u64 offset; \ + \ + offset = 0x54a0ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0xa00ull; \ + offset; }) + +#define MCSX_CSE_TX_SLAVE_STATS_CLEAR ({ \ + u64 offset; \ + \ + offset = 0x54b8ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0xa18ull; \ + offset; }) + +#define MCSX_CSE_TX_MEM_SLAVE_OUTOCTETSSCENCRYPTEDX(a) (0x1f440ull + (a) * 0x8ull) +#define MCSX_CSE_TX_MEM_SLAVE_OUTOCTETSSCPROTECTEDX(a) (0x1ec40ull + (a) * 0x8ull) +#define MCSX_CSE_TX_MEM_SLAVE_OUTPKTSEARLYPREEMPTERRX(a) (0x10eb8ull + (a) * 0x8ull) +#define MCSX_CSE_TX_MEM_SLAVE_OUTPKTSSAENCRYPTEDX(a) (0x21c40ull + (a) * 0x8ull) +#define MCSX_CSE_TX_MEM_SLAVE_OUTPKTSSAPROTECTEDX(a) (0x20c40ull + (a) * 0x8ull) + +#define MCSX_IP_INT ({ \ + u64 offset; \ + \ + offset = 0x80028ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0x60028ull; \ + offset; }) + +#define MCSX_IP_INT_ENA_W1S ({ \ + u64 offset; \ + \ + offset = 0x80040ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0x60040ull; \ + offset; }) + +#define MCSX_IP_INT_ENA_W1C ({ \ + u64 offset; \ + \ + offset = 0x80038ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0x60038ull; \ + offset; }) + +#define MCSX_TOP_SLAVE_INT_SUM ({ \ + u64 offset; \ + \ + offset = 0xc20ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0xab8ull; \ + offset; }) + +#define MCSX_TOP_SLAVE_INT_SUM_ENB ({ \ + u64 offset; \ + \ + offset = 0xc28ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0xac0ull; \ + offset; }) + +#define MCSX_CPM_RX_SLAVE_RX_INT ({ \ + u64 offset; \ + \ + offset = 0x23c00ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0x0ad8ull; \ + offset; }) + +#define MCSX_CPM_RX_SLAVE_RX_INT_ENB ({ \ + u64 offset; \ + \ + offset = 0x23c08ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0xae0ull; \ + offset; }) + +#define MCSX_CPM_TX_SLAVE_TX_INT ({ \ + u64 offset; \ + \ + offset = 0x3d490ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0x54a0ull; \ + offset; }) + +#define MCSX_CPM_TX_SLAVE_TX_INT_ENB ({ \ + u64 offset; \ + \ + offset = 0x3d498ull; \ + if (mcs->hw->mcs_blks > 1) \ + offset = 0x54a8ull; \ + offset; }) + +#endif diff --git a/drivers/net/ethernet/marvell/octeontx2/af/mcs_rvu_if.c b/drivers/net/ethernet/marvell/octeontx2/af/mcs_rvu_if.c new file mode 100644 index 000000000000..fa8029a94068 --- /dev/null +++ b/drivers/net/ethernet/marvell/octeontx2/af/mcs_rvu_if.c @@ -0,0 +1,889 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Marvell CN10K MCS driver + * + * Copyright (C) 2022 Marvell. + */ + +#include <linux/types.h> +#include <linux/device.h> +#include <linux/module.h> +#include <linux/pci.h> + +#include "mcs.h" +#include "rvu.h" +#include "lmac_common.h" + +#define M(_name, _id, _fn_name, _req_type, _rsp_type) \ +static struct _req_type __maybe_unused \ +*otx2_mbox_alloc_msg_ ## _fn_name(struct rvu *rvu, int devid) \ +{ \ + struct _req_type *req; \ + \ + req = (struct _req_type *)otx2_mbox_alloc_msg_rsp( \ + &rvu->afpf_wq_info.mbox_up, devid, sizeof(struct _req_type), \ + sizeof(struct _rsp_type)); \ + if (!req) \ + return NULL; \ + req->hdr.sig = OTX2_MBOX_REQ_SIG; \ + req->hdr.id = _id; \ + return req; \ +} + +MBOX_UP_MCS_MESSAGES +#undef M + +int rvu_mbox_handler_mcs_set_lmac_mode(struct rvu *rvu, + struct mcs_set_lmac_mode *req, + struct msg_rsp *rsp) +{ + struct mcs *mcs; + + if (req->mcs_id >= rvu->mcs_blk_cnt) + return MCS_AF_ERR_INVALID_MCSID; + + mcs = mcs_get_pdata(req->mcs_id); + + if (BIT_ULL(req->lmac_id) & mcs->hw->lmac_bmap) + mcs_set_lmac_mode(mcs, req->lmac_id, req->mode); + + return 0; +} + +int mcs_add_intr_wq_entry(struct mcs *mcs, struct mcs_intr_event *event) +{ + struct mcs_intrq_entry *qentry; + u16 pcifunc = event->pcifunc; + struct rvu *rvu = mcs->rvu; + struct mcs_pfvf *pfvf; + + /* Check if it is PF or VF */ + if (pcifunc & RVU_PFVF_FUNC_MASK) + pfvf = &mcs->vf[rvu_get_hwvf(rvu, pcifunc)]; + else + pfvf = &mcs->pf[rvu_get_pf(pcifunc)]; + + event->intr_mask &= pfvf->intr_mask; + + /* Check PF/VF interrupt notification is enabled */ + if (!(pfvf->intr_mask && event->intr_mask)) + return 0; + + qentry = kmalloc(sizeof(*qentry), GFP_ATOMIC); + if (!qentry) + return -ENOMEM; + + qentry->intr_event = *event; + spin_lock(&rvu->mcs_intrq_lock); + list_add_tail(&qentry->node, &rvu->mcs_intrq_head); + spin_unlock(&rvu->mcs_intrq_lock); + queue_work(rvu->mcs_intr_wq, &rvu->mcs_intr_work); + + return 0; +} + +static int mcs_notify_pfvf(struct mcs_intr_event *event, struct rvu *rvu) +{ + struct mcs_intr_info *req; + int err, pf; + + pf = rvu_get_pf(event->pcifunc); + + req = otx2_mbox_alloc_msg_mcs_intr_notify(rvu, pf); + if (!req) + return -ENOMEM; + + req->mcs_id = event->mcs_id; + req->intr_mask = event->intr_mask; + req->sa_id = event->sa_id; + req->hdr.pcifunc = event->pcifunc; + req->lmac_id = event->lmac_id; + + otx2_mbox_msg_send(&rvu->afpf_wq_info.mbox_up, pf); + err = otx2_mbox_wait_for_rsp(&rvu->afpf_wq_info.mbox_up, pf); + if (err) + dev_warn(rvu->dev, "MCS notification to pf %d failed\n", pf); + + return 0; +} + +static void mcs_intr_handler_task(struct work_struct *work) +{ + struct rvu *rvu = container_of(work, struct rvu, mcs_intr_work); + struct mcs_intrq_entry *qentry; + struct mcs_intr_event *event; + unsigned long flags; + + do { + spin_lock_irqsave(&rvu->mcs_intrq_lock, flags); + qentry = list_first_entry_or_null(&rvu->mcs_intrq_head, + struct mcs_intrq_entry, + node); + if (qentry) + list_del(&qentry->node); + + spin_unlock_irqrestore(&rvu->mcs_intrq_lock, flags); + if (!qentry) + break; /* nothing more to process */ + + event = &qentry->intr_event; + + mcs_notify_pfvf(event, rvu); + kfree(qentry); + } while (1); +} + +int rvu_mbox_handler_mcs_intr_cfg(struct rvu *rvu, + struct mcs_intr_cfg *req, + struct msg_rsp *rsp) +{ + u16 pcifunc = req->hdr.pcifunc; + struct mcs_pfvf *pfvf; + struct mcs *mcs; + + if (req->mcs_id >= rvu->mcs_blk_cnt) + return MCS_AF_ERR_INVALID_MCSID; + + mcs = mcs_get_pdata(req->mcs_id); + + /* Check if it is PF or VF */ + if (pcifunc & RVU_PFVF_FUNC_MASK) + pfvf = &mcs->vf[rvu_get_hwvf(rvu, pcifunc)]; + else + pfvf = &mcs->pf[rvu_get_pf(pcifunc)]; + + mcs->pf_map[0] = pcifunc; + pfvf->intr_mask = req->intr_mask; + + return 0; +} + +int rvu_mbox_handler_mcs_get_hw_info(struct rvu *rvu, + struct msg_req *req, + struct mcs_hw_info *rsp) +{ + struct mcs *mcs; + + if (!rvu->mcs_blk_cnt) + return MCS_AF_ERR_NOT_MAPPED; + + /* MCS resources are same across all blocks */ + mcs = mcs_get_pdata(0); + rsp->num_mcs_blks = rvu->mcs_blk_cnt; + rsp->tcam_entries = mcs->hw->tcam_entries; + rsp->secy_entries = mcs->hw->secy_entries; + rsp->sc_entries = mcs->hw->sc_entries; + rsp->sa_entries = mcs->hw->sa_entries; + return 0; +} + +int rvu_mbox_handler_mcs_port_reset(struct rvu *rvu, struct mcs_port_reset_req *req, + struct msg_rsp *rsp) +{ + struct mcs *mcs; + + if (req->mcs_id >= rvu->mcs_blk_cnt) + return MCS_AF_ERR_INVALID_MCSID; + + mcs = mcs_get_pdata(req->mcs_id); + + mcs_reset_port(mcs, req->port_id, req->reset); + + return 0; +} + +int rvu_mbox_handler_mcs_clear_stats(struct rvu *rvu, + struct mcs_clear_stats *req, + struct msg_rsp *rsp) +{ + u16 pcifunc = req->hdr.pcifunc; + struct mcs *mcs; + + if (req->mcs_id >= rvu->mcs_blk_cnt) + return MCS_AF_ERR_INVALID_MCSID; + + mcs = mcs_get_pdata(req->mcs_id); + + mutex_lock(&mcs->stats_lock); + if (req->all) + mcs_clear_all_stats(mcs, pcifunc, req->dir); + else + mcs_clear_stats(mcs, req->type, req->id, req->dir); + + mutex_unlock(&mcs->stats_lock); + return 0; +} + +int rvu_mbox_handler_mcs_get_flowid_stats(struct rvu *rvu, + struct mcs_stats_req *req, + struct mcs_flowid_stats *rsp) +{ + struct mcs *mcs; + + if (req->mcs_id >= rvu->mcs_blk_cnt) + return MCS_AF_ERR_INVALID_MCSID; + + mcs = mcs_get_pdata(req->mcs_id); + + /* In CNF10K-B, before reading the statistics, + * MCSX_MIL_GLOBAL.FORCE_CLK_EN_IP needs to be set + * to get accurate statistics + */ + if (mcs->hw->mcs_blks > 1) + mcs_set_force_clk_en(mcs, true); + + mutex_lock(&mcs->stats_lock); + mcs_get_flowid_stats(mcs, rsp, req->id, req->dir); + mutex_unlock(&mcs->stats_lock); + + /* Clear MCSX_MIL_GLOBAL.FORCE_CLK_EN_IP after reading + * the statistics + */ + if (mcs->hw->mcs_blks > 1) + mcs_set_force_clk_en(mcs, false); + + return 0; +} + +int rvu_mbox_handler_mcs_get_secy_stats(struct rvu *rvu, + struct mcs_stats_req *req, + struct mcs_secy_stats *rsp) +{ struct mcs *mcs; + + if (req->mcs_id >= rvu->mcs_blk_cnt) + return MCS_AF_ERR_INVALID_MCSID; + + mcs = mcs_get_pdata(req->mcs_id); + + if (mcs->hw->mcs_blks > 1) + mcs_set_force_clk_en(mcs, true); + + mutex_lock(&mcs->stats_lock); + + if (req->dir == MCS_RX) + mcs_get_rx_secy_stats(mcs, rsp, req->id); + else + mcs_get_tx_secy_stats(mcs, rsp, req->id); + + mutex_unlock(&mcs->stats_lock); + + if (mcs->hw->mcs_blks > 1) + mcs_set_force_clk_en(mcs, false); + + return 0; +} + +int rvu_mbox_handler_mcs_get_sc_stats(struct rvu *rvu, + struct mcs_stats_req *req, + struct mcs_sc_stats *rsp) +{ + struct mcs *mcs; + + if (req->mcs_id >= rvu->mcs_blk_cnt) + return MCS_AF_ERR_INVALID_MCSID; + + mcs = mcs_get_pdata(req->mcs_id); + + if (mcs->hw->mcs_blks > 1) + mcs_set_force_clk_en(mcs, true); + + mutex_lock(&mcs->stats_lock); + mcs_get_sc_stats(mcs, rsp, req->id, req->dir); + mutex_unlock(&mcs->stats_lock); + + if (mcs->hw->mcs_blks > 1) + mcs_set_force_clk_en(mcs, false); + + return 0; +} + +int rvu_mbox_handler_mcs_get_sa_stats(struct rvu *rvu, + struct mcs_stats_req *req, + struct mcs_sa_stats *rsp) +{ + struct mcs *mcs; + + if (req->mcs_id >= rvu->mcs_blk_cnt) + return MCS_AF_ERR_INVALID_MCSID; + + mcs = mcs_get_pdata(req->mcs_id); + + if (mcs->hw->mcs_blks > 1) + mcs_set_force_clk_en(mcs, true); + + mutex_lock(&mcs->stats_lock); + mcs_get_sa_stats(mcs, rsp, req->id, req->dir); + mutex_unlock(&mcs->stats_lock); + + if (mcs->hw->mcs_blks > 1) + mcs_set_force_clk_en(mcs, false); + + return 0; +} + +int rvu_mbox_handler_mcs_get_port_stats(struct rvu *rvu, + struct mcs_stats_req *req, + struct mcs_port_stats *rsp) +{ + struct mcs *mcs; + + if (req->mcs_id >= rvu->mcs_blk_cnt) + return MCS_AF_ERR_INVALID_MCSID; + + mcs = mcs_get_pdata(req->mcs_id); + + if (mcs->hw->mcs_blks > 1) + mcs_set_force_clk_en(mcs, true); + + mutex_lock(&mcs->stats_lock); + mcs_get_port_stats(mcs, rsp, req->id, req->dir); + mutex_unlock(&mcs->stats_lock); + + if (mcs->hw->mcs_blks > 1) + mcs_set_force_clk_en(mcs, false); + + return 0; +} + +int rvu_mbox_handler_mcs_set_active_lmac(struct rvu *rvu, + struct mcs_set_active_lmac *req, + struct msg_rsp *rsp) +{ + struct mcs *mcs; + + if (req->mcs_id >= rvu->mcs_blk_cnt) + return MCS_AF_ERR_INVALID_MCSID; + + mcs = mcs_get_pdata(req->mcs_id); + if (!mcs) + return MCS_AF_ERR_NOT_MAPPED; + + mcs->hw->lmac_bmap = req->lmac_bmap; + mcs_set_lmac_channels(req->mcs_id, req->chan_base); + return 0; +} + +int rvu_mbox_handler_mcs_port_cfg_set(struct rvu *rvu, struct mcs_port_cfg_set_req *req, + struct msg_rsp *rsp) +{ + struct mcs *mcs; + + if (req->mcs_id >= rvu->mcs_blk_cnt) + return MCS_AF_ERR_INVALID_MCSID; + + mcs = mcs_get_pdata(req->mcs_id); + + if (mcs->hw->lmac_cnt <= req->port_id || !(mcs->hw->lmac_bmap & BIT_ULL(req->port_id))) + return -EINVAL; + + mcs_set_port_cfg(mcs, req); + + return 0; +} + +int rvu_mbox_handler_mcs_port_cfg_get(struct rvu *rvu, struct mcs_port_cfg_get_req *req, + struct mcs_port_cfg_get_rsp *rsp) +{ + struct mcs *mcs; + + if (req->mcs_id >= rvu->mcs_blk_cnt) + return MCS_AF_ERR_INVALID_MCSID; + + mcs = mcs_get_pdata(req->mcs_id); + + if (mcs->hw->lmac_cnt <= req->port_id || !(mcs->hw->lmac_bmap & BIT_ULL(req->port_id))) + return -EINVAL; + + mcs_get_port_cfg(mcs, req, rsp); + + return 0; +} + +int rvu_mbox_handler_mcs_custom_tag_cfg_get(struct rvu *rvu, struct mcs_custom_tag_cfg_get_req *req, + struct mcs_custom_tag_cfg_get_rsp *rsp) +{ + struct mcs *mcs; + + if (req->mcs_id >= rvu->mcs_blk_cnt) + return MCS_AF_ERR_INVALID_MCSID; + + mcs = mcs_get_pdata(req->mcs_id); + + mcs_get_custom_tag_cfg(mcs, req, rsp); + + return 0; +} + +int rvu_mcs_flr_handler(struct rvu *rvu, u16 pcifunc) +{ + struct mcs *mcs; + int mcs_id; + + /* CNF10K-B mcs0-6 are mapped to RPM2-8*/ + if (rvu->mcs_blk_cnt > 1) { + for (mcs_id = 0; mcs_id < rvu->mcs_blk_cnt; mcs_id++) { + mcs = mcs_get_pdata(mcs_id); + mcs_free_all_rsrc(mcs, MCS_RX, pcifunc); + mcs_free_all_rsrc(mcs, MCS_TX, pcifunc); + } + } else { + /* CN10K-B has only one mcs block */ + mcs = mcs_get_pdata(0); + mcs_free_all_rsrc(mcs, MCS_RX, pcifunc); + mcs_free_all_rsrc(mcs, MCS_TX, pcifunc); + } + return 0; +} + +int rvu_mbox_handler_mcs_flowid_ena_entry(struct rvu *rvu, + struct mcs_flowid_ena_dis_entry *req, + struct msg_rsp *rsp) +{ + struct mcs *mcs; + + if (req->mcs_id >= rvu->mcs_blk_cnt) + return MCS_AF_ERR_INVALID_MCSID; + + mcs = mcs_get_pdata(req->mcs_id); + mcs_ena_dis_flowid_entry(mcs, req->flow_id, req->dir, req->ena); + return 0; +} + +int rvu_mbox_handler_mcs_pn_table_write(struct rvu *rvu, + struct mcs_pn_table_write_req *req, + struct msg_rsp *rsp) +{ + struct mcs *mcs; + + if (req->mcs_id >= rvu->mcs_blk_cnt) + return MCS_AF_ERR_INVALID_MCSID; + + mcs = mcs_get_pdata(req->mcs_id); + mcs_pn_table_write(mcs, req->pn_id, req->next_pn, req->dir); + return 0; +} + +int rvu_mbox_handler_mcs_set_pn_threshold(struct rvu *rvu, + struct mcs_set_pn_threshold *req, + struct msg_rsp *rsp) +{ + struct mcs *mcs; + + if (req->mcs_id >= rvu->mcs_blk_cnt) + return MCS_AF_ERR_INVALID_MCSID; + + mcs = mcs_get_pdata(req->mcs_id); + + mcs_pn_threshold_set(mcs, req); + + return 0; +} + +int rvu_mbox_handler_mcs_rx_sc_sa_map_write(struct rvu *rvu, + struct mcs_rx_sc_sa_map *req, + struct msg_rsp *rsp) +{ + struct mcs *mcs; + + if (req->mcs_id >= rvu->mcs_blk_cnt) + return MCS_AF_ERR_INVALID_MCSID; + + mcs = mcs_get_pdata(req->mcs_id); + mcs->mcs_ops->mcs_rx_sa_mem_map_write(mcs, req); + return 0; +} + +int rvu_mbox_handler_mcs_tx_sc_sa_map_write(struct rvu *rvu, + struct mcs_tx_sc_sa_map *req, + struct msg_rsp *rsp) +{ + struct mcs *mcs; + + if (req->mcs_id >= rvu->mcs_blk_cnt) + return MCS_AF_ERR_INVALID_MCSID; + + mcs = mcs_get_pdata(req->mcs_id); + mcs->mcs_ops->mcs_tx_sa_mem_map_write(mcs, req); + mcs->tx_sa_active[req->sc_id] = req->tx_sa_active; + + return 0; +} + +int rvu_mbox_handler_mcs_sa_plcy_write(struct rvu *rvu, + struct mcs_sa_plcy_write_req *req, + struct msg_rsp *rsp) +{ + struct mcs *mcs; + int i; + + if (req->mcs_id >= rvu->mcs_blk_cnt) + return MCS_AF_ERR_INVALID_MCSID; + + mcs = mcs_get_pdata(req->mcs_id); + + for (i = 0; i < req->sa_cnt; i++) + mcs_sa_plcy_write(mcs, &req->plcy[i][0], + req->sa_index[i], req->dir); + return 0; +} + +int rvu_mbox_handler_mcs_rx_sc_cam_write(struct rvu *rvu, + struct mcs_rx_sc_cam_write_req *req, + struct msg_rsp *rsp) +{ + struct mcs *mcs; + + if (req->mcs_id >= rvu->mcs_blk_cnt) + return MCS_AF_ERR_INVALID_MCSID; + + mcs = mcs_get_pdata(req->mcs_id); + mcs_rx_sc_cam_write(mcs, req->sci, req->secy_id, req->sc_id); + return 0; +} + +int rvu_mbox_handler_mcs_secy_plcy_write(struct rvu *rvu, + struct mcs_secy_plcy_write_req *req, + struct msg_rsp *rsp) +{ struct mcs *mcs; + + if (req->mcs_id >= rvu->mcs_blk_cnt) + return MCS_AF_ERR_INVALID_MCSID; + + mcs = mcs_get_pdata(req->mcs_id); + + mcs_secy_plcy_write(mcs, req->plcy, + req->secy_id, req->dir); + return 0; +} + +int rvu_mbox_handler_mcs_flowid_entry_write(struct rvu *rvu, + struct mcs_flowid_entry_write_req *req, + struct msg_rsp *rsp) +{ + struct secy_mem_map map; + struct mcs *mcs; + + if (req->mcs_id >= rvu->mcs_blk_cnt) + return MCS_AF_ERR_INVALID_MCSID; + + mcs = mcs_get_pdata(req->mcs_id); + + /* TODO validate the flowid */ + mcs_flowid_entry_write(mcs, req->data, req->mask, + req->flow_id, req->dir); + map.secy = req->secy_id; + map.sc = req->sc_id; + map.ctrl_pkt = req->ctrl_pkt; + map.flow_id = req->flow_id; + map.sci = req->sci; + mcs->mcs_ops->mcs_flowid_secy_map(mcs, &map, req->dir); + if (req->ena) + mcs_ena_dis_flowid_entry(mcs, req->flow_id, + req->dir, true); + return 0; +} + +int rvu_mbox_handler_mcs_free_resources(struct rvu *rvu, + struct mcs_free_rsrc_req *req, + struct msg_rsp *rsp) +{ + u16 pcifunc = req->hdr.pcifunc; + struct mcs_rsrc_map *map; + struct mcs *mcs; + int rc; + + if (req->mcs_id >= rvu->mcs_blk_cnt) + return MCS_AF_ERR_INVALID_MCSID; + + mcs = mcs_get_pdata(req->mcs_id); + + if (req->dir == MCS_RX) + map = &mcs->rx; + else + map = &mcs->tx; + + mutex_lock(&rvu->rsrc_lock); + /* Free all the cam resources mapped to PF/VF */ + if (req->all) { + rc = mcs_free_all_rsrc(mcs, req->dir, pcifunc); + goto exit; + } + + switch (req->rsrc_type) { + case MCS_RSRC_TYPE_FLOWID: + rc = mcs_free_rsrc(&map->flow_ids, map->flowid2pf_map, req->rsrc_id, pcifunc); + mcs_ena_dis_flowid_entry(mcs, req->rsrc_id, req->dir, false); + break; + case MCS_RSRC_TYPE_SECY: + rc = mcs_free_rsrc(&map->secy, map->secy2pf_map, req->rsrc_id, pcifunc); + mcs_clear_secy_plcy(mcs, req->rsrc_id, req->dir); + break; + case MCS_RSRC_TYPE_SC: + rc = mcs_free_rsrc(&map->sc, map->sc2pf_map, req->rsrc_id, pcifunc); + /* Disable SC CAM only on RX side */ + if (req->dir == MCS_RX) + mcs_ena_dis_sc_cam_entry(mcs, req->rsrc_id, false); + break; + case MCS_RSRC_TYPE_SA: + rc = mcs_free_rsrc(&map->sa, map->sa2pf_map, req->rsrc_id, pcifunc); + break; + } +exit: + mutex_unlock(&rvu->rsrc_lock); + return rc; +} + +int rvu_mbox_handler_mcs_alloc_resources(struct rvu *rvu, + struct mcs_alloc_rsrc_req *req, + struct mcs_alloc_rsrc_rsp *rsp) +{ + u16 pcifunc = req->hdr.pcifunc; + struct mcs_rsrc_map *map; + struct mcs *mcs; + int rsrc_id, i; + + if (req->mcs_id >= rvu->mcs_blk_cnt) + return MCS_AF_ERR_INVALID_MCSID; + + mcs = mcs_get_pdata(req->mcs_id); + + if (req->dir == MCS_RX) + map = &mcs->rx; + else + map = &mcs->tx; + + mutex_lock(&rvu->rsrc_lock); + + if (req->all) { + rsrc_id = mcs_alloc_all_rsrc(mcs, &rsp->flow_ids[0], + &rsp->secy_ids[0], + &rsp->sc_ids[0], + &rsp->sa_ids[0], + &rsp->sa_ids[1], + pcifunc, req->dir); + goto exit; + } + + switch (req->rsrc_type) { + case MCS_RSRC_TYPE_FLOWID: + for (i = 0; i < req->rsrc_cnt; i++) { + rsrc_id = mcs_alloc_rsrc(&map->flow_ids, map->flowid2pf_map, pcifunc); + if (rsrc_id < 0) + goto exit; + rsp->flow_ids[i] = rsrc_id; + rsp->rsrc_cnt++; + } + break; + case MCS_RSRC_TYPE_SECY: + for (i = 0; i < req->rsrc_cnt; i++) { + rsrc_id = mcs_alloc_rsrc(&map->secy, map->secy2pf_map, pcifunc); + if (rsrc_id < 0) + goto exit; + rsp->secy_ids[i] = rsrc_id; + rsp->rsrc_cnt++; + } + break; + case MCS_RSRC_TYPE_SC: + for (i = 0; i < req->rsrc_cnt; i++) { + rsrc_id = mcs_alloc_rsrc(&map->sc, map->sc2pf_map, pcifunc); + if (rsrc_id < 0) + goto exit; + rsp->sc_ids[i] = rsrc_id; + rsp->rsrc_cnt++; + } + break; + case MCS_RSRC_TYPE_SA: + for (i = 0; i < req->rsrc_cnt; i++) { + rsrc_id = mcs_alloc_rsrc(&map->sa, map->sa2pf_map, pcifunc); + if (rsrc_id < 0) + goto exit; + rsp->sa_ids[i] = rsrc_id; + rsp->rsrc_cnt++; + } + break; + } + + rsp->rsrc_type = req->rsrc_type; + rsp->dir = req->dir; + rsp->mcs_id = req->mcs_id; + rsp->all = req->all; + +exit: + if (rsrc_id < 0) + dev_err(rvu->dev, "Failed to allocate the mcs resources for PCIFUNC:%d\n", pcifunc); + mutex_unlock(&rvu->rsrc_lock); + return 0; +} + +int rvu_mbox_handler_mcs_alloc_ctrl_pkt_rule(struct rvu *rvu, + struct mcs_alloc_ctrl_pkt_rule_req *req, + struct mcs_alloc_ctrl_pkt_rule_rsp *rsp) +{ + u16 pcifunc = req->hdr.pcifunc; + struct mcs_rsrc_map *map; + struct mcs *mcs; + int rsrc_id; + u16 offset; + + if (req->mcs_id >= rvu->mcs_blk_cnt) + return MCS_AF_ERR_INVALID_MCSID; + + mcs = mcs_get_pdata(req->mcs_id); + + map = (req->dir == MCS_RX) ? &mcs->rx : &mcs->tx; + + mutex_lock(&rvu->rsrc_lock); + + switch (req->rule_type) { + case MCS_CTRL_PKT_RULE_TYPE_ETH: + offset = MCS_CTRLPKT_ETYPE_RULE_OFFSET; + break; + case MCS_CTRL_PKT_RULE_TYPE_DA: + offset = MCS_CTRLPKT_DA_RULE_OFFSET; + break; + case MCS_CTRL_PKT_RULE_TYPE_RANGE: + offset = MCS_CTRLPKT_DA_RANGE_RULE_OFFSET; + break; + case MCS_CTRL_PKT_RULE_TYPE_COMBO: + offset = MCS_CTRLPKT_COMBO_RULE_OFFSET; + break; + case MCS_CTRL_PKT_RULE_TYPE_MAC: + offset = MCS_CTRLPKT_MAC_EN_RULE_OFFSET; + break; + } + + rsrc_id = mcs_alloc_ctrlpktrule(&map->ctrlpktrule, map->ctrlpktrule2pf_map, offset, + pcifunc); + if (rsrc_id < 0) + goto exit; + + rsp->rule_idx = rsrc_id; + rsp->rule_type = req->rule_type; + rsp->dir = req->dir; + rsp->mcs_id = req->mcs_id; + + mutex_unlock(&rvu->rsrc_lock); + return 0; +exit: + if (rsrc_id < 0) + dev_err(rvu->dev, "Failed to allocate the mcs ctrl pkt rule for PCIFUNC:%d\n", + pcifunc); + mutex_unlock(&rvu->rsrc_lock); + return rsrc_id; +} + +int rvu_mbox_handler_mcs_free_ctrl_pkt_rule(struct rvu *rvu, + struct mcs_free_ctrl_pkt_rule_req *req, + struct msg_rsp *rsp) +{ + struct mcs *mcs; + int rc; + + if (req->mcs_id >= rvu->mcs_blk_cnt) + return MCS_AF_ERR_INVALID_MCSID; + + mcs = mcs_get_pdata(req->mcs_id); + + mutex_lock(&rvu->rsrc_lock); + + rc = mcs_free_ctrlpktrule(mcs, req); + + mutex_unlock(&rvu->rsrc_lock); + + return rc; +} + +int rvu_mbox_handler_mcs_ctrl_pkt_rule_write(struct rvu *rvu, + struct mcs_ctrl_pkt_rule_write_req *req, + struct msg_rsp *rsp) +{ + struct mcs *mcs; + int rc; + + if (req->mcs_id >= rvu->mcs_blk_cnt) + return MCS_AF_ERR_INVALID_MCSID; + + mcs = mcs_get_pdata(req->mcs_id); + + rc = mcs_ctrlpktrule_write(mcs, req); + + return rc; +} + +static void rvu_mcs_set_lmac_bmap(struct rvu *rvu) +{ + struct mcs *mcs = mcs_get_pdata(0); + unsigned long lmac_bmap; + int cgx, lmac, port; + + for (port = 0; port < mcs->hw->lmac_cnt; port++) { + cgx = port / rvu->hw->lmac_per_cgx; + lmac = port % rvu->hw->lmac_per_cgx; + if (!is_lmac_valid(rvu_cgx_pdata(cgx, rvu), lmac)) + continue; + set_bit(port, &lmac_bmap); + } + mcs->hw->lmac_bmap = lmac_bmap; +} + +int rvu_mcs_init(struct rvu *rvu) +{ + struct rvu_hwinfo *hw = rvu->hw; + int lmac, err = 0, mcs_id; + struct mcs *mcs; + + rvu->mcs_blk_cnt = mcs_get_blkcnt(); + + if (!rvu->mcs_blk_cnt) + return 0; + + /* Needed only for CN10K-B */ + if (rvu->mcs_blk_cnt == 1) { + err = mcs_set_lmac_channels(0, hw->cgx_chan_base); + if (err) + return err; + /* Set active lmacs */ + rvu_mcs_set_lmac_bmap(rvu); + } + + /* Install default tcam bypass entry and set port to operational mode */ + for (mcs_id = 0; mcs_id < rvu->mcs_blk_cnt; mcs_id++) { + mcs = mcs_get_pdata(mcs_id); + mcs_install_flowid_bypass_entry(mcs); + for (lmac = 0; lmac < mcs->hw->lmac_cnt; lmac++) + mcs_set_lmac_mode(mcs, lmac, 0); + + mcs->rvu = rvu; + + /* Allocated memory for PFVF data */ + mcs->pf = devm_kcalloc(mcs->dev, hw->total_pfs, + sizeof(struct mcs_pfvf), GFP_KERNEL); + if (!mcs->pf) + return -ENOMEM; + + mcs->vf = devm_kcalloc(mcs->dev, hw->total_vfs, + sizeof(struct mcs_pfvf), GFP_KERNEL); + if (!mcs->vf) + return -ENOMEM; + } + + /* Initialize the wq for handling mcs interrupts */ + INIT_LIST_HEAD(&rvu->mcs_intrq_head); + INIT_WORK(&rvu->mcs_intr_work, mcs_intr_handler_task); + rvu->mcs_intr_wq = alloc_workqueue("mcs_intr_wq", 0, 0); + if (!rvu->mcs_intr_wq) { + dev_err(rvu->dev, "mcs alloc workqueue failed\n"); + return -ENOMEM; + } + + return err; +} + +void rvu_mcs_exit(struct rvu *rvu) +{ + if (!rvu->mcs_intr_wq) + return; + + flush_workqueue(rvu->mcs_intr_wq); + destroy_workqueue(rvu->mcs_intr_wq); + rvu->mcs_intr_wq = NULL; +} diff --git a/drivers/net/ethernet/marvell/octeontx2/af/ptp.c b/drivers/net/ethernet/marvell/octeontx2/af/ptp.c index 67a6821d2dff..3411e2e47d46 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/ptp.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/ptp.c @@ -9,6 +9,8 @@ #include <linux/device.h> #include <linux/module.h> #include <linux/pci.h> +#include <linux/hrtimer.h> +#include <linux/ktime.h> #include "ptp.h" #include "mbox.h" @@ -50,12 +52,23 @@ #define PTP_CLOCK_COMP 0xF18ULL #define PTP_TIMESTAMP 0xF20ULL #define PTP_CLOCK_SEC 0xFD0ULL +#define PTP_SEC_ROLLOVER 0xFD8ULL #define CYCLE_MULT 1000 static struct ptp *first_ptp_block; static const struct pci_device_id ptp_id_table[]; +static bool is_ptp_dev_cnf10kb(struct ptp *ptp) +{ + return (ptp->pdev->subsystem_device == PCI_SUBSYS_DEVID_CNF10K_B_PTP) ? true : false; +} + +static bool is_ptp_dev_cn10k(struct ptp *ptp) +{ + return (ptp->pdev->device == PCI_DEVID_CN10K_PTP) ? true : false; +} + static bool cn10k_ptp_errata(struct ptp *ptp) { if (ptp->pdev->subsystem_device == PCI_SUBSYS_DEVID_CN10K_A_PTP || @@ -72,6 +85,43 @@ static bool is_ptp_tsfmt_sec_nsec(struct ptp *ptp) return false; } +static enum hrtimer_restart ptp_reset_thresh(struct hrtimer *hrtimer) +{ + struct ptp *ptp = container_of(hrtimer, struct ptp, hrtimer); + ktime_t curr_ts = ktime_get(); + ktime_t delta_ns, period_ns; + u64 ptp_clock_hi; + + /* calculate the elapsed time since last restart */ + delta_ns = ktime_to_ns(ktime_sub(curr_ts, ptp->last_ts)); + + /* if the ptp clock value has crossed 0.5 seconds, + * its too late to update pps threshold value, so + * update threshold after 1 second. + */ + ptp_clock_hi = readq(ptp->reg_base + PTP_CLOCK_HI); + if (ptp_clock_hi > 500000000) { + period_ns = ktime_set(0, (NSEC_PER_SEC + 100 - ptp_clock_hi)); + } else { + writeq(500000000, ptp->reg_base + PTP_PPS_THRESH_HI); + period_ns = ktime_set(0, (NSEC_PER_SEC + 100 - delta_ns)); + } + + hrtimer_forward_now(hrtimer, period_ns); + ptp->last_ts = curr_ts; + + return HRTIMER_RESTART; +} + +static void ptp_hrtimer_start(struct ptp *ptp, ktime_t start_ns) +{ + ktime_t period_ns; + + period_ns = ktime_set(0, (NSEC_PER_SEC + 100 - start_ns)); + hrtimer_start(&ptp->hrtimer, period_ns, HRTIMER_MODE_REL); + ptp->last_ts = ktime_get(); +} + static u64 read_ptp_tstmp_sec_nsec(struct ptp *ptp) { u64 sec, sec1, nsec; @@ -246,6 +296,10 @@ void ptp_start(struct ptp *ptp, u64 sclk, u32 ext_clk_freq, u32 extts) /* sclk is in MHz */ ptp->clock_rate = sclk * 1000000; + /* Program the seconds rollover value to 1 second */ + if (is_ptp_dev_cnf10kb(ptp)) + writeq(0x3b9aca00, ptp->reg_base + PTP_SEC_ROLLOVER); + /* Enable PTP clock */ clock_cfg = readq(ptp->reg_base + PTP_CLOCK_CFG); @@ -270,6 +324,18 @@ void ptp_start(struct ptp *ptp, u64 sclk, u32 ext_clk_freq, u32 extts) /* Set 50% duty cycle for 1Hz output */ writeq(0x1dcd650000000000, ptp->reg_base + PTP_PPS_HI_INCR); writeq(0x1dcd650000000000, ptp->reg_base + PTP_PPS_LO_INCR); + if (cn10k_ptp_errata(ptp)) { + /* The ptp_clock_hi rollsover to zero once clock cycle before it + * reaches one second boundary. so, program the pps_lo_incr in + * such a way that the pps threshold value comparison at one + * second boundary will succeed and pps edge changes. After each + * one second boundary, the hrtimer handler will be invoked and + * reprograms the pps threshold value. + */ + ptp->clock_period = NSEC_PER_SEC / ptp->clock_rate; + writeq((0x1dcd6500ULL - ptp->clock_period) << 32, + ptp->reg_base + PTP_PPS_LO_INCR); + } if (cn10k_ptp_errata(ptp)) clock_comp = ptp_calc_adjusted_comp(ptp->clock_rate); @@ -282,14 +348,39 @@ void ptp_start(struct ptp *ptp, u64 sclk, u32 ext_clk_freq, u32 extts) static int ptp_get_tstmp(struct ptp *ptp, u64 *clk) { - *clk = readq(ptp->reg_base + PTP_TIMESTAMP); + u64 timestamp; + + if (is_ptp_dev_cn10k(ptp)) { + timestamp = readq(ptp->reg_base + PTP_TIMESTAMP); + *clk = (timestamp >> 32) * NSEC_PER_SEC + (timestamp & 0xFFFFFFFF); + } else { + *clk = readq(ptp->reg_base + PTP_TIMESTAMP); + } return 0; } static int ptp_set_thresh(struct ptp *ptp, u64 thresh) { - writeq(thresh, ptp->reg_base + PTP_PPS_THRESH_HI); + if (!cn10k_ptp_errata(ptp)) + writeq(thresh, ptp->reg_base + PTP_PPS_THRESH_HI); + + return 0; +} + +static int ptp_extts_on(struct ptp *ptp, int on) +{ + u64 ptp_clock_hi; + + if (cn10k_ptp_errata(ptp)) { + if (on) { + ptp_clock_hi = readq(ptp->reg_base + PTP_CLOCK_HI); + ptp_hrtimer_start(ptp, (ktime_t)ptp_clock_hi); + } else { + if (hrtimer_active(&ptp->hrtimer)) + hrtimer_cancel(&ptp->hrtimer); + } + } return 0; } @@ -329,6 +420,11 @@ static int ptp_probe(struct pci_dev *pdev, else ptp->read_ptp_tstmp = &read_ptp_tstmp_nsec; + if (cn10k_ptp_errata(ptp)) { + hrtimer_init(&ptp->hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + ptp->hrtimer.function = ptp_reset_thresh; + } + return 0; error_free: @@ -353,6 +449,9 @@ static void ptp_remove(struct pci_dev *pdev) struct ptp *ptp = pci_get_drvdata(pdev); u64 clock_cfg; + if (cn10k_ptp_errata(ptp) && hrtimer_active(&ptp->hrtimer)) + hrtimer_cancel(&ptp->hrtimer); + if (IS_ERR_OR_NULL(ptp)) return; @@ -420,6 +519,9 @@ int rvu_mbox_handler_ptp_op(struct rvu *rvu, struct ptp_req *req, case PTP_OP_SET_THRESH: err = ptp_set_thresh(rvu->ptp, req->thresh); break; + case PTP_OP_EXTTS_ON: + err = ptp_extts_on(rvu->ptp, req->extts_on); + break; default: err = -EINVAL; break; diff --git a/drivers/net/ethernet/marvell/octeontx2/af/ptp.h b/drivers/net/ethernet/marvell/octeontx2/af/ptp.h index 95a955159f40..b9d92abc3844 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/ptp.h +++ b/drivers/net/ethernet/marvell/octeontx2/af/ptp.h @@ -17,7 +17,10 @@ struct ptp { void __iomem *reg_base; u64 (*read_ptp_tstmp)(struct ptp *ptp); spinlock_t ptp_lock; /* lock */ + struct hrtimer hrtimer; + ktime_t last_ts; u32 clock_rate; + u32 clock_period; }; struct ptp *ptp_get(void); diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rpm.c b/drivers/net/ethernet/marvell/octeontx2/af/rpm.c index ef59de43b11e..a70e1153fa04 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rpm.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rpm.c @@ -415,11 +415,26 @@ void rpm_lmac_ptp_config(void *rpmd, int lmac_id, bool enable) return; cfg = rpm_read(rpm, lmac_id, RPMX_CMRX_CFG); - if (enable) + if (enable) { cfg |= RPMX_RX_TS_PREPEND; - else + cfg |= RPMX_TX_PTP_1S_SUPPORT; + } else { cfg &= ~RPMX_RX_TS_PREPEND; + cfg &= ~RPMX_TX_PTP_1S_SUPPORT; + } + rpm_write(rpm, lmac_id, RPMX_CMRX_CFG, cfg); + + cfg = rpm_read(rpm, lmac_id, RPMX_MTI_MAC100X_XIF_MODE); + + if (enable) { + cfg |= RPMX_ONESTEP_ENABLE; + cfg &= ~RPMX_TS_BINARY_MODE; + } else { + cfg &= ~RPMX_ONESTEP_ENABLE; + } + + rpm_write(rpm, lmac_id, RPMX_MTI_MAC100X_XIF_MODE, cfg); } int rpm_lmac_pfc_config(void *rpmd, int lmac_id, u8 tx_pause, u8 rx_pause, u16 pfc_en) diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rpm.h b/drivers/net/ethernet/marvell/octeontx2/af/rpm.h index c2bd6e54ea51..77f2ef9e1425 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rpm.h +++ b/drivers/net/ethernet/marvell/octeontx2/af/rpm.h @@ -16,6 +16,7 @@ /* Registers */ #define RPMX_CMRX_CFG 0x00 #define RPMX_RX_TS_PREPEND BIT_ULL(22) +#define RPMX_TX_PTP_1S_SUPPORT BIT_ULL(17) #define RPMX_CMRX_SW_INT 0x180 #define RPMX_CMRX_SW_INT_W1S 0x188 #define RPMX_CMRX_SW_INT_ENA_W1S 0x198 @@ -72,6 +73,10 @@ #define RPMX_MTI_MAC100X_CL89_PAUSE_QUANTA 0x8108 #define RPM_DEFAULT_PAUSE_TIME 0x7FF +#define RPMX_MTI_MAC100X_XIF_MODE 0x8100 +#define RPMX_ONESTEP_ENABLE BIT_ULL(5) +#define RPMX_TS_BINARY_MODE BIT_ULL(11) + /* Function Declarations */ int rpm_get_nr_lmacs(void *rpmd); u8 rpm_get_lmac_type(void *rpmd, int lmac_id); diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu.c index 7282a826d81e..3f5e09b77d4b 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu.c @@ -16,6 +16,7 @@ #include "rvu.h" #include "rvu_reg.h" #include "ptp.h" +#include "mcs.h" #include "rvu_trace.h" #include "rvu_npc_hash.h" @@ -23,8 +24,6 @@ #define DRV_NAME "rvu_af" #define DRV_STRING "Marvell OcteonTX2 RVU Admin Function Driver" -static int rvu_get_hwvf(struct rvu *rvu, int pcifunc); - static void rvu_set_msix_offset(struct rvu *rvu, struct rvu_pfvf *pfvf, struct rvu_block *block, int lf); static void rvu_clear_msix_offset(struct rvu *rvu, struct rvu_pfvf *pfvf, @@ -418,7 +417,7 @@ void rvu_get_pf_numvfs(struct rvu *rvu, int pf, int *numvfs, int *hwvf) *hwvf = cfg & 0xFFF; } -static int rvu_get_hwvf(struct rvu *rvu, int pcifunc) +int rvu_get_hwvf(struct rvu *rvu, int pcifunc) { int pf, func; u64 cfg; @@ -1159,6 +1158,12 @@ cpt: rvu_program_channels(rvu); + err = rvu_mcs_init(rvu); + if (err) { + dev_err(rvu->dev, "%s: Failed to initialize mcs\n", __func__); + goto nix_err; + } + return 0; nix_err: @@ -3293,6 +3298,7 @@ err_mbox: err_hwsetup: rvu_cgx_exit(rvu); rvu_fwdata_exit(rvu); + rvu_mcs_exit(rvu); rvu_reset_all_blocks(rvu); rvu_free_hw_resources(rvu); rvu_clear_rvum_blk_revid(rvu); @@ -3319,6 +3325,7 @@ static void rvu_remove(struct pci_dev *pdev) rvu_flr_wq_destroy(rvu); rvu_cgx_exit(rvu); rvu_fwdata_exit(rvu); + rvu_mcs_exit(rvu); rvu_mbox_destroy(&rvu->afpf_wq_info); rvu_disable_sriov(rvu); rvu_reset_all_blocks(rvu); @@ -3354,12 +3361,18 @@ static int __init rvu_init_module(void) if (err < 0) goto ptp_err; + err = pci_register_driver(&mcs_driver); + if (err < 0) + goto mcs_err; + err = pci_register_driver(&rvu_driver); if (err < 0) goto rvu_err; return 0; rvu_err: + pci_unregister_driver(&mcs_driver); +mcs_err: pci_unregister_driver(&ptp_driver); ptp_err: pci_unregister_driver(&cgx_driver); @@ -3370,6 +3383,7 @@ ptp_err: static void __exit rvu_cleanup_module(void) { pci_unregister_driver(&rvu_driver); + pci_unregister_driver(&mcs_driver); pci_unregister_driver(&ptp_driver); pci_unregister_driver(&cgx_driver); } diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu.h b/drivers/net/ethernet/marvell/octeontx2/af/rvu.h index d15bc443335d..76474385a602 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu.h +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu.h @@ -25,6 +25,8 @@ /* Subsystem Device ID */ #define PCI_SUBSYS_DEVID_96XX 0xB200 #define PCI_SUBSYS_DEVID_CN10K_A 0xB900 +#define PCI_SUBSYS_DEVID_CNF10K_B 0xBC00 +#define PCI_SUBSYS_DEVID_CN10K_B 0xBD00 /* PCI BAR nos */ #define PCI_AF_REG_BAR_NUM 0 @@ -62,6 +64,10 @@ struct rvu_debugfs { struct dentry *nix; struct dentry *npc; struct dentry *cpt; + struct dentry *mcs_root; + struct dentry *mcs; + struct dentry *mcs_rx; + struct dentry *mcs_tx; struct dump_ctx npa_aura_ctx; struct dump_ctx npa_pool_ctx; struct dump_ctx nix_cq_ctx; @@ -497,6 +503,8 @@ struct rvu { struct ptp *ptp; + int mcs_blk_cnt; + #ifdef CONFIG_DEBUG_FS struct rvu_debugfs rvu_dbg; #endif @@ -504,6 +512,12 @@ struct rvu { /* RVU switch implementation over NPC with DMAC rules */ struct rvu_switch rswitch; + + struct work_struct mcs_intr_work; + struct workqueue_struct *mcs_intr_wq; + struct list_head mcs_intrq_head; + /* mcs interrupt queue lock */ + spinlock_t mcs_intrq_lock; }; static inline void rvu_write64(struct rvu *rvu, u64 block, u64 offset, u64 val) @@ -868,4 +882,11 @@ void rvu_switch_update_rules(struct rvu *rvu, u16 pcifunc); int rvu_npc_set_parse_mode(struct rvu *rvu, u16 pcifunc, u64 mode, u8 dir, u64 pkind, u8 var_len_off, u8 var_len_off_mask, u8 shift_dir); +int rvu_get_hwvf(struct rvu *rvu, int pcifunc); + +/* CN10K MCS */ +int rvu_mcs_init(struct rvu *rvu); +int rvu_mcs_flr_handler(struct rvu *rvu, u16 pcifunc); +void rvu_mcs_exit(struct rvu *rvu); + #endif /* RVU_H */ diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_debugfs.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_debugfs.c index f42a09f04b25..a1970ebedf95 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_debugfs.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_debugfs.c @@ -19,6 +19,7 @@ #include "lmac_common.h" #include "npc.h" #include "rvu_npc_hash.h" +#include "mcs.h" #define DEBUGFS_DIR_NAME "octeontx2" @@ -227,6 +228,350 @@ static const struct file_operations rvu_dbg_##name##_fops = { \ static void print_nix_qsize(struct seq_file *filp, struct rvu_pfvf *pfvf); +static int rvu_dbg_mcs_port_stats_display(struct seq_file *filp, void *unused, int dir) +{ + struct mcs *mcs = filp->private; + struct mcs_port_stats stats; + int lmac; + + seq_puts(filp, "\n port stats\n"); + mutex_lock(&mcs->stats_lock); + for_each_set_bit(lmac, &mcs->hw->lmac_bmap, mcs->hw->lmac_cnt) { + mcs_get_port_stats(mcs, &stats, lmac, dir); + seq_printf(filp, "port%d: Tcam Miss: %lld\n", lmac, stats.tcam_miss_cnt); + seq_printf(filp, "port%d: Parser errors: %lld\n", lmac, stats.parser_err_cnt); + + if (dir == MCS_RX && mcs->hw->mcs_blks > 1) + seq_printf(filp, "port%d: Preempt error: %lld\n", lmac, + stats.preempt_err_cnt); + if (dir == MCS_TX) + seq_printf(filp, "port%d: Sectag insert error: %lld\n", lmac, + stats.sectag_insert_err_cnt); + } + mutex_unlock(&mcs->stats_lock); + return 0; +} + +static int rvu_dbg_mcs_rx_port_stats_display(struct seq_file *filp, void *unused) +{ + return rvu_dbg_mcs_port_stats_display(filp, unused, MCS_RX); +} + +RVU_DEBUG_SEQ_FOPS(mcs_rx_port_stats, mcs_rx_port_stats_display, NULL); + +static int rvu_dbg_mcs_tx_port_stats_display(struct seq_file *filp, void *unused) +{ + return rvu_dbg_mcs_port_stats_display(filp, unused, MCS_TX); +} + +RVU_DEBUG_SEQ_FOPS(mcs_tx_port_stats, mcs_tx_port_stats_display, NULL); + +static int rvu_dbg_mcs_sa_stats_display(struct seq_file *filp, void *unused, int dir) +{ + struct mcs *mcs = filp->private; + struct mcs_sa_stats stats; + struct rsrc_bmap *map; + int sa_id; + + if (dir == MCS_TX) { + map = &mcs->tx.sa; + mutex_lock(&mcs->stats_lock); + for_each_set_bit(sa_id, map->bmap, mcs->hw->sa_entries) { + seq_puts(filp, "\n TX SA stats\n"); + mcs_get_sa_stats(mcs, &stats, sa_id, MCS_TX); + seq_printf(filp, "sa%d: Pkts encrypted: %lld\n", sa_id, + stats.pkt_encrypt_cnt); + + seq_printf(filp, "sa%d: Pkts protected: %lld\n", sa_id, + stats.pkt_protected_cnt); + } + mutex_unlock(&mcs->stats_lock); + return 0; + } + + /* RX stats */ + map = &mcs->rx.sa; + mutex_lock(&mcs->stats_lock); + for_each_set_bit(sa_id, map->bmap, mcs->hw->sa_entries) { + seq_puts(filp, "\n RX SA stats\n"); + mcs_get_sa_stats(mcs, &stats, sa_id, MCS_RX); + seq_printf(filp, "sa%d: Invalid pkts: %lld\n", sa_id, stats.pkt_invalid_cnt); + seq_printf(filp, "sa%d: Pkts no sa error: %lld\n", sa_id, stats.pkt_nosaerror_cnt); + seq_printf(filp, "sa%d: Pkts not valid: %lld\n", sa_id, stats.pkt_notvalid_cnt); + seq_printf(filp, "sa%d: Pkts ok: %lld\n", sa_id, stats.pkt_ok_cnt); + seq_printf(filp, "sa%d: Pkts no sa: %lld\n", sa_id, stats.pkt_nosa_cnt); + } + mutex_unlock(&mcs->stats_lock); + return 0; +} + +static int rvu_dbg_mcs_rx_sa_stats_display(struct seq_file *filp, void *unused) +{ + return rvu_dbg_mcs_sa_stats_display(filp, unused, MCS_RX); +} + +RVU_DEBUG_SEQ_FOPS(mcs_rx_sa_stats, mcs_rx_sa_stats_display, NULL); + +static int rvu_dbg_mcs_tx_sa_stats_display(struct seq_file *filp, void *unused) +{ + return rvu_dbg_mcs_sa_stats_display(filp, unused, MCS_TX); +} + +RVU_DEBUG_SEQ_FOPS(mcs_tx_sa_stats, mcs_tx_sa_stats_display, NULL); + +static int rvu_dbg_mcs_tx_sc_stats_display(struct seq_file *filp, void *unused) +{ + struct mcs *mcs = filp->private; + struct mcs_sc_stats stats; + struct rsrc_bmap *map; + int sc_id; + + map = &mcs->tx.sc; + seq_puts(filp, "\n SC stats\n"); + + mutex_lock(&mcs->stats_lock); + for_each_set_bit(sc_id, map->bmap, mcs->hw->sc_entries) { + mcs_get_sc_stats(mcs, &stats, sc_id, MCS_TX); + seq_printf(filp, "\n=======sc%d======\n\n", sc_id); + seq_printf(filp, "sc%d: Pkts encrypted: %lld\n", sc_id, stats.pkt_encrypt_cnt); + seq_printf(filp, "sc%d: Pkts protected: %lld\n", sc_id, stats.pkt_protected_cnt); + + if (mcs->hw->mcs_blks == 1) { + seq_printf(filp, "sc%d: Octets encrypted: %lld\n", sc_id, + stats.octet_encrypt_cnt); + seq_printf(filp, "sc%d: Octets protected: %lld\n", sc_id, + stats.octet_protected_cnt); + } + } + mutex_unlock(&mcs->stats_lock); + return 0; +} + +RVU_DEBUG_SEQ_FOPS(mcs_tx_sc_stats, mcs_tx_sc_stats_display, NULL); + +static int rvu_dbg_mcs_rx_sc_stats_display(struct seq_file *filp, void *unused) +{ + struct mcs *mcs = filp->private; + struct mcs_sc_stats stats; + struct rsrc_bmap *map; + int sc_id; + + map = &mcs->rx.sc; + seq_puts(filp, "\n SC stats\n"); + + mutex_lock(&mcs->stats_lock); + for_each_set_bit(sc_id, map->bmap, mcs->hw->sc_entries) { + mcs_get_sc_stats(mcs, &stats, sc_id, MCS_RX); + seq_printf(filp, "\n=======sc%d======\n\n", sc_id); + seq_printf(filp, "sc%d: Cam hits: %lld\n", sc_id, stats.hit_cnt); + seq_printf(filp, "sc%d: Invalid pkts: %lld\n", sc_id, stats.pkt_invalid_cnt); + seq_printf(filp, "sc%d: Late pkts: %lld\n", sc_id, stats.pkt_late_cnt); + seq_printf(filp, "sc%d: Notvalid pkts: %lld\n", sc_id, stats.pkt_notvalid_cnt); + seq_printf(filp, "sc%d: Unchecked pkts: %lld\n", sc_id, stats.pkt_unchecked_cnt); + + if (mcs->hw->mcs_blks > 1) { + seq_printf(filp, "sc%d: Delay pkts: %lld\n", sc_id, stats.pkt_delay_cnt); + seq_printf(filp, "sc%d: Pkts ok: %lld\n", sc_id, stats.pkt_ok_cnt); + } + if (mcs->hw->mcs_blks == 1) { + seq_printf(filp, "sc%d: Octets decrypted: %lld\n", sc_id, + stats.octet_decrypt_cnt); + seq_printf(filp, "sc%d: Octets validated: %lld\n", sc_id, + stats.octet_validate_cnt); + } + } + mutex_unlock(&mcs->stats_lock); + return 0; +} + +RVU_DEBUG_SEQ_FOPS(mcs_rx_sc_stats, mcs_rx_sc_stats_display, NULL); + +static int rvu_dbg_mcs_flowid_stats_display(struct seq_file *filp, void *unused, int dir) +{ + struct mcs *mcs = filp->private; + struct mcs_flowid_stats stats; + struct rsrc_bmap *map; + int flow_id; + + seq_puts(filp, "\n Flowid stats\n"); + + if (dir == MCS_RX) + map = &mcs->rx.flow_ids; + else + map = &mcs->tx.flow_ids; + + mutex_lock(&mcs->stats_lock); + for_each_set_bit(flow_id, map->bmap, mcs->hw->tcam_entries) { + mcs_get_flowid_stats(mcs, &stats, flow_id, dir); + seq_printf(filp, "Flowid%d: Hit:%lld\n", flow_id, stats.tcam_hit_cnt); + } + mutex_unlock(&mcs->stats_lock); + return 0; +} + +static int rvu_dbg_mcs_tx_flowid_stats_display(struct seq_file *filp, void *unused) +{ + return rvu_dbg_mcs_flowid_stats_display(filp, unused, MCS_TX); +} + +RVU_DEBUG_SEQ_FOPS(mcs_tx_flowid_stats, mcs_tx_flowid_stats_display, NULL); + +static int rvu_dbg_mcs_rx_flowid_stats_display(struct seq_file *filp, void *unused) +{ + return rvu_dbg_mcs_flowid_stats_display(filp, unused, MCS_RX); +} + +RVU_DEBUG_SEQ_FOPS(mcs_rx_flowid_stats, mcs_rx_flowid_stats_display, NULL); + +static int rvu_dbg_mcs_tx_secy_stats_display(struct seq_file *filp, void *unused) +{ + struct mcs *mcs = filp->private; + struct mcs_secy_stats stats; + struct rsrc_bmap *map; + int secy_id; + + map = &mcs->tx.secy; + seq_puts(filp, "\n MCS TX secy stats\n"); + + mutex_lock(&mcs->stats_lock); + for_each_set_bit(secy_id, map->bmap, mcs->hw->secy_entries) { + mcs_get_tx_secy_stats(mcs, &stats, secy_id); + seq_printf(filp, "\n=======Secy%d======\n\n", secy_id); + seq_printf(filp, "secy%d: Ctrl bcast pkts: %lld\n", secy_id, + stats.ctl_pkt_bcast_cnt); + seq_printf(filp, "secy%d: Ctrl Mcast pkts: %lld\n", secy_id, + stats.ctl_pkt_mcast_cnt); + seq_printf(filp, "secy%d: Ctrl ucast pkts: %lld\n", secy_id, + stats.ctl_pkt_ucast_cnt); + seq_printf(filp, "secy%d: Ctrl octets: %lld\n", secy_id, stats.ctl_octet_cnt); + seq_printf(filp, "secy%d: Unctrl bcast cnt: %lld\n", secy_id, + stats.unctl_pkt_bcast_cnt); + seq_printf(filp, "secy%d: Unctrl mcast pkts: %lld\n", secy_id, + stats.unctl_pkt_mcast_cnt); + seq_printf(filp, "secy%d: Unctrl ucast pkts: %lld\n", secy_id, + stats.unctl_pkt_ucast_cnt); + seq_printf(filp, "secy%d: Unctrl octets: %lld\n", secy_id, stats.unctl_octet_cnt); + seq_printf(filp, "secy%d: Octet encrypted: %lld\n", secy_id, + stats.octet_encrypted_cnt); + seq_printf(filp, "secy%d: octet protected: %lld\n", secy_id, + stats.octet_protected_cnt); + seq_printf(filp, "secy%d: Pkts on active sa: %lld\n", secy_id, + stats.pkt_noactivesa_cnt); + seq_printf(filp, "secy%d: Pkts too long: %lld\n", secy_id, stats.pkt_toolong_cnt); + seq_printf(filp, "secy%d: Pkts untagged: %lld\n", secy_id, stats.pkt_untagged_cnt); + } + mutex_unlock(&mcs->stats_lock); + return 0; +} + +RVU_DEBUG_SEQ_FOPS(mcs_tx_secy_stats, mcs_tx_secy_stats_display, NULL); + +static int rvu_dbg_mcs_rx_secy_stats_display(struct seq_file *filp, void *unused) +{ + struct mcs *mcs = filp->private; + struct mcs_secy_stats stats; + struct rsrc_bmap *map; + int secy_id; + + map = &mcs->rx.secy; + seq_puts(filp, "\n MCS secy stats\n"); + + mutex_lock(&mcs->stats_lock); + for_each_set_bit(secy_id, map->bmap, mcs->hw->secy_entries) { + mcs_get_rx_secy_stats(mcs, &stats, secy_id); + seq_printf(filp, "\n=======Secy%d======\n\n", secy_id); + seq_printf(filp, "secy%d: Ctrl bcast pkts: %lld\n", secy_id, + stats.ctl_pkt_bcast_cnt); + seq_printf(filp, "secy%d: Ctrl Mcast pkts: %lld\n", secy_id, + stats.ctl_pkt_mcast_cnt); + seq_printf(filp, "secy%d: Ctrl ucast pkts: %lld\n", secy_id, + stats.ctl_pkt_ucast_cnt); + seq_printf(filp, "secy%d: Ctrl octets: %lld\n", secy_id, stats.ctl_octet_cnt); + seq_printf(filp, "secy%d: Unctrl bcast cnt: %lld\n", secy_id, + stats.unctl_pkt_bcast_cnt); + seq_printf(filp, "secy%d: Unctrl mcast pkts: %lld\n", secy_id, + stats.unctl_pkt_mcast_cnt); + seq_printf(filp, "secy%d: Unctrl ucast pkts: %lld\n", secy_id, + stats.unctl_pkt_ucast_cnt); + seq_printf(filp, "secy%d: Unctrl octets: %lld\n", secy_id, stats.unctl_octet_cnt); + seq_printf(filp, "secy%d: Octet decrypted: %lld\n", secy_id, + stats.octet_decrypted_cnt); + seq_printf(filp, "secy%d: octet validated: %lld\n", secy_id, + stats.octet_validated_cnt); + seq_printf(filp, "secy%d: Pkts on disable port: %lld\n", secy_id, + stats.pkt_port_disabled_cnt); + seq_printf(filp, "secy%d: Octets validated: %lld\n", secy_id, stats.pkt_badtag_cnt); + seq_printf(filp, "secy%d: Octets validated: %lld\n", secy_id, stats.pkt_nosa_cnt); + seq_printf(filp, "secy%d: Pkts with nosaerror: %lld\n", secy_id, + stats.pkt_nosaerror_cnt); + seq_printf(filp, "secy%d: Tagged ctrl pkts: %lld\n", secy_id, + stats.pkt_tagged_ctl_cnt); + seq_printf(filp, "secy%d: Untaged pkts: %lld\n", secy_id, stats.pkt_untaged_cnt); + seq_printf(filp, "secy%d: Ctrl pkts: %lld\n", secy_id, stats.pkt_ctl_cnt); + if (mcs->hw->mcs_blks > 1) + seq_printf(filp, "secy%d: pkts notag: %lld\n", secy_id, + stats.pkt_notag_cnt); + } + mutex_unlock(&mcs->stats_lock); + return 0; +} + +RVU_DEBUG_SEQ_FOPS(mcs_rx_secy_stats, mcs_rx_secy_stats_display, NULL); + +static void rvu_dbg_mcs_init(struct rvu *rvu) +{ + struct mcs *mcs; + char dname[10]; + int i; + + if (!rvu->mcs_blk_cnt) + return; + + rvu->rvu_dbg.mcs_root = debugfs_create_dir("mcs", rvu->rvu_dbg.root); + + for (i = 0; i < rvu->mcs_blk_cnt; i++) { + mcs = mcs_get_pdata(i); + + sprintf(dname, "mcs%d", i); + rvu->rvu_dbg.mcs = debugfs_create_dir(dname, + rvu->rvu_dbg.mcs_root); + + rvu->rvu_dbg.mcs_rx = debugfs_create_dir("rx_stats", rvu->rvu_dbg.mcs); + + debugfs_create_file("flowid", 0600, rvu->rvu_dbg.mcs_rx, mcs, + &rvu_dbg_mcs_rx_flowid_stats_fops); + + debugfs_create_file("secy", 0600, rvu->rvu_dbg.mcs_rx, mcs, + &rvu_dbg_mcs_rx_secy_stats_fops); + + debugfs_create_file("sc", 0600, rvu->rvu_dbg.mcs_rx, mcs, + &rvu_dbg_mcs_rx_sc_stats_fops); + + debugfs_create_file("sa", 0600, rvu->rvu_dbg.mcs_rx, mcs, + &rvu_dbg_mcs_rx_sa_stats_fops); + + debugfs_create_file("port", 0600, rvu->rvu_dbg.mcs_rx, mcs, + &rvu_dbg_mcs_rx_port_stats_fops); + + rvu->rvu_dbg.mcs_tx = debugfs_create_dir("tx_stats", rvu->rvu_dbg.mcs); + + debugfs_create_file("flowid", 0600, rvu->rvu_dbg.mcs_tx, mcs, + &rvu_dbg_mcs_tx_flowid_stats_fops); + + debugfs_create_file("secy", 0600, rvu->rvu_dbg.mcs_tx, mcs, + &rvu_dbg_mcs_tx_secy_stats_fops); + + debugfs_create_file("sc", 0600, rvu->rvu_dbg.mcs_tx, mcs, + &rvu_dbg_mcs_tx_sc_stats_fops); + + debugfs_create_file("sa", 0600, rvu->rvu_dbg.mcs_tx, mcs, + &rvu_dbg_mcs_tx_sa_stats_fops); + + debugfs_create_file("port", 0600, rvu->rvu_dbg.mcs_tx, mcs, + &rvu_dbg_mcs_tx_port_stats_fops); + } +} + #define LMT_MAPTBL_ENTRY_SIZE 16 /* Dump LMTST map table */ static ssize_t rvu_dbg_lmtst_map_table_display(struct file *filp, @@ -3053,6 +3398,7 @@ create: rvu_dbg_npc_init(rvu); rvu_dbg_cpt_init(rvu, BLKADDR_CPT0); rvu_dbg_cpt_init(rvu, BLKADDR_CPT1); + rvu_dbg_mcs_init(rvu); } void rvu_dbg_exit(struct rvu *rvu) diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c index 0879a48411f3..7646bb2ec89b 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c @@ -4296,8 +4296,14 @@ static int rvu_nix_block_init(struct rvu *rvu, struct nix_hw *nix_hw) /* Restore CINT timer delay to HW reset values */ rvu_write64(rvu, blkaddr, NIX_AF_CINT_DELAY, 0x0ULL); + cfg = rvu_read64(rvu, blkaddr, NIX_AF_SEB_CFG); + /* For better performance use NDC TX instead of NDC RX for SQ's SQEs" */ - rvu_write64(rvu, blkaddr, NIX_AF_SEB_CFG, 0x1ULL); + cfg |= 1ULL; + if (!is_rvu_otx2(rvu)) + cfg |= NIX_PTP_1STEP_EN; + + rvu_write64(rvu, blkaddr, NIX_AF_SEB_CFG, cfg); if (is_block_implemented(hw, blkaddr)) { err = nix_setup_txschq(rvu, nix_hw, blkaddr); diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_reg.h b/drivers/net/ethernet/marvell/octeontx2/af/rvu_reg.h index 77a9ade91f3e..0e0d536645ac 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_reg.h +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_reg.h @@ -266,6 +266,7 @@ #define NIX_AF_TX_NPC_CAPTURE_CONFIG (0x0660) #define NIX_AF_TX_NPC_CAPTURE_INFO (0x0670) #define NIX_AF_SEB_CFG (0x05F0) +#define NIX_PTP_1STEP_EN BIT_ULL(2) #define NIX_AF_DEBUG_NPC_RESP_DATAX(a) (0x680 | (a) << 3) #define NIX_AF_SMQX_CFG(a) (0x700 | (a) << 16) diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/Makefile b/drivers/net/ethernet/marvell/octeontx2/nic/Makefile index d463dc72d80a..73fdb8798614 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/Makefile +++ b/drivers/net/ethernet/marvell/octeontx2/nic/Makefile @@ -13,5 +13,6 @@ rvu_nicvf-y := otx2_vf.o otx2_devlink.o rvu_nicpf-$(CONFIG_DCB) += otx2_dcbnl.o rvu_nicvf-$(CONFIG_DCB) += otx2_dcbnl.o +rvu_nicpf-$(CONFIG_MACSEC) += cn10k_macsec.o ccflags-y += -I$(srctree)/drivers/net/ethernet/marvell/octeontx2/af diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/cn10k.c b/drivers/net/ethernet/marvell/octeontx2/nic/cn10k.c index fd4f083c699e..826f691de259 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/cn10k.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/cn10k.c @@ -86,8 +86,7 @@ int cn10k_sq_aq_init(void *dev, u16 qidx, u16 sqb_aura) aq->sq.max_sqe_size = NIX_MAXSQESZ_W16; /* 128 byte */ aq->sq.cq_ena = 1; aq->sq.ena = 1; - /* Only one SMQ is allocated, map all SQ's to that SMQ */ - aq->sq.smq = pfvf->hw.txschq_list[NIX_TXSCH_LVL_SMQ][0]; + aq->sq.smq = otx2_get_smq_idx(pfvf, qidx); aq->sq.smq_rr_weight = mtu_to_dwrr_weight(pfvf, pfvf->tx_max_pktlen); aq->sq.default_chan = pfvf->hw.tx_chan_base; aq->sq.sqe_stype = NIX_STYPE_STF; /* Cache SQB */ diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/cn10k_macsec.c b/drivers/net/ethernet/marvell/octeontx2/nic/cn10k_macsec.c new file mode 100644 index 000000000000..64f3acd7f67b --- /dev/null +++ b/drivers/net/ethernet/marvell/octeontx2/nic/cn10k_macsec.c @@ -0,0 +1,1668 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Marvell MACSEC hardware offload driver + * + * Copyright (C) 2022 Marvell. + */ + +#include <linux/rtnetlink.h> +#include <linux/bitfield.h> +#include <net/macsec.h> +#include "otx2_common.h" + +#define MCS_TCAM0_MAC_SA_MASK GENMASK_ULL(63, 48) +#define MCS_TCAM1_MAC_SA_MASK GENMASK_ULL(31, 0) +#define MCS_TCAM1_ETYPE_MASK GENMASK_ULL(47, 32) + +#define MCS_SA_MAP_MEM_SA_USE BIT_ULL(9) + +#define MCS_RX_SECY_PLCY_RW_MASK GENMASK_ULL(49, 18) +#define MCS_RX_SECY_PLCY_RP BIT_ULL(17) +#define MCS_RX_SECY_PLCY_AUTH_ENA BIT_ULL(16) +#define MCS_RX_SECY_PLCY_CIP GENMASK_ULL(8, 5) +#define MCS_RX_SECY_PLCY_VAL GENMASK_ULL(2, 1) +#define MCS_RX_SECY_PLCY_ENA BIT_ULL(0) + +#define MCS_TX_SECY_PLCY_MTU GENMASK_ULL(43, 28) +#define MCS_TX_SECY_PLCY_ST_TCI GENMASK_ULL(27, 22) +#define MCS_TX_SECY_PLCY_ST_OFFSET GENMASK_ULL(21, 15) +#define MCS_TX_SECY_PLCY_INS_MODE BIT_ULL(14) +#define MCS_TX_SECY_PLCY_AUTH_ENA BIT_ULL(13) +#define MCS_TX_SECY_PLCY_CIP GENMASK_ULL(5, 2) +#define MCS_TX_SECY_PLCY_PROTECT BIT_ULL(1) +#define MCS_TX_SECY_PLCY_ENA BIT_ULL(0) + +#define MCS_GCM_AES_128 0 +#define MCS_GCM_AES_256 1 +#define MCS_GCM_AES_XPN_128 2 +#define MCS_GCM_AES_XPN_256 3 + +#define MCS_TCI_ES 0x40 /* end station */ +#define MCS_TCI_SC 0x20 /* SCI present */ +#define MCS_TCI_SCB 0x10 /* epon */ +#define MCS_TCI_E 0x08 /* encryption */ +#define MCS_TCI_C 0x04 /* changed text */ + +static struct cn10k_mcs_txsc *cn10k_mcs_get_txsc(struct cn10k_mcs_cfg *cfg, + struct macsec_secy *secy) +{ + struct cn10k_mcs_txsc *txsc; + + list_for_each_entry(txsc, &cfg->txsc_list, entry) { + if (txsc->sw_secy == secy) + return txsc; + } + + return NULL; +} + +static struct cn10k_mcs_rxsc *cn10k_mcs_get_rxsc(struct cn10k_mcs_cfg *cfg, + struct macsec_secy *secy, + struct macsec_rx_sc *rx_sc) +{ + struct cn10k_mcs_rxsc *rxsc; + + list_for_each_entry(rxsc, &cfg->rxsc_list, entry) { + if (rxsc->sw_rxsc == rx_sc && rxsc->sw_secy == secy) + return rxsc; + } + + return NULL; +} + +static const char *rsrc_name(enum mcs_rsrc_type rsrc_type) +{ + switch (rsrc_type) { + case MCS_RSRC_TYPE_FLOWID: + return "FLOW"; + case MCS_RSRC_TYPE_SC: + return "SC"; + case MCS_RSRC_TYPE_SECY: + return "SECY"; + case MCS_RSRC_TYPE_SA: + return "SA"; + default: + return "Unknown"; + }; + + return "Unknown"; +} + +static int cn10k_mcs_alloc_rsrc(struct otx2_nic *pfvf, enum mcs_direction dir, + enum mcs_rsrc_type type, u16 *rsrc_id) +{ + struct mbox *mbox = &pfvf->mbox; + struct mcs_alloc_rsrc_req *req; + struct mcs_alloc_rsrc_rsp *rsp; + int ret = -ENOMEM; + + mutex_lock(&mbox->lock); + + req = otx2_mbox_alloc_msg_mcs_alloc_resources(mbox); + if (!req) + goto fail; + + req->rsrc_type = type; + req->rsrc_cnt = 1; + req->dir = dir; + + ret = otx2_sync_mbox_msg(mbox); + if (ret) + goto fail; + + rsp = (struct mcs_alloc_rsrc_rsp *)otx2_mbox_get_rsp(&pfvf->mbox.mbox, + 0, &req->hdr); + if (IS_ERR(rsp) || req->rsrc_cnt != rsp->rsrc_cnt || + req->rsrc_type != rsp->rsrc_type || req->dir != rsp->dir) { + ret = -EINVAL; + goto fail; + } + + switch (rsp->rsrc_type) { + case MCS_RSRC_TYPE_FLOWID: + *rsrc_id = rsp->flow_ids[0]; + break; + case MCS_RSRC_TYPE_SC: + *rsrc_id = rsp->sc_ids[0]; + break; + case MCS_RSRC_TYPE_SECY: + *rsrc_id = rsp->secy_ids[0]; + break; + case MCS_RSRC_TYPE_SA: + *rsrc_id = rsp->sa_ids[0]; + break; + default: + ret = -EINVAL; + goto fail; + }; + + mutex_unlock(&mbox->lock); + + return 0; +fail: + dev_err(pfvf->dev, "Failed to allocate %s %s resource\n", + dir == MCS_TX ? "TX" : "RX", rsrc_name(type)); + mutex_unlock(&mbox->lock); + return ret; +} + +static void cn10k_mcs_free_rsrc(struct otx2_nic *pfvf, enum mcs_direction dir, + enum mcs_rsrc_type type, u16 hw_rsrc_id, + bool all) +{ + struct mbox *mbox = &pfvf->mbox; + struct mcs_free_rsrc_req *req; + + mutex_lock(&mbox->lock); + + req = otx2_mbox_alloc_msg_mcs_free_resources(mbox); + if (!req) + goto fail; + + req->rsrc_id = hw_rsrc_id; + req->rsrc_type = type; + req->dir = dir; + if (all) + req->all = 1; + + if (otx2_sync_mbox_msg(&pfvf->mbox)) + goto fail; + + mutex_unlock(&mbox->lock); + + return; +fail: + dev_err(pfvf->dev, "Failed to free %s %s resource\n", + dir == MCS_TX ? "TX" : "RX", rsrc_name(type)); + mutex_unlock(&mbox->lock); +} + +static int cn10k_mcs_alloc_txsa(struct otx2_nic *pfvf, u16 *hw_sa_id) +{ + return cn10k_mcs_alloc_rsrc(pfvf, MCS_TX, MCS_RSRC_TYPE_SA, hw_sa_id); +} + +static int cn10k_mcs_alloc_rxsa(struct otx2_nic *pfvf, u16 *hw_sa_id) +{ + return cn10k_mcs_alloc_rsrc(pfvf, MCS_RX, MCS_RSRC_TYPE_SA, hw_sa_id); +} + +static void cn10k_mcs_free_txsa(struct otx2_nic *pfvf, u16 hw_sa_id) +{ + cn10k_mcs_free_rsrc(pfvf, MCS_TX, MCS_RSRC_TYPE_SA, hw_sa_id, false); +} + +static void cn10k_mcs_free_rxsa(struct otx2_nic *pfvf, u16 hw_sa_id) +{ + cn10k_mcs_free_rsrc(pfvf, MCS_RX, MCS_RSRC_TYPE_SA, hw_sa_id, false); +} + +static int cn10k_mcs_write_rx_secy(struct otx2_nic *pfvf, + struct macsec_secy *secy, u8 hw_secy_id) +{ + struct mcs_secy_plcy_write_req *req; + struct mbox *mbox = &pfvf->mbox; + u64 policy; + int ret; + + mutex_lock(&mbox->lock); + + req = otx2_mbox_alloc_msg_mcs_secy_plcy_write(mbox); + if (!req) { + ret = -ENOMEM; + goto fail; + } + + policy = FIELD_PREP(MCS_RX_SECY_PLCY_RW_MASK, secy->replay_window); + if (secy->replay_protect) + policy |= MCS_RX_SECY_PLCY_RP; + + policy |= MCS_RX_SECY_PLCY_AUTH_ENA; + policy |= FIELD_PREP(MCS_RX_SECY_PLCY_CIP, MCS_GCM_AES_128); + policy |= FIELD_PREP(MCS_RX_SECY_PLCY_VAL, secy->validate_frames); + + policy |= MCS_RX_SECY_PLCY_ENA; + + req->plcy = policy; + req->secy_id = hw_secy_id; + req->dir = MCS_RX; + + ret = otx2_sync_mbox_msg(mbox); + +fail: + mutex_unlock(&mbox->lock); + return ret; +} + +static int cn10k_mcs_write_rx_flowid(struct otx2_nic *pfvf, + struct cn10k_mcs_rxsc *rxsc, u8 hw_secy_id) +{ + struct macsec_rx_sc *sw_rx_sc = rxsc->sw_rxsc; + struct mcs_flowid_entry_write_req *req; + struct mbox *mbox = &pfvf->mbox; + int ret; + + mutex_lock(&mbox->lock); + + req = otx2_mbox_alloc_msg_mcs_flowid_entry_write(mbox); + if (!req) { + ret = -ENOMEM; + goto fail; + } + + req->data[1] = FIELD_PREP(MCS_TCAM1_ETYPE_MASK, ETH_P_MACSEC); + req->mask[1] = ~0ULL; + req->mask[1] &= ~MCS_TCAM1_ETYPE_MASK; + + req->mask[0] = ~0ULL; + req->mask[2] = ~0ULL; + req->mask[3] = ~0ULL; + + req->flow_id = rxsc->hw_flow_id; + req->secy_id = hw_secy_id; + req->sc_id = rxsc->hw_sc_id; + req->dir = MCS_RX; + + if (sw_rx_sc->active) + req->ena = 1; + + ret = otx2_sync_mbox_msg(mbox); + +fail: + mutex_unlock(&mbox->lock); + return ret; +} + +static int cn10k_mcs_write_sc_cam(struct otx2_nic *pfvf, + struct cn10k_mcs_rxsc *rxsc, u8 hw_secy_id) +{ + struct macsec_rx_sc *sw_rx_sc = rxsc->sw_rxsc; + struct mcs_rx_sc_cam_write_req *sc_req; + struct mbox *mbox = &pfvf->mbox; + int ret; + + mutex_lock(&mbox->lock); + + sc_req = otx2_mbox_alloc_msg_mcs_rx_sc_cam_write(mbox); + if (!sc_req) { + return -ENOMEM; + goto fail; + } + + sc_req->sci = (__force u64)cpu_to_be64((__force u64)sw_rx_sc->sci); + sc_req->sc_id = rxsc->hw_sc_id; + sc_req->secy_id = hw_secy_id; + + ret = otx2_sync_mbox_msg(mbox); + +fail: + mutex_unlock(&mbox->lock); + return ret; +} + +static int cn10k_mcs_write_rx_sa_plcy(struct otx2_nic *pfvf, + struct macsec_secy *secy, + struct cn10k_mcs_rxsc *rxsc, + u8 assoc_num, bool sa_in_use) +{ + unsigned char *src = rxsc->sa_key[assoc_num]; + struct mcs_sa_plcy_write_req *plcy_req; + struct mcs_rx_sc_sa_map *map_req; + struct mbox *mbox = &pfvf->mbox; + u8 reg, key_len; + int ret; + + mutex_lock(&mbox->lock); + + plcy_req = otx2_mbox_alloc_msg_mcs_sa_plcy_write(mbox); + if (!plcy_req) { + ret = -ENOMEM; + goto fail; + } + + map_req = otx2_mbox_alloc_msg_mcs_rx_sc_sa_map_write(mbox); + if (!map_req) { + otx2_mbox_reset(&mbox->mbox, 0); + ret = -ENOMEM; + goto fail; + } + + for (reg = 0, key_len = 0; key_len < secy->key_len; key_len += 8) { + memcpy((u8 *)&plcy_req->plcy[0][reg], + (src + reg * 8), 8); + reg++; + } + + plcy_req->sa_index[0] = rxsc->hw_sa_id[assoc_num]; + plcy_req->sa_cnt = 1; + plcy_req->dir = MCS_RX; + + map_req->sa_index = rxsc->hw_sa_id[assoc_num]; + map_req->sa_in_use = sa_in_use; + map_req->sc_id = rxsc->hw_sc_id; + map_req->an = assoc_num; + + /* Send two messages together */ + ret = otx2_sync_mbox_msg(mbox); + +fail: + mutex_unlock(&mbox->lock); + return ret; +} + +static int cn10k_mcs_write_rx_sa_pn(struct otx2_nic *pfvf, + struct cn10k_mcs_rxsc *rxsc, + u8 assoc_num, u64 next_pn) +{ + struct mcs_pn_table_write_req *req; + struct mbox *mbox = &pfvf->mbox; + int ret; + + mutex_lock(&mbox->lock); + + req = otx2_mbox_alloc_msg_mcs_pn_table_write(mbox); + if (!req) { + ret = -ENOMEM; + goto fail; + } + + req->pn_id = rxsc->hw_sa_id[assoc_num]; + req->next_pn = next_pn; + req->dir = MCS_RX; + + ret = otx2_sync_mbox_msg(mbox); + +fail: + mutex_unlock(&mbox->lock); + return ret; +} + +static int cn10k_mcs_write_tx_secy(struct otx2_nic *pfvf, + struct macsec_secy *secy, + struct cn10k_mcs_txsc *txsc) +{ + struct mcs_secy_plcy_write_req *req; + struct mbox *mbox = &pfvf->mbox; + struct macsec_tx_sc *sw_tx_sc; + /* Insert SecTag after 12 bytes (DA+SA)*/ + u8 tag_offset = 12; + u8 sectag_tci = 0; + u64 policy; + int ret; + + sw_tx_sc = &secy->tx_sc; + + mutex_lock(&mbox->lock); + + req = otx2_mbox_alloc_msg_mcs_secy_plcy_write(mbox); + if (!req) { + ret = -ENOMEM; + goto fail; + } + + if (sw_tx_sc->send_sci) { + sectag_tci |= MCS_TCI_SC; + } else { + if (sw_tx_sc->end_station) + sectag_tci |= MCS_TCI_ES; + if (sw_tx_sc->scb) + sectag_tci |= MCS_TCI_SCB; + } + + if (sw_tx_sc->encrypt) + sectag_tci |= (MCS_TCI_E | MCS_TCI_C); + + policy = FIELD_PREP(MCS_TX_SECY_PLCY_MTU, secy->netdev->mtu); + /* Write SecTag excluding AN bits(1..0) */ + policy |= FIELD_PREP(MCS_TX_SECY_PLCY_ST_TCI, sectag_tci >> 2); + policy |= FIELD_PREP(MCS_TX_SECY_PLCY_ST_OFFSET, tag_offset); + policy |= MCS_TX_SECY_PLCY_INS_MODE; + policy |= MCS_TX_SECY_PLCY_AUTH_ENA; + policy |= FIELD_PREP(MCS_TX_SECY_PLCY_CIP, MCS_GCM_AES_128); + + if (secy->protect_frames) + policy |= MCS_TX_SECY_PLCY_PROTECT; + + /* If the encodingsa does not exist/active and protect is + * not set then frames can be sent out as it is. Hence enable + * the policy irrespective of secy operational when !protect. + */ + if (!secy->protect_frames || secy->operational) + policy |= MCS_TX_SECY_PLCY_ENA; + + req->plcy = policy; + req->secy_id = txsc->hw_secy_id_tx; + req->dir = MCS_TX; + + ret = otx2_sync_mbox_msg(mbox); + +fail: + mutex_unlock(&mbox->lock); + return ret; +} + +static int cn10k_mcs_write_tx_flowid(struct otx2_nic *pfvf, + struct macsec_secy *secy, + struct cn10k_mcs_txsc *txsc) +{ + struct mcs_flowid_entry_write_req *req; + struct mbox *mbox = &pfvf->mbox; + u64 mac_sa; + int ret; + + mutex_lock(&mbox->lock); + + req = otx2_mbox_alloc_msg_mcs_flowid_entry_write(mbox); + if (!req) { + ret = -ENOMEM; + goto fail; + } + + mac_sa = ether_addr_to_u64(secy->netdev->dev_addr); + + req->data[0] = FIELD_PREP(MCS_TCAM0_MAC_SA_MASK, mac_sa); + req->data[1] = FIELD_PREP(MCS_TCAM1_MAC_SA_MASK, mac_sa >> 16); + + req->mask[0] = ~0ULL; + req->mask[0] &= ~MCS_TCAM0_MAC_SA_MASK; + + req->mask[1] = ~0ULL; + req->mask[1] &= ~MCS_TCAM1_MAC_SA_MASK; + + req->mask[2] = ~0ULL; + req->mask[3] = ~0ULL; + + req->flow_id = txsc->hw_flow_id; + req->secy_id = txsc->hw_secy_id_tx; + req->sc_id = txsc->hw_sc_id; + req->sci = (__force u64)cpu_to_be64((__force u64)secy->sci); + req->dir = MCS_TX; + /* This can be enabled since stack xmits packets only when interface is up */ + req->ena = 1; + + ret = otx2_sync_mbox_msg(mbox); + +fail: + mutex_unlock(&mbox->lock); + return ret; +} + +static int cn10k_mcs_link_tx_sa2sc(struct otx2_nic *pfvf, + struct macsec_secy *secy, + struct cn10k_mcs_txsc *txsc, + u8 sa_num, bool sa_active) +{ + struct mcs_tx_sc_sa_map *map_req; + struct mbox *mbox = &pfvf->mbox; + int ret; + + /* Link the encoding_sa only to SC out of all SAs */ + if (txsc->encoding_sa != sa_num) + return 0; + + mutex_lock(&mbox->lock); + + map_req = otx2_mbox_alloc_msg_mcs_tx_sc_sa_map_write(mbox); + if (!map_req) { + otx2_mbox_reset(&mbox->mbox, 0); + ret = -ENOMEM; + goto fail; + } + + map_req->sa_index0 = txsc->hw_sa_id[sa_num]; + map_req->sa_index0_vld = sa_active; + map_req->sectag_sci = (__force u64)cpu_to_be64((__force u64)secy->sci); + map_req->sc_id = txsc->hw_sc_id; + + ret = otx2_sync_mbox_msg(mbox); + +fail: + mutex_unlock(&mbox->lock); + return ret; +} + +static int cn10k_mcs_write_tx_sa_plcy(struct otx2_nic *pfvf, + struct macsec_secy *secy, + struct cn10k_mcs_txsc *txsc, + u8 assoc_num) +{ + unsigned char *src = txsc->sa_key[assoc_num]; + struct mcs_sa_plcy_write_req *plcy_req; + struct mbox *mbox = &pfvf->mbox; + u8 reg, key_len; + int ret; + + mutex_lock(&mbox->lock); + + plcy_req = otx2_mbox_alloc_msg_mcs_sa_plcy_write(mbox); + if (!plcy_req) { + ret = -ENOMEM; + goto fail; + } + + for (reg = 0, key_len = 0; key_len < secy->key_len; key_len += 8) { + memcpy((u8 *)&plcy_req->plcy[0][reg], (src + reg * 8), 8); + reg++; + } + + plcy_req->plcy[0][8] = assoc_num; + plcy_req->sa_index[0] = txsc->hw_sa_id[assoc_num]; + plcy_req->sa_cnt = 1; + plcy_req->dir = MCS_TX; + + ret = otx2_sync_mbox_msg(mbox); + +fail: + mutex_unlock(&mbox->lock); + return ret; +} + +static int cn10k_write_tx_sa_pn(struct otx2_nic *pfvf, + struct cn10k_mcs_txsc *txsc, + u8 assoc_num, u64 next_pn) +{ + struct mcs_pn_table_write_req *req; + struct mbox *mbox = &pfvf->mbox; + int ret; + + mutex_lock(&mbox->lock); + + req = otx2_mbox_alloc_msg_mcs_pn_table_write(mbox); + if (!req) { + ret = -ENOMEM; + goto fail; + } + + req->pn_id = txsc->hw_sa_id[assoc_num]; + req->next_pn = next_pn; + req->dir = MCS_TX; + + ret = otx2_sync_mbox_msg(mbox); + +fail: + mutex_unlock(&mbox->lock); + return ret; +} + +static int cn10k_mcs_ena_dis_flowid(struct otx2_nic *pfvf, u16 hw_flow_id, + bool enable, enum mcs_direction dir) +{ + struct mcs_flowid_ena_dis_entry *req; + struct mbox *mbox = &pfvf->mbox; + int ret; + + mutex_lock(&mbox->lock); + + req = otx2_mbox_alloc_msg_mcs_flowid_ena_entry(mbox); + if (!req) { + return -ENOMEM; + goto fail; + } + + req->flow_id = hw_flow_id; + req->ena = enable; + req->dir = dir; + + ret = otx2_sync_mbox_msg(mbox); + +fail: + mutex_unlock(&mbox->lock); + return ret; +} + +static int cn10k_mcs_sa_stats(struct otx2_nic *pfvf, u8 hw_sa_id, + struct mcs_sa_stats *rsp_p, + enum mcs_direction dir, bool clear) +{ + struct mcs_clear_stats *clear_req; + struct mbox *mbox = &pfvf->mbox; + struct mcs_stats_req *req; + struct mcs_sa_stats *rsp; + int ret; + + mutex_lock(&mbox->lock); + + req = otx2_mbox_alloc_msg_mcs_get_sa_stats(mbox); + if (!req) { + ret = -ENOMEM; + goto fail; + } + + req->id = hw_sa_id; + req->dir = dir; + + if (!clear) + goto send_msg; + + clear_req = otx2_mbox_alloc_msg_mcs_clear_stats(mbox); + if (!clear_req) { + ret = -ENOMEM; + goto fail; + } + clear_req->id = hw_sa_id; + clear_req->dir = dir; + clear_req->type = MCS_RSRC_TYPE_SA; + +send_msg: + ret = otx2_sync_mbox_msg(mbox); + if (ret) + goto fail; + + rsp = (struct mcs_sa_stats *)otx2_mbox_get_rsp(&pfvf->mbox.mbox, + 0, &req->hdr); + if (IS_ERR(rsp)) { + ret = PTR_ERR(rsp); + goto fail; + } + + memcpy(rsp_p, rsp, sizeof(*rsp_p)); + + mutex_unlock(&mbox->lock); + + return 0; +fail: + mutex_unlock(&mbox->lock); + return ret; +} + +static int cn10k_mcs_sc_stats(struct otx2_nic *pfvf, u8 hw_sc_id, + struct mcs_sc_stats *rsp_p, + enum mcs_direction dir, bool clear) +{ + struct mcs_clear_stats *clear_req; + struct mbox *mbox = &pfvf->mbox; + struct mcs_stats_req *req; + struct mcs_sc_stats *rsp; + int ret; + + mutex_lock(&mbox->lock); + + req = otx2_mbox_alloc_msg_mcs_get_sc_stats(mbox); + if (!req) { + ret = -ENOMEM; + goto fail; + } + + req->id = hw_sc_id; + req->dir = dir; + + if (!clear) + goto send_msg; + + clear_req = otx2_mbox_alloc_msg_mcs_clear_stats(mbox); + if (!clear_req) { + ret = -ENOMEM; + goto fail; + } + clear_req->id = hw_sc_id; + clear_req->dir = dir; + clear_req->type = MCS_RSRC_TYPE_SC; + +send_msg: + ret = otx2_sync_mbox_msg(mbox); + if (ret) + goto fail; + + rsp = (struct mcs_sc_stats *)otx2_mbox_get_rsp(&pfvf->mbox.mbox, + 0, &req->hdr); + if (IS_ERR(rsp)) { + ret = PTR_ERR(rsp); + goto fail; + } + + memcpy(rsp_p, rsp, sizeof(*rsp_p)); + + mutex_unlock(&mbox->lock); + + return 0; +fail: + mutex_unlock(&mbox->lock); + return ret; +} + +static int cn10k_mcs_secy_stats(struct otx2_nic *pfvf, u8 hw_secy_id, + struct mcs_secy_stats *rsp_p, + enum mcs_direction dir, bool clear) +{ + struct mcs_clear_stats *clear_req; + struct mbox *mbox = &pfvf->mbox; + struct mcs_secy_stats *rsp; + struct mcs_stats_req *req; + int ret; + + mutex_lock(&mbox->lock); + + req = otx2_mbox_alloc_msg_mcs_get_secy_stats(mbox); + if (!req) { + ret = -ENOMEM; + goto fail; + } + + req->id = hw_secy_id; + req->dir = dir; + + if (!clear) + goto send_msg; + + clear_req = otx2_mbox_alloc_msg_mcs_clear_stats(mbox); + if (!clear_req) { + ret = -ENOMEM; + goto fail; + } + clear_req->id = hw_secy_id; + clear_req->dir = dir; + clear_req->type = MCS_RSRC_TYPE_SECY; + +send_msg: + ret = otx2_sync_mbox_msg(mbox); + if (ret) + goto fail; + + rsp = (struct mcs_secy_stats *)otx2_mbox_get_rsp(&pfvf->mbox.mbox, + 0, &req->hdr); + if (IS_ERR(rsp)) { + ret = PTR_ERR(rsp); + goto fail; + } + + memcpy(rsp_p, rsp, sizeof(*rsp_p)); + + mutex_unlock(&mbox->lock); + + return 0; +fail: + mutex_unlock(&mbox->lock); + return ret; +} + +static struct cn10k_mcs_txsc *cn10k_mcs_create_txsc(struct otx2_nic *pfvf) +{ + struct cn10k_mcs_txsc *txsc; + int ret; + + txsc = kzalloc(sizeof(*txsc), GFP_KERNEL); + if (!txsc) + return ERR_PTR(-ENOMEM); + + ret = cn10k_mcs_alloc_rsrc(pfvf, MCS_TX, MCS_RSRC_TYPE_FLOWID, + &txsc->hw_flow_id); + if (ret) + goto fail; + + /* For a SecY, one TX secy and one RX secy HW resources are needed */ + ret = cn10k_mcs_alloc_rsrc(pfvf, MCS_TX, MCS_RSRC_TYPE_SECY, + &txsc->hw_secy_id_tx); + if (ret) + goto free_flowid; + + ret = cn10k_mcs_alloc_rsrc(pfvf, MCS_RX, MCS_RSRC_TYPE_SECY, + &txsc->hw_secy_id_rx); + if (ret) + goto free_tx_secy; + + ret = cn10k_mcs_alloc_rsrc(pfvf, MCS_TX, MCS_RSRC_TYPE_SC, + &txsc->hw_sc_id); + if (ret) + goto free_rx_secy; + + return txsc; +free_rx_secy: + cn10k_mcs_free_rsrc(pfvf, MCS_RX, MCS_RSRC_TYPE_SECY, + txsc->hw_secy_id_rx, false); +free_tx_secy: + cn10k_mcs_free_rsrc(pfvf, MCS_TX, MCS_RSRC_TYPE_SECY, + txsc->hw_secy_id_tx, false); +free_flowid: + cn10k_mcs_free_rsrc(pfvf, MCS_TX, MCS_RSRC_TYPE_FLOWID, + txsc->hw_flow_id, false); +fail: + return ERR_PTR(ret); +} + +/* Free Tx SC and its SAs(if any) resources to AF + */ +static void cn10k_mcs_delete_txsc(struct otx2_nic *pfvf, + struct cn10k_mcs_txsc *txsc) +{ + u8 sa_bmap = txsc->sa_bmap; + u8 sa_num = 0; + + while (sa_bmap) { + if (sa_bmap & 1) { + cn10k_mcs_write_tx_sa_plcy(pfvf, txsc->sw_secy, + txsc, sa_num); + cn10k_mcs_free_txsa(pfvf, txsc->hw_sa_id[sa_num]); + } + sa_num++; + sa_bmap >>= 1; + } + + cn10k_mcs_free_rsrc(pfvf, MCS_TX, MCS_RSRC_TYPE_SC, + txsc->hw_sc_id, false); + cn10k_mcs_free_rsrc(pfvf, MCS_RX, MCS_RSRC_TYPE_SECY, + txsc->hw_secy_id_rx, false); + cn10k_mcs_free_rsrc(pfvf, MCS_TX, MCS_RSRC_TYPE_SECY, + txsc->hw_secy_id_tx, false); + cn10k_mcs_free_rsrc(pfvf, MCS_TX, MCS_RSRC_TYPE_FLOWID, + txsc->hw_flow_id, false); +} + +static struct cn10k_mcs_rxsc *cn10k_mcs_create_rxsc(struct otx2_nic *pfvf) +{ + struct cn10k_mcs_rxsc *rxsc; + int ret; + + rxsc = kzalloc(sizeof(*rxsc), GFP_KERNEL); + if (!rxsc) + return ERR_PTR(-ENOMEM); + + ret = cn10k_mcs_alloc_rsrc(pfvf, MCS_RX, MCS_RSRC_TYPE_FLOWID, + &rxsc->hw_flow_id); + if (ret) + goto fail; + + ret = cn10k_mcs_alloc_rsrc(pfvf, MCS_RX, MCS_RSRC_TYPE_SC, + &rxsc->hw_sc_id); + if (ret) + goto free_flowid; + + return rxsc; +free_flowid: + cn10k_mcs_free_rsrc(pfvf, MCS_RX, MCS_RSRC_TYPE_FLOWID, + rxsc->hw_flow_id, false); +fail: + return ERR_PTR(ret); +} + +/* Free Rx SC and its SAs(if any) resources to AF + */ +static void cn10k_mcs_delete_rxsc(struct otx2_nic *pfvf, + struct cn10k_mcs_rxsc *rxsc) +{ + u8 sa_bmap = rxsc->sa_bmap; + u8 sa_num = 0; + + while (sa_bmap) { + if (sa_bmap & 1) { + cn10k_mcs_write_rx_sa_plcy(pfvf, rxsc->sw_secy, rxsc, + sa_num, false); + cn10k_mcs_free_rxsa(pfvf, rxsc->hw_sa_id[sa_num]); + } + sa_num++; + sa_bmap >>= 1; + } + + cn10k_mcs_free_rsrc(pfvf, MCS_RX, MCS_RSRC_TYPE_SC, + rxsc->hw_sc_id, false); + cn10k_mcs_free_rsrc(pfvf, MCS_RX, MCS_RSRC_TYPE_FLOWID, + rxsc->hw_flow_id, false); +} + +static int cn10k_mcs_secy_tx_cfg(struct otx2_nic *pfvf, struct macsec_secy *secy, + struct cn10k_mcs_txsc *txsc, + struct macsec_tx_sa *sw_tx_sa, u8 sa_num) +{ + if (sw_tx_sa) { + cn10k_mcs_write_tx_sa_plcy(pfvf, secy, txsc, sa_num); + cn10k_write_tx_sa_pn(pfvf, txsc, sa_num, + sw_tx_sa->next_pn_halves.lower); + cn10k_mcs_link_tx_sa2sc(pfvf, secy, txsc, sa_num, + sw_tx_sa->active); + } + + cn10k_mcs_write_tx_secy(pfvf, secy, txsc); + cn10k_mcs_write_tx_flowid(pfvf, secy, txsc); + /* When updating secy, change RX secy also */ + cn10k_mcs_write_rx_secy(pfvf, secy, txsc->hw_secy_id_rx); + + return 0; +} + +static int cn10k_mcs_secy_rx_cfg(struct otx2_nic *pfvf, + struct macsec_secy *secy, u8 hw_secy_id) +{ + struct cn10k_mcs_cfg *cfg = pfvf->macsec_cfg; + struct cn10k_mcs_rxsc *mcs_rx_sc; + struct macsec_rx_sc *sw_rx_sc; + struct macsec_rx_sa *sw_rx_sa; + u8 sa_num; + + for (sw_rx_sc = rcu_dereference_bh(secy->rx_sc); sw_rx_sc && sw_rx_sc->active; + sw_rx_sc = rcu_dereference_bh(sw_rx_sc->next)) { + mcs_rx_sc = cn10k_mcs_get_rxsc(cfg, secy, sw_rx_sc); + if (unlikely(!mcs_rx_sc)) + continue; + + for (sa_num = 0; sa_num < CN10K_MCS_SA_PER_SC; sa_num++) { + sw_rx_sa = rcu_dereference_bh(sw_rx_sc->sa[sa_num]); + if (!sw_rx_sa) + continue; + + cn10k_mcs_write_rx_sa_plcy(pfvf, secy, mcs_rx_sc, + sa_num, sw_rx_sa->active); + cn10k_mcs_write_rx_sa_pn(pfvf, mcs_rx_sc, sa_num, + sw_rx_sa->next_pn_halves.lower); + } + + cn10k_mcs_write_rx_flowid(pfvf, mcs_rx_sc, hw_secy_id); + cn10k_mcs_write_sc_cam(pfvf, mcs_rx_sc, hw_secy_id); + } + + return 0; +} + +static int cn10k_mcs_disable_rxscs(struct otx2_nic *pfvf, + struct macsec_secy *secy, + bool delete) +{ + struct cn10k_mcs_cfg *cfg = pfvf->macsec_cfg; + struct cn10k_mcs_rxsc *mcs_rx_sc; + struct macsec_rx_sc *sw_rx_sc; + int ret; + + for (sw_rx_sc = rcu_dereference_bh(secy->rx_sc); sw_rx_sc && sw_rx_sc->active; + sw_rx_sc = rcu_dereference_bh(sw_rx_sc->next)) { + mcs_rx_sc = cn10k_mcs_get_rxsc(cfg, secy, sw_rx_sc); + if (unlikely(!mcs_rx_sc)) + continue; + + ret = cn10k_mcs_ena_dis_flowid(pfvf, mcs_rx_sc->hw_flow_id, + false, MCS_RX); + if (ret) + dev_err(pfvf->dev, "Failed to disable TCAM for SC %d\n", + mcs_rx_sc->hw_sc_id); + if (delete) { + cn10k_mcs_delete_rxsc(pfvf, mcs_rx_sc); + list_del(&mcs_rx_sc->entry); + kfree(mcs_rx_sc); + } + } + + return 0; +} + +static void cn10k_mcs_sync_stats(struct otx2_nic *pfvf, struct macsec_secy *secy, + struct cn10k_mcs_txsc *txsc) +{ + struct cn10k_mcs_cfg *cfg = pfvf->macsec_cfg; + struct mcs_secy_stats rx_rsp = { 0 }; + struct mcs_sc_stats sc_rsp = { 0 }; + struct cn10k_mcs_rxsc *rxsc; + + /* Because of shared counters for some stats in the hardware, when + * updating secy policy take a snapshot of current stats and reset them. + * Below are the effected stats because of shared counters. + */ + + /* Check if sync is really needed */ + if (secy->validate_frames == txsc->last_validate_frames && + secy->protect_frames == txsc->last_protect_frames) + return; + + cn10k_mcs_secy_stats(pfvf, txsc->hw_secy_id_rx, &rx_rsp, MCS_RX, true); + + txsc->stats.InPktsBadTag += rx_rsp.pkt_badtag_cnt; + txsc->stats.InPktsUnknownSCI += rx_rsp.pkt_nosa_cnt; + txsc->stats.InPktsNoSCI += rx_rsp.pkt_nosaerror_cnt; + if (txsc->last_validate_frames == MACSEC_VALIDATE_STRICT) + txsc->stats.InPktsNoTag += rx_rsp.pkt_untaged_cnt; + else + txsc->stats.InPktsUntagged += rx_rsp.pkt_untaged_cnt; + + list_for_each_entry(rxsc, &cfg->rxsc_list, entry) { + cn10k_mcs_sc_stats(pfvf, rxsc->hw_sc_id, &sc_rsp, MCS_RX, true); + + rxsc->stats.InOctetsValidated += sc_rsp.octet_validate_cnt; + rxsc->stats.InOctetsDecrypted += sc_rsp.octet_decrypt_cnt; + + rxsc->stats.InPktsInvalid += sc_rsp.pkt_invalid_cnt; + rxsc->stats.InPktsNotValid += sc_rsp.pkt_notvalid_cnt; + + if (txsc->last_protect_frames) + rxsc->stats.InPktsLate += sc_rsp.pkt_late_cnt; + else + rxsc->stats.InPktsDelayed += sc_rsp.pkt_late_cnt; + + if (txsc->last_validate_frames == MACSEC_VALIDATE_CHECK) + rxsc->stats.InPktsUnchecked += sc_rsp.pkt_unchecked_cnt; + else + rxsc->stats.InPktsOK += sc_rsp.pkt_unchecked_cnt; + } + + txsc->last_validate_frames = secy->validate_frames; + txsc->last_protect_frames = secy->protect_frames; +} + +static int cn10k_mdo_open(struct macsec_context *ctx) +{ + struct otx2_nic *pfvf = netdev_priv(ctx->netdev); + struct cn10k_mcs_cfg *cfg = pfvf->macsec_cfg; + struct macsec_secy *secy = ctx->secy; + struct macsec_tx_sa *sw_tx_sa; + struct cn10k_mcs_txsc *txsc; + u8 sa_num; + int err; + + txsc = cn10k_mcs_get_txsc(cfg, ctx->secy); + if (!txsc) + return -ENOENT; + + sa_num = txsc->encoding_sa; + sw_tx_sa = rcu_dereference_bh(secy->tx_sc.sa[sa_num]); + + err = cn10k_mcs_secy_tx_cfg(pfvf, secy, txsc, sw_tx_sa, sa_num); + if (err) + return err; + + return cn10k_mcs_secy_rx_cfg(pfvf, secy, txsc->hw_secy_id_rx); +} + +static int cn10k_mdo_stop(struct macsec_context *ctx) +{ + struct otx2_nic *pfvf = netdev_priv(ctx->netdev); + struct cn10k_mcs_cfg *cfg = pfvf->macsec_cfg; + struct cn10k_mcs_txsc *txsc; + int err; + + txsc = cn10k_mcs_get_txsc(cfg, ctx->secy); + if (!txsc) + return -ENOENT; + + err = cn10k_mcs_ena_dis_flowid(pfvf, txsc->hw_flow_id, false, MCS_TX); + if (err) + return err; + + return cn10k_mcs_disable_rxscs(pfvf, ctx->secy, false); +} + +static int cn10k_mdo_add_secy(struct macsec_context *ctx) +{ + struct otx2_nic *pfvf = netdev_priv(ctx->netdev); + struct cn10k_mcs_cfg *cfg = pfvf->macsec_cfg; + struct macsec_secy *secy = ctx->secy; + struct cn10k_mcs_txsc *txsc; + + if (secy->icv_len != MACSEC_DEFAULT_ICV_LEN) + return -EOPNOTSUPP; + + /* Stick to 16 bytes key len until XPN support is added */ + if (secy->key_len != 16) + return -EOPNOTSUPP; + + if (secy->xpn) + return -EOPNOTSUPP; + + txsc = cn10k_mcs_create_txsc(pfvf); + if (IS_ERR(txsc)) + return -ENOSPC; + + txsc->sw_secy = secy; + txsc->encoding_sa = secy->tx_sc.encoding_sa; + txsc->last_validate_frames = secy->validate_frames; + txsc->last_protect_frames = secy->protect_frames; + + list_add(&txsc->entry, &cfg->txsc_list); + + if (netif_running(secy->netdev)) + return cn10k_mcs_secy_tx_cfg(pfvf, secy, txsc, NULL, 0); + + return 0; +} + +static int cn10k_mdo_upd_secy(struct macsec_context *ctx) +{ + struct otx2_nic *pfvf = netdev_priv(ctx->netdev); + struct cn10k_mcs_cfg *cfg = pfvf->macsec_cfg; + struct macsec_secy *secy = ctx->secy; + struct macsec_tx_sa *sw_tx_sa; + struct cn10k_mcs_txsc *txsc; + u8 sa_num; + int err; + + txsc = cn10k_mcs_get_txsc(cfg, secy); + if (!txsc) + return -ENOENT; + + txsc->encoding_sa = secy->tx_sc.encoding_sa; + + sa_num = txsc->encoding_sa; + sw_tx_sa = rcu_dereference_bh(secy->tx_sc.sa[sa_num]); + + if (netif_running(secy->netdev)) { + cn10k_mcs_sync_stats(pfvf, secy, txsc); + + err = cn10k_mcs_secy_tx_cfg(pfvf, secy, txsc, sw_tx_sa, sa_num); + if (err) + return err; + } + + return 0; +} + +static int cn10k_mdo_del_secy(struct macsec_context *ctx) +{ + struct otx2_nic *pfvf = netdev_priv(ctx->netdev); + struct cn10k_mcs_cfg *cfg = pfvf->macsec_cfg; + struct cn10k_mcs_txsc *txsc; + + txsc = cn10k_mcs_get_txsc(cfg, ctx->secy); + if (!txsc) + return -ENOENT; + + cn10k_mcs_ena_dis_flowid(pfvf, txsc->hw_flow_id, false, MCS_TX); + cn10k_mcs_disable_rxscs(pfvf, ctx->secy, true); + cn10k_mcs_delete_txsc(pfvf, txsc); + list_del(&txsc->entry); + kfree(txsc); + + return 0; +} + +static int cn10k_mdo_add_txsa(struct macsec_context *ctx) +{ + struct otx2_nic *pfvf = netdev_priv(ctx->netdev); + struct macsec_tx_sa *sw_tx_sa = ctx->sa.tx_sa; + struct cn10k_mcs_cfg *cfg = pfvf->macsec_cfg; + struct macsec_secy *secy = ctx->secy; + u8 sa_num = ctx->sa.assoc_num; + struct cn10k_mcs_txsc *txsc; + int err; + + txsc = cn10k_mcs_get_txsc(cfg, secy); + if (!txsc) + return -ENOENT; + + if (sa_num >= CN10K_MCS_SA_PER_SC) + return -EOPNOTSUPP; + + if (cn10k_mcs_alloc_txsa(pfvf, &txsc->hw_sa_id[sa_num])) + return -ENOSPC; + + memcpy(&txsc->sa_key[sa_num], ctx->sa.key, secy->key_len); + txsc->sa_bmap |= 1 << sa_num; + + if (netif_running(secy->netdev)) { + err = cn10k_mcs_write_tx_sa_plcy(pfvf, secy, txsc, sa_num); + if (err) + return err; + + err = cn10k_write_tx_sa_pn(pfvf, txsc, sa_num, + sw_tx_sa->next_pn_halves.lower); + if (err) + return err; + + err = cn10k_mcs_link_tx_sa2sc(pfvf, secy, txsc, + sa_num, sw_tx_sa->active); + if (err) + return err; + } + + return 0; +} + +static int cn10k_mdo_upd_txsa(struct macsec_context *ctx) +{ + struct otx2_nic *pfvf = netdev_priv(ctx->netdev); + struct macsec_tx_sa *sw_tx_sa = ctx->sa.tx_sa; + struct cn10k_mcs_cfg *cfg = pfvf->macsec_cfg; + struct macsec_secy *secy = ctx->secy; + u8 sa_num = ctx->sa.assoc_num; + struct cn10k_mcs_txsc *txsc; + int err; + + txsc = cn10k_mcs_get_txsc(cfg, secy); + if (!txsc) + return -ENOENT; + + if (sa_num >= CN10K_MCS_SA_PER_SC) + return -EOPNOTSUPP; + + if (netif_running(secy->netdev)) { + /* Keys cannot be changed after creation */ + err = cn10k_write_tx_sa_pn(pfvf, txsc, sa_num, + sw_tx_sa->next_pn_halves.lower); + if (err) + return err; + + err = cn10k_mcs_link_tx_sa2sc(pfvf, secy, txsc, + sa_num, sw_tx_sa->active); + if (err) + return err; + } + + return 0; +} + +static int cn10k_mdo_del_txsa(struct macsec_context *ctx) +{ + struct otx2_nic *pfvf = netdev_priv(ctx->netdev); + struct cn10k_mcs_cfg *cfg = pfvf->macsec_cfg; + u8 sa_num = ctx->sa.assoc_num; + struct cn10k_mcs_txsc *txsc; + + txsc = cn10k_mcs_get_txsc(cfg, ctx->secy); + if (!txsc) + return -ENOENT; + + if (sa_num >= CN10K_MCS_SA_PER_SC) + return -EOPNOTSUPP; + + cn10k_mcs_free_txsa(pfvf, txsc->hw_sa_id[sa_num]); + txsc->sa_bmap &= ~(1 << sa_num); + + return 0; +} + +static int cn10k_mdo_add_rxsc(struct macsec_context *ctx) +{ + struct otx2_nic *pfvf = netdev_priv(ctx->netdev); + struct cn10k_mcs_cfg *cfg = pfvf->macsec_cfg; + struct macsec_secy *secy = ctx->secy; + struct cn10k_mcs_rxsc *rxsc; + struct cn10k_mcs_txsc *txsc; + int err; + + txsc = cn10k_mcs_get_txsc(cfg, secy); + if (!txsc) + return -ENOENT; + + rxsc = cn10k_mcs_create_rxsc(pfvf); + if (IS_ERR(rxsc)) + return -ENOSPC; + + rxsc->sw_secy = ctx->secy; + rxsc->sw_rxsc = ctx->rx_sc; + list_add(&rxsc->entry, &cfg->rxsc_list); + + if (netif_running(secy->netdev)) { + err = cn10k_mcs_write_rx_flowid(pfvf, rxsc, txsc->hw_secy_id_rx); + if (err) + return err; + + err = cn10k_mcs_write_sc_cam(pfvf, rxsc, txsc->hw_secy_id_rx); + if (err) + return err; + } + + return 0; +} + +static int cn10k_mdo_upd_rxsc(struct macsec_context *ctx) +{ + struct otx2_nic *pfvf = netdev_priv(ctx->netdev); + struct cn10k_mcs_cfg *cfg = pfvf->macsec_cfg; + struct macsec_secy *secy = ctx->secy; + bool enable = ctx->rx_sc->active; + struct cn10k_mcs_rxsc *rxsc; + + rxsc = cn10k_mcs_get_rxsc(cfg, secy, ctx->rx_sc); + if (!rxsc) + return -ENOENT; + + if (netif_running(secy->netdev)) + return cn10k_mcs_ena_dis_flowid(pfvf, rxsc->hw_flow_id, + enable, MCS_RX); + + return 0; +} + +static int cn10k_mdo_del_rxsc(struct macsec_context *ctx) +{ + struct otx2_nic *pfvf = netdev_priv(ctx->netdev); + struct cn10k_mcs_cfg *cfg = pfvf->macsec_cfg; + struct cn10k_mcs_rxsc *rxsc; + + rxsc = cn10k_mcs_get_rxsc(cfg, ctx->secy, ctx->rx_sc); + if (!rxsc) + return -ENOENT; + + cn10k_mcs_ena_dis_flowid(pfvf, rxsc->hw_flow_id, false, MCS_RX); + cn10k_mcs_delete_rxsc(pfvf, rxsc); + list_del(&rxsc->entry); + kfree(rxsc); + + return 0; +} + +static int cn10k_mdo_add_rxsa(struct macsec_context *ctx) +{ + struct macsec_rx_sc *sw_rx_sc = ctx->sa.rx_sa->sc; + struct otx2_nic *pfvf = netdev_priv(ctx->netdev); + struct cn10k_mcs_cfg *cfg = pfvf->macsec_cfg; + struct macsec_rx_sa *rx_sa = ctx->sa.rx_sa; + u64 next_pn = rx_sa->next_pn_halves.lower; + struct macsec_secy *secy = ctx->secy; + bool sa_in_use = rx_sa->active; + u8 sa_num = ctx->sa.assoc_num; + struct cn10k_mcs_rxsc *rxsc; + int err; + + rxsc = cn10k_mcs_get_rxsc(cfg, secy, sw_rx_sc); + if (!rxsc) + return -ENOENT; + + if (sa_num >= CN10K_MCS_SA_PER_SC) + return -EOPNOTSUPP; + + if (cn10k_mcs_alloc_rxsa(pfvf, &rxsc->hw_sa_id[sa_num])) + return -ENOSPC; + + memcpy(&rxsc->sa_key[sa_num], ctx->sa.key, ctx->secy->key_len); + rxsc->sa_bmap |= 1 << sa_num; + + if (netif_running(secy->netdev)) { + err = cn10k_mcs_write_rx_sa_plcy(pfvf, secy, rxsc, + sa_num, sa_in_use); + if (err) + return err; + + err = cn10k_mcs_write_rx_sa_pn(pfvf, rxsc, sa_num, next_pn); + if (err) + return err; + } + + return 0; +} + +static int cn10k_mdo_upd_rxsa(struct macsec_context *ctx) +{ + struct macsec_rx_sc *sw_rx_sc = ctx->sa.rx_sa->sc; + struct otx2_nic *pfvf = netdev_priv(ctx->netdev); + struct cn10k_mcs_cfg *cfg = pfvf->macsec_cfg; + struct macsec_rx_sa *rx_sa = ctx->sa.rx_sa; + u64 next_pn = rx_sa->next_pn_halves.lower; + struct macsec_secy *secy = ctx->secy; + bool sa_in_use = rx_sa->active; + u8 sa_num = ctx->sa.assoc_num; + struct cn10k_mcs_rxsc *rxsc; + int err; + + rxsc = cn10k_mcs_get_rxsc(cfg, secy, sw_rx_sc); + if (!rxsc) + return -ENOENT; + + if (sa_num >= CN10K_MCS_SA_PER_SC) + return -EOPNOTSUPP; + + if (netif_running(secy->netdev)) { + err = cn10k_mcs_write_rx_sa_plcy(pfvf, secy, rxsc, sa_num, sa_in_use); + if (err) + return err; + + err = cn10k_mcs_write_rx_sa_pn(pfvf, rxsc, sa_num, next_pn); + if (err) + return err; + } + + return 0; +} + +static int cn10k_mdo_del_rxsa(struct macsec_context *ctx) +{ + struct macsec_rx_sc *sw_rx_sc = ctx->sa.rx_sa->sc; + struct otx2_nic *pfvf = netdev_priv(ctx->netdev); + struct cn10k_mcs_cfg *cfg = pfvf->macsec_cfg; + u8 sa_num = ctx->sa.assoc_num; + struct cn10k_mcs_rxsc *rxsc; + + rxsc = cn10k_mcs_get_rxsc(cfg, ctx->secy, sw_rx_sc); + if (!rxsc) + return -ENOENT; + + if (sa_num >= CN10K_MCS_SA_PER_SC) + return -EOPNOTSUPP; + + cn10k_mcs_write_rx_sa_plcy(pfvf, ctx->secy, rxsc, sa_num, false); + cn10k_mcs_free_rxsa(pfvf, rxsc->hw_sa_id[sa_num]); + + rxsc->sa_bmap &= ~(1 << sa_num); + + return 0; +} + +static int cn10k_mdo_get_dev_stats(struct macsec_context *ctx) +{ + struct mcs_secy_stats tx_rsp = { 0 }, rx_rsp = { 0 }; + struct otx2_nic *pfvf = netdev_priv(ctx->netdev); + struct cn10k_mcs_cfg *cfg = pfvf->macsec_cfg; + struct macsec_secy *secy = ctx->secy; + struct cn10k_mcs_txsc *txsc; + + txsc = cn10k_mcs_get_txsc(cfg, ctx->secy); + if (!txsc) + return -ENOENT; + + cn10k_mcs_secy_stats(pfvf, txsc->hw_secy_id_tx, &tx_rsp, MCS_TX, false); + ctx->stats.dev_stats->OutPktsUntagged = tx_rsp.pkt_untagged_cnt; + ctx->stats.dev_stats->OutPktsTooLong = tx_rsp.pkt_toolong_cnt; + + cn10k_mcs_secy_stats(pfvf, txsc->hw_secy_id_rx, &rx_rsp, MCS_RX, true); + txsc->stats.InPktsBadTag += rx_rsp.pkt_badtag_cnt; + txsc->stats.InPktsUnknownSCI += rx_rsp.pkt_nosa_cnt; + txsc->stats.InPktsNoSCI += rx_rsp.pkt_nosaerror_cnt; + if (secy->validate_frames == MACSEC_VALIDATE_STRICT) + txsc->stats.InPktsNoTag += rx_rsp.pkt_untaged_cnt; + else + txsc->stats.InPktsUntagged += rx_rsp.pkt_untaged_cnt; + txsc->stats.InPktsOverrun = 0; + + ctx->stats.dev_stats->InPktsNoTag = txsc->stats.InPktsNoTag; + ctx->stats.dev_stats->InPktsUntagged = txsc->stats.InPktsUntagged; + ctx->stats.dev_stats->InPktsBadTag = txsc->stats.InPktsBadTag; + ctx->stats.dev_stats->InPktsUnknownSCI = txsc->stats.InPktsUnknownSCI; + ctx->stats.dev_stats->InPktsNoSCI = txsc->stats.InPktsNoSCI; + ctx->stats.dev_stats->InPktsOverrun = txsc->stats.InPktsOverrun; + + return 0; +} + +static int cn10k_mdo_get_tx_sc_stats(struct macsec_context *ctx) +{ + struct otx2_nic *pfvf = netdev_priv(ctx->netdev); + struct cn10k_mcs_cfg *cfg = pfvf->macsec_cfg; + struct mcs_sc_stats rsp = { 0 }; + struct cn10k_mcs_txsc *txsc; + + txsc = cn10k_mcs_get_txsc(cfg, ctx->secy); + if (!txsc) + return -ENOENT; + + cn10k_mcs_sc_stats(pfvf, txsc->hw_sc_id, &rsp, MCS_TX, false); + + ctx->stats.tx_sc_stats->OutPktsProtected = rsp.pkt_protected_cnt; + ctx->stats.tx_sc_stats->OutPktsEncrypted = rsp.pkt_encrypt_cnt; + ctx->stats.tx_sc_stats->OutOctetsProtected = rsp.octet_protected_cnt; + ctx->stats.tx_sc_stats->OutOctetsEncrypted = rsp.octet_encrypt_cnt; + + return 0; +} + +static int cn10k_mdo_get_tx_sa_stats(struct macsec_context *ctx) +{ + struct otx2_nic *pfvf = netdev_priv(ctx->netdev); + struct cn10k_mcs_cfg *cfg = pfvf->macsec_cfg; + struct mcs_sa_stats rsp = { 0 }; + u8 sa_num = ctx->sa.assoc_num; + struct cn10k_mcs_txsc *txsc; + + txsc = cn10k_mcs_get_txsc(cfg, ctx->secy); + if (!txsc) + return -ENOENT; + + if (sa_num >= CN10K_MCS_SA_PER_SC) + return -EOPNOTSUPP; + + cn10k_mcs_sa_stats(pfvf, txsc->hw_sa_id[sa_num], &rsp, MCS_TX, false); + + ctx->stats.tx_sa_stats->OutPktsProtected = rsp.pkt_protected_cnt; + ctx->stats.tx_sa_stats->OutPktsEncrypted = rsp.pkt_encrypt_cnt; + + return 0; +} + +static int cn10k_mdo_get_rx_sc_stats(struct macsec_context *ctx) +{ + struct otx2_nic *pfvf = netdev_priv(ctx->netdev); + struct cn10k_mcs_cfg *cfg = pfvf->macsec_cfg; + struct macsec_secy *secy = ctx->secy; + struct mcs_sc_stats rsp = { 0 }; + struct cn10k_mcs_rxsc *rxsc; + + rxsc = cn10k_mcs_get_rxsc(cfg, secy, ctx->rx_sc); + if (!rxsc) + return -ENOENT; + + cn10k_mcs_sc_stats(pfvf, rxsc->hw_sc_id, &rsp, MCS_RX, true); + + rxsc->stats.InOctetsValidated += rsp.octet_validate_cnt; + rxsc->stats.InOctetsDecrypted += rsp.octet_decrypt_cnt; + + rxsc->stats.InPktsInvalid += rsp.pkt_invalid_cnt; + rxsc->stats.InPktsNotValid += rsp.pkt_notvalid_cnt; + + if (secy->protect_frames) + rxsc->stats.InPktsLate += rsp.pkt_late_cnt; + else + rxsc->stats.InPktsDelayed += rsp.pkt_late_cnt; + + if (secy->validate_frames == MACSEC_VALIDATE_CHECK) + rxsc->stats.InPktsUnchecked += rsp.pkt_unchecked_cnt; + else + rxsc->stats.InPktsOK += rsp.pkt_unchecked_cnt; + + ctx->stats.rx_sc_stats->InOctetsValidated = rxsc->stats.InOctetsValidated; + ctx->stats.rx_sc_stats->InOctetsDecrypted = rxsc->stats.InOctetsDecrypted; + ctx->stats.rx_sc_stats->InPktsInvalid = rxsc->stats.InPktsInvalid; + ctx->stats.rx_sc_stats->InPktsNotValid = rxsc->stats.InPktsNotValid; + ctx->stats.rx_sc_stats->InPktsLate = rxsc->stats.InPktsLate; + ctx->stats.rx_sc_stats->InPktsDelayed = rxsc->stats.InPktsDelayed; + ctx->stats.rx_sc_stats->InPktsUnchecked = rxsc->stats.InPktsUnchecked; + ctx->stats.rx_sc_stats->InPktsOK = rxsc->stats.InPktsOK; + + return 0; +} + +static int cn10k_mdo_get_rx_sa_stats(struct macsec_context *ctx) +{ + struct macsec_rx_sc *sw_rx_sc = ctx->sa.rx_sa->sc; + struct otx2_nic *pfvf = netdev_priv(ctx->netdev); + struct cn10k_mcs_cfg *cfg = pfvf->macsec_cfg; + struct mcs_sa_stats rsp = { 0 }; + u8 sa_num = ctx->sa.assoc_num; + struct cn10k_mcs_rxsc *rxsc; + + rxsc = cn10k_mcs_get_rxsc(cfg, ctx->secy, sw_rx_sc); + if (!rxsc) + return -ENOENT; + + if (sa_num >= CN10K_MCS_SA_PER_SC) + return -EOPNOTSUPP; + + cn10k_mcs_sa_stats(pfvf, rxsc->hw_sa_id[sa_num], &rsp, MCS_RX, false); + + ctx->stats.rx_sa_stats->InPktsOK = rsp.pkt_ok_cnt; + ctx->stats.rx_sa_stats->InPktsInvalid = rsp.pkt_invalid_cnt; + ctx->stats.rx_sa_stats->InPktsNotValid = rsp.pkt_notvalid_cnt; + ctx->stats.rx_sa_stats->InPktsNotUsingSA = rsp.pkt_nosaerror_cnt; + ctx->stats.rx_sa_stats->InPktsUnusedSA = rsp.pkt_nosa_cnt; + + return 0; +} + +static const struct macsec_ops cn10k_mcs_ops = { + .mdo_dev_open = cn10k_mdo_open, + .mdo_dev_stop = cn10k_mdo_stop, + .mdo_add_secy = cn10k_mdo_add_secy, + .mdo_upd_secy = cn10k_mdo_upd_secy, + .mdo_del_secy = cn10k_mdo_del_secy, + .mdo_add_rxsc = cn10k_mdo_add_rxsc, + .mdo_upd_rxsc = cn10k_mdo_upd_rxsc, + .mdo_del_rxsc = cn10k_mdo_del_rxsc, + .mdo_add_rxsa = cn10k_mdo_add_rxsa, + .mdo_upd_rxsa = cn10k_mdo_upd_rxsa, + .mdo_del_rxsa = cn10k_mdo_del_rxsa, + .mdo_add_txsa = cn10k_mdo_add_txsa, + .mdo_upd_txsa = cn10k_mdo_upd_txsa, + .mdo_del_txsa = cn10k_mdo_del_txsa, + .mdo_get_dev_stats = cn10k_mdo_get_dev_stats, + .mdo_get_tx_sc_stats = cn10k_mdo_get_tx_sc_stats, + .mdo_get_tx_sa_stats = cn10k_mdo_get_tx_sa_stats, + .mdo_get_rx_sc_stats = cn10k_mdo_get_rx_sc_stats, + .mdo_get_rx_sa_stats = cn10k_mdo_get_rx_sa_stats, +}; + +void cn10k_handle_mcs_event(struct otx2_nic *pfvf, struct mcs_intr_info *event) +{ + struct cn10k_mcs_cfg *cfg = pfvf->macsec_cfg; + struct macsec_tx_sa *sw_tx_sa = NULL; + struct macsec_secy *secy = NULL; + struct cn10k_mcs_txsc *txsc; + u8 an; + + if (!test_bit(CN10K_HW_MACSEC, &pfvf->hw.cap_flag)) + return; + + if (!(event->intr_mask & MCS_CPM_TX_PACKET_XPN_EQ0_INT)) + return; + + /* Find the SecY to which the expired hardware SA is mapped */ + list_for_each_entry(txsc, &cfg->txsc_list, entry) { + for (an = 0; an < CN10K_MCS_SA_PER_SC; an++) + if (txsc->hw_sa_id[an] == event->sa_id) { + secy = txsc->sw_secy; + sw_tx_sa = rcu_dereference_bh(secy->tx_sc.sa[an]); + } + } + + if (secy && sw_tx_sa) + macsec_pn_wrapped(secy, sw_tx_sa); +} + +int cn10k_mcs_init(struct otx2_nic *pfvf) +{ + struct mbox *mbox = &pfvf->mbox; + struct cn10k_mcs_cfg *cfg; + struct mcs_intr_cfg *req; + + if (!test_bit(CN10K_HW_MACSEC, &pfvf->hw.cap_flag)) + return 0; + + cfg = kzalloc(sizeof(*cfg), GFP_KERNEL); + if (!cfg) + return -ENOMEM; + + INIT_LIST_HEAD(&cfg->txsc_list); + INIT_LIST_HEAD(&cfg->rxsc_list); + pfvf->macsec_cfg = cfg; + + pfvf->netdev->features |= NETIF_F_HW_MACSEC; + pfvf->netdev->macsec_ops = &cn10k_mcs_ops; + + mutex_lock(&mbox->lock); + + req = otx2_mbox_alloc_msg_mcs_intr_cfg(mbox); + if (!req) + goto fail; + + req->intr_mask = MCS_CPM_TX_PACKET_XPN_EQ0_INT; + + if (otx2_sync_mbox_msg(mbox)) + goto fail; + + mutex_unlock(&mbox->lock); + + return 0; +fail: + dev_err(pfvf->dev, "Cannot notify PN wrapped event\n"); + return 0; +} + +void cn10k_mcs_free(struct otx2_nic *pfvf) +{ + if (!test_bit(CN10K_HW_MACSEC, &pfvf->hw.cap_flag)) + return; + + cn10k_mcs_free_rsrc(pfvf, MCS_TX, MCS_RSRC_TYPE_SECY, 0, true); + cn10k_mcs_free_rsrc(pfvf, MCS_RX, MCS_RSRC_TYPE_SECY, 0, true); + kfree(pfvf->macsec_cfg); + pfvf->macsec_cfg = NULL; +} diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c index d686c7b6252f..9ac9e6615ae7 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c @@ -586,8 +586,9 @@ void otx2_get_mac_from_af(struct net_device *netdev) } EXPORT_SYMBOL(otx2_get_mac_from_af); -int otx2_txschq_config(struct otx2_nic *pfvf, int lvl) +int otx2_txschq_config(struct otx2_nic *pfvf, int lvl, int prio, bool txschq_for_pfc) { + u16 (*schq_list)[MAX_TXSCHQ_PER_FUNC]; struct otx2_hw *hw = &pfvf->hw; struct nix_txschq_config *req; u64 schq, parent; @@ -602,7 +603,13 @@ int otx2_txschq_config(struct otx2_nic *pfvf, int lvl) req->lvl = lvl; req->num_regs = 1; - schq = hw->txschq_list[lvl][0]; + schq_list = hw->txschq_list; +#ifdef CONFIG_DCB + if (txschq_for_pfc) + schq_list = pfvf->pfc_schq_list; +#endif + + schq = schq_list[lvl][prio]; /* Set topology e.t.c configuration */ if (lvl == NIX_TXSCH_LVL_SMQ) { req->reg[0] = NIX_AF_SMQX_CFG(schq); @@ -611,7 +618,7 @@ int otx2_txschq_config(struct otx2_nic *pfvf, int lvl) (0x2ULL << 36); req->num_regs++; /* MDQ config */ - parent = hw->txschq_list[NIX_TXSCH_LVL_TL4][0]; + parent = schq_list[NIX_TXSCH_LVL_TL4][prio]; req->reg[1] = NIX_AF_MDQX_PARENT(schq); req->regval[1] = parent << 16; req->num_regs++; @@ -619,14 +626,14 @@ int otx2_txschq_config(struct otx2_nic *pfvf, int lvl) req->reg[2] = NIX_AF_MDQX_SCHEDULE(schq); req->regval[2] = dwrr_val; } else if (lvl == NIX_TXSCH_LVL_TL4) { - parent = hw->txschq_list[NIX_TXSCH_LVL_TL3][0]; + parent = schq_list[NIX_TXSCH_LVL_TL3][prio]; req->reg[0] = NIX_AF_TL4X_PARENT(schq); req->regval[0] = parent << 16; req->num_regs++; req->reg[1] = NIX_AF_TL4X_SCHEDULE(schq); req->regval[1] = dwrr_val; } else if (lvl == NIX_TXSCH_LVL_TL3) { - parent = hw->txschq_list[NIX_TXSCH_LVL_TL2][0]; + parent = schq_list[NIX_TXSCH_LVL_TL2][prio]; req->reg[0] = NIX_AF_TL3X_PARENT(schq); req->regval[0] = parent << 16; req->num_regs++; @@ -635,11 +642,13 @@ int otx2_txschq_config(struct otx2_nic *pfvf, int lvl) if (lvl == hw->txschq_link_cfg_lvl) { req->num_regs++; req->reg[2] = NIX_AF_TL3_TL2X_LINKX_CFG(schq, hw->tx_link); - /* Enable this queue and backpressure */ - req->regval[2] = BIT_ULL(13) | BIT_ULL(12); + /* Enable this queue and backpressure + * and set relative channel + */ + req->regval[2] = BIT_ULL(13) | BIT_ULL(12) | prio; } } else if (lvl == NIX_TXSCH_LVL_TL2) { - parent = hw->txschq_list[NIX_TXSCH_LVL_TL1][0]; + parent = schq_list[NIX_TXSCH_LVL_TL1][prio]; req->reg[0] = NIX_AF_TL2X_PARENT(schq); req->regval[0] = parent << 16; @@ -650,8 +659,10 @@ int otx2_txschq_config(struct otx2_nic *pfvf, int lvl) if (lvl == hw->txschq_link_cfg_lvl) { req->num_regs++; req->reg[2] = NIX_AF_TL3_TL2X_LINKX_CFG(schq, hw->tx_link); - /* Enable this queue and backpressure */ - req->regval[2] = BIT_ULL(13) | BIT_ULL(12); + /* Enable this queue and backpressure + * and set relative channel + */ + req->regval[2] = BIT_ULL(13) | BIT_ULL(12) | prio; } } else if (lvl == NIX_TXSCH_LVL_TL1) { /* Default config for TL1. @@ -676,6 +687,31 @@ int otx2_txschq_config(struct otx2_nic *pfvf, int lvl) return otx2_sync_mbox_msg(&pfvf->mbox); } +EXPORT_SYMBOL(otx2_txschq_config); + +int otx2_smq_flush(struct otx2_nic *pfvf, int smq) +{ + struct nix_txschq_config *req; + int rc; + + mutex_lock(&pfvf->mbox.lock); + + req = otx2_mbox_alloc_msg_nix_txschq_cfg(&pfvf->mbox); + if (!req) { + mutex_unlock(&pfvf->mbox.lock); + return -ENOMEM; + } + + req->lvl = NIX_TXSCH_LVL_SMQ; + req->reg[0] = NIX_AF_SMQX_CFG(smq); + req->regval[0] |= BIT_ULL(49); + req->num_regs++; + + rc = otx2_sync_mbox_msg(&pfvf->mbox); + mutex_unlock(&pfvf->mbox.lock); + return rc; +} +EXPORT_SYMBOL(otx2_smq_flush); int otx2_txsch_alloc(struct otx2_nic *pfvf) { @@ -806,8 +842,7 @@ int otx2_sq_aq_init(void *dev, u16 qidx, u16 sqb_aura) aq->sq.max_sqe_size = NIX_MAXSQESZ_W16; /* 128 byte */ aq->sq.cq_ena = 1; aq->sq.ena = 1; - /* Only one SMQ is allocated, map all SQ's to that SMQ */ - aq->sq.smq = pfvf->hw.txschq_list[NIX_TXSCH_LVL_SMQ][0]; + aq->sq.smq = otx2_get_smq_idx(pfvf, qidx); aq->sq.smq_rr_quantum = mtu_to_dwrr_weight(pfvf, pfvf->tx_max_pktlen); aq->sq.default_chan = pfvf->hw.tx_chan_base; aq->sq.sqe_stype = NIX_STYPE_STF; /* Cache SQB */ @@ -1792,4 +1827,5 @@ otx2_mbox_up_handler_ ## _fn_name(struct otx2_nic *pfvf, \ } \ EXPORT_SYMBOL(otx2_mbox_up_handler_ ## _fn_name); MBOX_UP_CGX_MESSAGES +MBOX_UP_MCS_MESSAGES #undef M diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h index b28029cc4316..282db6fe3b08 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h @@ -19,6 +19,7 @@ #include <net/devlink.h> #include <linux/time64.h> #include <linux/dim.h> +#include <uapi/linux/if_macsec.h> #include <mbox.h> #include <npc.h> @@ -33,6 +34,7 @@ #define PCI_DEVID_OCTEONTX2_RVU_AFVF 0xA0F8 #define PCI_SUBSYS_DEVID_96XX_RVU_PFVF 0xB200 +#define PCI_SUBSYS_DEVID_CN10K_B_RVU_PFVF 0xBD00 /* PCI BAR nos */ #define PCI_CFG_REG_BAR_NUM 2 @@ -40,6 +42,11 @@ #define NAME_SIZE 32 +#ifdef CONFIG_DCB +/* Max priority supported for PFC */ +#define NIX_PF_PFC_PRIO_MAX 8 +#endif + enum arua_mapped_qtypes { AURA_NIX_RQ, AURA_NIX_SQ, @@ -196,7 +203,7 @@ struct otx2_hw { /* NIX */ u8 txschq_link_cfg_lvl; - u16 txschq_list[NIX_TXSCH_LVL_CNT][MAX_TXSCHQ_PER_FUNC]; + u16 txschq_list[NIX_TXSCH_LVL_CNT][MAX_TXSCHQ_PER_FUNC]; u16 matchall_ipolicer; u32 dwrr_mtu; @@ -238,6 +245,8 @@ struct otx2_hw { #define CN10K_MBOX 1 #define CN10K_LMTST 2 #define CN10K_RPM 3 +#define CN10K_PTP_ONESTEP 4 +#define CN10K_HW_MACSEC 5 unsigned long cap_flag; #define LMT_LINE_SIZE 128 @@ -271,6 +280,13 @@ struct refill_work { struct otx2_nic *pf; }; +/* PTPv2 originTimestamp structure */ +struct ptpv2_tstamp { + __be16 seconds_msb; /* 16 bits + */ + __be32 seconds_lsb; /* 32 bits = 48 bits*/ + __be32 nanoseconds; +} __packed; + struct otx2_ptp { struct ptp_clock_info ptp_info; struct ptp_clock *ptp_clock; @@ -286,6 +302,9 @@ struct otx2_ptp { struct ptp_pin_desc extts_config; u64 (*convert_rx_ptp_tstmp)(u64 timestamp); u64 (*convert_tx_ptp_tstmp)(u64 timestamp); + struct delayed_work synctstamp_work; + u64 tstamp; + u32 base_ns; }; #define OTX2_HW_TIMESTAMP_LEN 8 @@ -335,6 +354,66 @@ struct dev_hw_ops { void (*aura_freeptr)(void *dev, int aura, u64 buf); }; +#define CN10K_MCS_SA_PER_SC 4 + +/* Stats which need to be accumulated in software because + * of shared counters in hardware. + */ +struct cn10k_txsc_stats { + u64 InPktsUntagged; + u64 InPktsNoTag; + u64 InPktsBadTag; + u64 InPktsUnknownSCI; + u64 InPktsNoSCI; + u64 InPktsOverrun; +}; + +struct cn10k_rxsc_stats { + u64 InOctetsValidated; + u64 InOctetsDecrypted; + u64 InPktsUnchecked; + u64 InPktsDelayed; + u64 InPktsOK; + u64 InPktsInvalid; + u64 InPktsLate; + u64 InPktsNotValid; + u64 InPktsNotUsingSA; + u64 InPktsUnusedSA; +}; + +struct cn10k_mcs_txsc { + struct macsec_secy *sw_secy; + struct cn10k_txsc_stats stats; + struct list_head entry; + enum macsec_validation_type last_validate_frames; + bool last_protect_frames; + u16 hw_secy_id_tx; + u16 hw_secy_id_rx; + u16 hw_flow_id; + u16 hw_sc_id; + u16 hw_sa_id[CN10K_MCS_SA_PER_SC]; + u8 sa_bmap; + u8 sa_key[CN10K_MCS_SA_PER_SC][MACSEC_MAX_KEY_LEN]; + u8 encoding_sa; +}; + +struct cn10k_mcs_rxsc { + struct macsec_secy *sw_secy; + struct macsec_rx_sc *sw_rxsc; + struct cn10k_rxsc_stats stats; + struct list_head entry; + u16 hw_flow_id; + u16 hw_sc_id; + u16 hw_sa_id[CN10K_MCS_SA_PER_SC]; + u8 sa_bmap; + u8 sa_key[CN10K_MCS_SA_PER_SC][MACSEC_MAX_KEY_LEN]; +}; + +struct cn10k_mcs_cfg { + struct list_head txsc_list; + struct list_head rxsc_list; +}; + struct otx2_nic { void __iomem *reg_base; struct net_device *netdev; @@ -358,6 +437,7 @@ struct otx2_nic { #define OTX2_FLAG_TC_MATCHALL_EGRESS_ENABLED BIT_ULL(12) #define OTX2_FLAG_TC_MATCHALL_INGRESS_ENABLED BIT_ULL(13) #define OTX2_FLAG_DMACFLTR_SUPPORT BIT_ULL(14) +#define OTX2_FLAG_PTP_ONESTEP_SYNC BIT_ULL(15) #define OTX2_FLAG_ADPTV_INT_COAL_ENABLED BIT_ULL(16) u64 flags; u64 *cq_op_addr; @@ -415,10 +495,16 @@ struct otx2_nic { /* PFC */ u8 pfc_en; u8 *queue_to_pfc_map; + u16 pfc_schq_list[NIX_TXSCH_LVL_CNT][MAX_TXSCHQ_PER_FUNC]; + bool pfc_alloc_status[NIX_PF_PFC_PRIO_MAX]; #endif /* napi event count. It is needed for adaptive irq coalescing. */ u32 napi_events; + +#if IS_ENABLED(CONFIG_MACSEC) + struct cn10k_mcs_cfg *macsec_cfg; +#endif }; static inline bool is_otx2_lbkvf(struct pci_dev *pdev) @@ -458,6 +544,11 @@ static inline bool is_dev_otx2(struct pci_dev *pdev) midr == PCI_REVISION_ID_95XXMM || midr == PCI_REVISION_ID_95XXO); } +static inline bool is_dev_cn10kb(struct pci_dev *pdev) +{ + return pdev->subsystem_device == PCI_SUBSYS_DEVID_CN10K_B_RVU_PFVF; +} + static inline void otx2_setup_dev_hw_settings(struct otx2_nic *pfvf) { struct otx2_hw *hw = &pfvf->hw; @@ -487,7 +578,11 @@ static inline void otx2_setup_dev_hw_settings(struct otx2_nic *pfvf) __set_bit(CN10K_MBOX, &hw->cap_flag); __set_bit(CN10K_LMTST, &hw->cap_flag); __set_bit(CN10K_RPM, &hw->cap_flag); + __set_bit(CN10K_PTP_ONESTEP, &hw->cap_flag); } + + if (is_dev_cn10kb(pfvf->pdev)) + __set_bit(CN10K_HW_MACSEC, &hw->cap_flag); } /* Register read/write APIs */ @@ -743,6 +838,7 @@ otx2_mbox_up_handler_ ## _fn_name(struct otx2_nic *pfvf, \ struct _rsp_type *rsp); \ MBOX_UP_CGX_MESSAGES +MBOX_UP_MCS_MESSAGES #undef M /* Time to wait before watchdog kicks off */ @@ -785,6 +881,16 @@ static inline void otx2_dma_unmap_page(struct otx2_nic *pfvf, dir, DMA_ATTR_SKIP_CPU_SYNC); } +static inline u16 otx2_get_smq_idx(struct otx2_nic *pfvf, u16 qidx) +{ +#ifdef CONFIG_DCB + if (pfvf->pfc_alloc_status[qidx]) + return pfvf->pfc_schq_list[NIX_TXSCH_LVL_SMQ][qidx]; +#endif + + return pfvf->hw.txschq_list[NIX_TXSCH_LVL_SMQ][0]; +} + /* MSI-X APIs */ void otx2_free_cints(struct otx2_nic *pfvf, int n); void otx2_set_cints_affinity(struct otx2_nic *pfvf); @@ -807,7 +913,7 @@ void otx2_free_aura_ptr(struct otx2_nic *pfvf, int type); void otx2_sq_free_sqbs(struct otx2_nic *pfvf); int otx2_config_nix(struct otx2_nic *pfvf); int otx2_config_nix_queues(struct otx2_nic *pfvf); -int otx2_txschq_config(struct otx2_nic *pfvf, int lvl); +int otx2_txschq_config(struct otx2_nic *pfvf, int lvl, int prio, bool pfc_en); int otx2_txsch_alloc(struct otx2_nic *pfvf); int otx2_txschq_stop(struct otx2_nic *pfvf); void otx2_sqb_flush(struct otx2_nic *pfvf); @@ -888,6 +994,8 @@ bool otx2_xdp_sq_append_pkt(struct otx2_nic *pfvf, u64 iova, int len, u16 qidx); u16 otx2_get_max_mtu(struct otx2_nic *pfvf); int otx2_handle_ntuple_tc_features(struct net_device *netdev, netdev_features_t features); +int otx2_smq_flush(struct otx2_nic *pfvf, int smq); + /* tc support */ int otx2_init_tc(struct otx2_nic *nic); void otx2_shutdown_tc(struct otx2_nic *nic); @@ -907,5 +1015,24 @@ void otx2_dmacflt_update_pfmac_flow(struct otx2_nic *pfvf); void otx2_update_bpid_in_rqctx(struct otx2_nic *pfvf, int vlan_prio, int qidx, bool pfc_enable); int otx2_config_priority_flow_ctrl(struct otx2_nic *pfvf); int otx2_dcbnl_set_ops(struct net_device *dev); +/* PFC support */ +int otx2_pfc_txschq_config(struct otx2_nic *pfvf); +int otx2_pfc_txschq_alloc(struct otx2_nic *pfvf); +int otx2_pfc_txschq_update(struct otx2_nic *pfvf); +int otx2_pfc_txschq_stop(struct otx2_nic *pfvf); #endif + +#if IS_ENABLED(CONFIG_MACSEC) +/* MACSEC offload support */ +int cn10k_mcs_init(struct otx2_nic *pfvf); +void cn10k_mcs_free(struct otx2_nic *pfvf); +void cn10k_handle_mcs_event(struct otx2_nic *pfvf, struct mcs_intr_info *event); +#else +static inline int cn10k_mcs_init(struct otx2_nic *pfvf) { return 0; } +static inline void cn10k_mcs_free(struct otx2_nic *pfvf) {} +static inline void cn10k_handle_mcs_event(struct otx2_nic *pfvf, + struct mcs_intr_info *event) +{} +#endif /* CONFIG_MACSEC */ + #endif /* OTX2_COMMON_H */ diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_dcbnl.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_dcbnl.c index 723d2506d309..ccaf97bb1ce0 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_dcbnl.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_dcbnl.c @@ -7,6 +7,289 @@ #include "otx2_common.h" +static int otx2_check_pfc_config(struct otx2_nic *pfvf) +{ + u8 tx_queues = pfvf->hw.tx_queues, prio; + u8 pfc_en = pfvf->pfc_en; + + for (prio = 0; prio < NIX_PF_PFC_PRIO_MAX; prio++) { + if ((pfc_en & (1 << prio)) && + prio > tx_queues - 1) { + dev_warn(pfvf->dev, + "Increase number of tx queues from %d to %d to support PFC.\n", + tx_queues, prio + 1); + return -EINVAL; + } + } + + return 0; +} + +int otx2_pfc_txschq_config(struct otx2_nic *pfvf) +{ + u8 pfc_en, pfc_bit_set; + int prio, lvl, err; + + pfc_en = pfvf->pfc_en; + for (prio = 0; prio < NIX_PF_PFC_PRIO_MAX; prio++) { + pfc_bit_set = pfc_en & (1 << prio); + + /* Either PFC bit is not set + * or tx scheduler is not allocated for the priority + */ + if (!pfc_bit_set || !pfvf->pfc_alloc_status[prio]) + continue; + + /* configure the scheduler for the tls*/ + for (lvl = 0; lvl < NIX_TXSCH_LVL_CNT; lvl++) { + err = otx2_txschq_config(pfvf, lvl, prio, true); + if (err) { + dev_err(pfvf->dev, + "%s configure PFC tx schq for lvl:%d, prio:%d failed!\n", + __func__, lvl, prio); + return err; + } + } + } + + return 0; +} + +static int otx2_pfc_txschq_alloc_one(struct otx2_nic *pfvf, u8 prio) +{ + struct nix_txsch_alloc_req *req; + struct nix_txsch_alloc_rsp *rsp; + int lvl, rc; + + /* Get memory to put this msg */ + req = otx2_mbox_alloc_msg_nix_txsch_alloc(&pfvf->mbox); + if (!req) + return -ENOMEM; + + /* Request one schq per level upto max level as configured + * link config level. These rest of the scheduler can be + * same as hw.txschq_list. + */ + for (lvl = 0; lvl < pfvf->hw.txschq_link_cfg_lvl; lvl++) + req->schq[lvl] = 1; + + rc = otx2_sync_mbox_msg(&pfvf->mbox); + if (rc) + return rc; + + rsp = (struct nix_txsch_alloc_rsp *) + otx2_mbox_get_rsp(&pfvf->mbox.mbox, 0, &req->hdr); + if (IS_ERR(rsp)) + return PTR_ERR(rsp); + + /* Setup transmit scheduler list */ + for (lvl = 0; lvl < pfvf->hw.txschq_link_cfg_lvl; lvl++) { + if (!rsp->schq[lvl]) + return -ENOSPC; + + pfvf->pfc_schq_list[lvl][prio] = rsp->schq_list[lvl][0]; + } + + /* Set the Tx schedulers for rest of the levels same as + * hw.txschq_list as those will be common for all. + */ + for (; lvl < NIX_TXSCH_LVL_CNT; lvl++) + pfvf->pfc_schq_list[lvl][prio] = pfvf->hw.txschq_list[lvl][0]; + + pfvf->pfc_alloc_status[prio] = true; + return 0; +} + +int otx2_pfc_txschq_alloc(struct otx2_nic *pfvf) +{ + u8 pfc_en = pfvf->pfc_en; + u8 pfc_bit_set; + int err, prio; + + for (prio = 0; prio < NIX_PF_PFC_PRIO_MAX; prio++) { + pfc_bit_set = pfc_en & (1 << prio); + + if (!pfc_bit_set || pfvf->pfc_alloc_status[prio]) + continue; + + /* Add new scheduler to the priority */ + err = otx2_pfc_txschq_alloc_one(pfvf, prio); + if (err) { + dev_err(pfvf->dev, "%s failed to allocate PFC TX schedulers\n", __func__); + return err; + } + } + + return 0; +} + +static int otx2_pfc_txschq_stop_one(struct otx2_nic *pfvf, u8 prio) +{ + struct nix_txsch_free_req *free_req; + + mutex_lock(&pfvf->mbox.lock); + /* free PFC TLx nodes */ + free_req = otx2_mbox_alloc_msg_nix_txsch_free(&pfvf->mbox); + if (!free_req) { + mutex_unlock(&pfvf->mbox.lock); + return -ENOMEM; + } + + free_req->flags = TXSCHQ_FREE_ALL; + otx2_sync_mbox_msg(&pfvf->mbox); + mutex_unlock(&pfvf->mbox.lock); + + pfvf->pfc_alloc_status[prio] = false; + return 0; +} + +static int otx2_pfc_update_sq_smq_mapping(struct otx2_nic *pfvf, int prio) +{ + struct nix_cn10k_aq_enq_req *cn10k_sq_aq; + struct net_device *dev = pfvf->netdev; + bool if_up = netif_running(dev); + struct nix_aq_enq_req *sq_aq; + + if (if_up) { + if (pfvf->pfc_alloc_status[prio]) + netif_tx_stop_all_queues(pfvf->netdev); + else + netif_tx_stop_queue(netdev_get_tx_queue(dev, prio)); + } + + if (test_bit(CN10K_LMTST, &pfvf->hw.cap_flag)) { + cn10k_sq_aq = otx2_mbox_alloc_msg_nix_cn10k_aq_enq(&pfvf->mbox); + if (!cn10k_sq_aq) + return -ENOMEM; + + /* Fill AQ info */ + cn10k_sq_aq->qidx = prio; + cn10k_sq_aq->ctype = NIX_AQ_CTYPE_SQ; + cn10k_sq_aq->op = NIX_AQ_INSTOP_WRITE; + + /* Fill fields to update */ + cn10k_sq_aq->sq.ena = 1; + cn10k_sq_aq->sq_mask.ena = 1; + cn10k_sq_aq->sq_mask.smq = GENMASK(9, 0); + cn10k_sq_aq->sq.smq = otx2_get_smq_idx(pfvf, prio); + } else { + sq_aq = otx2_mbox_alloc_msg_nix_aq_enq(&pfvf->mbox); + if (!sq_aq) + return -ENOMEM; + + /* Fill AQ info */ + sq_aq->qidx = prio; + sq_aq->ctype = NIX_AQ_CTYPE_SQ; + sq_aq->op = NIX_AQ_INSTOP_WRITE; + + /* Fill fields to update */ + sq_aq->sq.ena = 1; + sq_aq->sq_mask.ena = 1; + sq_aq->sq_mask.smq = GENMASK(8, 0); + sq_aq->sq.smq = otx2_get_smq_idx(pfvf, prio); + } + + otx2_sync_mbox_msg(&pfvf->mbox); + + if (if_up) { + if (pfvf->pfc_alloc_status[prio]) + netif_tx_start_all_queues(pfvf->netdev); + else + netif_tx_start_queue(netdev_get_tx_queue(dev, prio)); + } + + return 0; +} + +int otx2_pfc_txschq_update(struct otx2_nic *pfvf) +{ + bool if_up = netif_running(pfvf->netdev); + u8 pfc_en = pfvf->pfc_en, pfc_bit_set; + struct mbox *mbox = &pfvf->mbox; + int err, prio; + + mutex_lock(&mbox->lock); + for (prio = 0; prio < NIX_PF_PFC_PRIO_MAX; prio++) { + pfc_bit_set = pfc_en & (1 << prio); + + /* tx scheduler was created but user wants to disable now */ + if (!pfc_bit_set && pfvf->pfc_alloc_status[prio]) { + mutex_unlock(&mbox->lock); + if (if_up) + netif_tx_stop_all_queues(pfvf->netdev); + + otx2_smq_flush(pfvf, pfvf->pfc_schq_list[NIX_TXSCH_LVL_SMQ][prio]); + if (if_up) + netif_tx_start_all_queues(pfvf->netdev); + + /* delete the schq */ + err = otx2_pfc_txschq_stop_one(pfvf, prio); + if (err) { + dev_err(pfvf->dev, + "%s failed to stop PFC tx schedulers for priority: %d\n", + __func__, prio); + return err; + } + + mutex_lock(&mbox->lock); + goto update_sq_smq_map; + } + + /* Either PFC bit is not set + * or Tx scheduler is already mapped for the priority + */ + if (!pfc_bit_set || pfvf->pfc_alloc_status[prio]) + continue; + + /* Add new scheduler to the priority */ + err = otx2_pfc_txschq_alloc_one(pfvf, prio); + if (err) { + mutex_unlock(&mbox->lock); + dev_err(pfvf->dev, + "%s failed to allocate PFC tx schedulers for priority: %d\n", + __func__, prio); + return err; + } + +update_sq_smq_map: + err = otx2_pfc_update_sq_smq_mapping(pfvf, prio); + if (err) { + mutex_unlock(&mbox->lock); + dev_err(pfvf->dev, "%s failed PFC Tx schq sq:%d mapping", __func__, prio); + return err; + } + } + + err = otx2_pfc_txschq_config(pfvf); + mutex_unlock(&mbox->lock); + if (err) + return err; + + return 0; +} + +int otx2_pfc_txschq_stop(struct otx2_nic *pfvf) +{ + u8 pfc_en, pfc_bit_set; + int prio, err; + + pfc_en = pfvf->pfc_en; + for (prio = 0; prio < NIX_PF_PFC_PRIO_MAX; prio++) { + pfc_bit_set = pfc_en & (1 << prio); + if (!pfc_bit_set || !pfvf->pfc_alloc_status[prio]) + continue; + + /* Delete the existing scheduler */ + err = otx2_pfc_txschq_stop_one(pfvf, prio); + if (err) { + dev_err(pfvf->dev, "%s failed to stop PFC TX schedulers\n", __func__); + return err; + } + } + + return 0; +} + int otx2_config_priority_flow_ctrl(struct otx2_nic *pfvf) { struct cgx_pfc_cfg *req; @@ -128,6 +411,17 @@ static int otx2_dcbnl_ieee_setpfc(struct net_device *dev, struct ieee_pfc *pfc) /* Save PFC configuration to interface */ pfvf->pfc_en = pfc->pfc_en; + if (pfvf->hw.tx_queues >= NIX_PF_PFC_PRIO_MAX) + goto process_pfc; + + /* Check if the PFC configuration can be + * supported by the tx queue configuration + */ + err = otx2_check_pfc_config(pfvf); + if (err) + return err; + +process_pfc: err = otx2_config_priority_flow_ctrl(pfvf); if (err) return err; @@ -136,6 +430,12 @@ static int otx2_dcbnl_ieee_setpfc(struct net_device *dev, struct ieee_pfc *pfc) if (pfc->pfc_en) otx2_nix_config_bp(pfvf, true); + err = otx2_pfc_txschq_update(pfvf); + if (err) { + dev_err(pfvf->dev, "%s failed to update TX schedulers\n", __func__); + return err; + } + return 0; } diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_ethtool.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_ethtool.c index 3f60a80e34c8..0eb74e8c553d 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_ethtool.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_ethtool.c @@ -76,8 +76,8 @@ static void otx2_get_drvinfo(struct net_device *netdev, { struct otx2_nic *pfvf = netdev_priv(netdev); - strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); - strlcpy(info->bus_info, pci_name(pfvf->pdev), sizeof(info->bus_info)); + strscpy(info->driver, DRV_NAME, sizeof(info->driver)); + strscpy(info->bus_info, pci_name(pfvf->pdev), sizeof(info->bus_info)); } static void otx2_get_qset_strings(struct otx2_nic *pfvf, u8 **data, int qset) @@ -963,10 +963,12 @@ static int otx2_get_ts_info(struct net_device *netdev, info->phc_index = otx2_ptp_clock_index(pfvf); - info->tx_types = (1 << HWTSTAMP_TX_OFF) | (1 << HWTSTAMP_TX_ON); + info->tx_types = BIT(HWTSTAMP_TX_OFF) | BIT(HWTSTAMP_TX_ON); + if (test_bit(CN10K_PTP_ONESTEP, &pfvf->hw.cap_flag)) + info->tx_types |= BIT(HWTSTAMP_TX_ONESTEP_SYNC); - info->rx_filters = (1 << HWTSTAMP_FILTER_NONE) | - (1 << HWTSTAMP_FILTER_ALL); + info->rx_filters = BIT(HWTSTAMP_FILTER_NONE) | + BIT(HWTSTAMP_FILTER_ALL); return 0; } @@ -1313,8 +1315,8 @@ static void otx2vf_get_drvinfo(struct net_device *netdev, { struct otx2_nic *vf = netdev_priv(netdev); - strlcpy(info->driver, DRV_VF_NAME, sizeof(info->driver)); - strlcpy(info->bus_info, pci_name(vf->pdev), sizeof(info->bus_info)); + strscpy(info->driver, DRV_VF_NAME, sizeof(info->driver)); + strscpy(info->bus_info, pci_name(vf->pdev), sizeof(info->bus_info)); } static void otx2vf_get_strings(struct net_device *netdev, u32 sset, u8 *data) diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c index 9376d0e62914..5803d7f9137c 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c @@ -858,6 +858,15 @@ static void otx2_handle_link_event(struct otx2_nic *pf) } } +int otx2_mbox_up_handler_mcs_intr_notify(struct otx2_nic *pf, + struct mcs_intr_info *event, + struct msg_rsp *rsp) +{ + cn10k_handle_mcs_event(pf, event); + + return 0; +} + int otx2_mbox_up_handler_cgx_link_event(struct otx2_nic *pf, struct cgx_link_info_msg *msg, struct msg_rsp *rsp) @@ -917,6 +926,7 @@ static int otx2_process_mbox_msg_up(struct otx2_nic *pf, return err; \ } MBOX_UP_CGX_MESSAGES +MBOX_UP_MCS_MESSAGES #undef M break; default: @@ -1389,18 +1399,40 @@ static int otx2_init_hw_resources(struct otx2_nic *pf) goto err_free_sq_ptrs; } +#ifdef CONFIG_DCB + if (pf->pfc_en) { + err = otx2_pfc_txschq_alloc(pf); + if (err) { + mutex_unlock(&mbox->lock); + goto err_free_sq_ptrs; + } + } +#endif + err = otx2_config_nix_queues(pf); if (err) { mutex_unlock(&mbox->lock); goto err_free_txsch; } + for (lvl = 0; lvl < NIX_TXSCH_LVL_CNT; lvl++) { - err = otx2_txschq_config(pf, lvl); + err = otx2_txschq_config(pf, lvl, 0, false); + if (err) { + mutex_unlock(&mbox->lock); + goto err_free_nix_queues; + } + } + +#ifdef CONFIG_DCB + if (pf->pfc_en) { + err = otx2_pfc_txschq_config(pf); if (err) { mutex_unlock(&mbox->lock); goto err_free_nix_queues; } } +#endif + mutex_unlock(&mbox->lock); return err; @@ -1455,6 +1487,11 @@ static void otx2_free_hw_resources(struct otx2_nic *pf) if (err) dev_err(pf->dev, "RVUPF: Failed to stop/free TX schedulers\n"); +#ifdef CONFIG_DCB + if (pf->pfc_en) + otx2_pfc_txschq_stop(pf); +#endif + mutex_lock(&mbox->lock); /* Disable backpressure */ if (!(pf->pcifunc & RVU_PFVF_FUNC_MASK)) @@ -1634,8 +1671,7 @@ int otx2_open(struct net_device *netdev) cq_poll->dev = (void *)pf; cq_poll->dim.mode = DIM_CQ_PERIOD_MODE_START_FROM_CQE; INIT_WORK(&cq_poll->dim.work, otx2_dim_work); - netif_napi_add(netdev, &cq_poll->napi, - otx2_napi_handler, NAPI_POLL_WEIGHT); + netif_napi_add(netdev, &cq_poll->napi, otx2_napi_handler); napi_enable(&cq_poll->napi); } @@ -1853,6 +1889,30 @@ static netdev_tx_t otx2_xmit(struct sk_buff *skb, struct net_device *netdev) return NETDEV_TX_OK; } +static u16 otx2_select_queue(struct net_device *netdev, struct sk_buff *skb, + struct net_device *sb_dev) +{ +#ifdef CONFIG_DCB + struct otx2_nic *pf = netdev_priv(netdev); + u8 vlan_prio; +#endif + +#ifdef CONFIG_DCB + if (!skb->vlan_present) + goto pick_tx; + + vlan_prio = skb->vlan_tci >> 13; + if ((vlan_prio > pf->hw.tx_queues - 1) || + !pf->pfc_alloc_status[vlan_prio]) + goto pick_tx; + + return vlan_prio; + +pick_tx: +#endif + return netdev_pick_tx(netdev, skb, NULL); +} + static netdev_features_t otx2_fix_features(struct net_device *dev, netdev_features_t features) { @@ -1987,8 +2047,19 @@ int otx2_config_hwtstamp(struct net_device *netdev, struct ifreq *ifr) switch (config.tx_type) { case HWTSTAMP_TX_OFF: + if (pfvf->flags & OTX2_FLAG_PTP_ONESTEP_SYNC) + pfvf->flags &= ~OTX2_FLAG_PTP_ONESTEP_SYNC; + + cancel_delayed_work(&pfvf->ptp->synctstamp_work); otx2_config_hw_tx_tstamp(pfvf, false); break; + case HWTSTAMP_TX_ONESTEP_SYNC: + if (!test_bit(CN10K_PTP_ONESTEP, &pfvf->hw.cap_flag)) + return -ERANGE; + pfvf->flags |= OTX2_FLAG_PTP_ONESTEP_SYNC; + schedule_delayed_work(&pfvf->ptp->synctstamp_work, + msecs_to_jiffies(500)); + fallthrough; case HWTSTAMP_TX_ON: otx2_config_hw_tx_tstamp(pfvf, true); break; @@ -2447,6 +2518,7 @@ static const struct net_device_ops otx2_netdev_ops = { .ndo_open = otx2_open, .ndo_stop = otx2_stop, .ndo_start_xmit = otx2_xmit, + .ndo_select_queue = otx2_select_queue, .ndo_fix_features = otx2_fix_features, .ndo_set_mac_address = otx2_set_mac_address, .ndo_change_mtu = otx2_change_mtu, @@ -2702,6 +2774,10 @@ static int otx2_probe(struct pci_dev *pdev, const struct pci_device_id *id) if (err) goto err_ptp_destroy; + err = cn10k_mcs_init(pf); + if (err) + goto err_del_mcam_entries; + if (pf->flags & OTX2_FLAG_NTUPLE_SUPPORT) netdev->hw_features |= NETIF_F_NTUPLE; @@ -2916,6 +2992,8 @@ static void otx2_remove(struct pci_dev *pdev) otx2_config_pause_frm(pf); } + cn10k_mcs_free(pf); + #ifdef CONFIG_DCB /* Disable PFC config */ if (pf->pfc_en) { diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_ptp.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_ptp.c index fdc2c9315b91..896b2f9bac34 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_ptp.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_ptp.c @@ -10,6 +10,33 @@ #include "otx2_common.h" #include "otx2_ptp.h" +static u64 otx2_ptp_get_clock(struct otx2_ptp *ptp) +{ + struct ptp_req *req; + struct ptp_rsp *rsp; + int err; + + if (!ptp->nic) + return 0; + + req = otx2_mbox_alloc_msg_ptp_op(&ptp->nic->mbox); + if (!req) + return 0; + + req->op = PTP_OP_GET_CLOCK; + + err = otx2_sync_mbox_msg(&ptp->nic->mbox); + if (err) + return 0; + + rsp = (struct ptp_rsp *)otx2_mbox_get_rsp(&ptp->nic->mbox.mbox, 0, + &req->hdr); + if (IS_ERR(rsp)) + return 0; + + return rsp->clk; +} + static int otx2_ptp_adjfine(struct ptp_clock_info *ptp_info, long scaled_ppm) { struct otx2_ptp *ptp = container_of(ptp_info, struct otx2_ptp, @@ -46,32 +73,28 @@ static int ptp_set_thresh(struct otx2_ptp *ptp, u64 thresh) return otx2_sync_mbox_msg(&ptp->nic->mbox); } -static u64 ptp_cc_read(const struct cyclecounter *cc) +static int ptp_extts_on(struct otx2_ptp *ptp, int on) { - struct otx2_ptp *ptp = container_of(cc, struct otx2_ptp, cycle_counter); struct ptp_req *req; - struct ptp_rsp *rsp; - int err; if (!ptp->nic) - return 0; + return -ENODEV; req = otx2_mbox_alloc_msg_ptp_op(&ptp->nic->mbox); if (!req) - return 0; + return -ENOMEM; - req->op = PTP_OP_GET_CLOCK; + req->op = PTP_OP_EXTTS_ON; + req->extts_on = on; - err = otx2_sync_mbox_msg(&ptp->nic->mbox); - if (err) - return 0; + return otx2_sync_mbox_msg(&ptp->nic->mbox); +} - rsp = (struct ptp_rsp *)otx2_mbox_get_rsp(&ptp->nic->mbox.mbox, 0, - &req->hdr); - if (IS_ERR(rsp)) - return 0; +static u64 ptp_cc_read(const struct cyclecounter *cc) +{ + struct otx2_ptp *ptp = container_of(cc, struct otx2_ptp, cycle_counter); - return rsp->clk; + return otx2_ptp_get_clock(ptp); } static u64 ptp_tstmp_read(struct otx2_ptp *ptp) @@ -101,6 +124,15 @@ static u64 ptp_tstmp_read(struct otx2_ptp *ptp) return rsp->clk; } +static void otx2_get_ptpclock(struct otx2_ptp *ptp, u64 *tstamp) +{ + struct otx2_nic *pfvf = ptp->nic; + + mutex_lock(&pfvf->mbox.lock); + *tstamp = timecounter_read(&ptp->time_counter); + mutex_unlock(&pfvf->mbox.lock); +} + static int otx2_ptp_adjtime(struct ptp_clock_info *ptp_info, s64 delta) { struct otx2_ptp *ptp = container_of(ptp_info, struct otx2_ptp, @@ -119,14 +151,10 @@ static int otx2_ptp_gettime(struct ptp_clock_info *ptp_info, { struct otx2_ptp *ptp = container_of(ptp_info, struct otx2_ptp, ptp_info); - struct otx2_nic *pfvf = ptp->nic; - u64 nsec; + u64 tstamp; - mutex_lock(&pfvf->mbox.lock); - nsec = timecounter_read(&ptp->time_counter); - mutex_unlock(&pfvf->mbox.lock); - - *ts = ns_to_timespec64(nsec); + otx2_get_ptpclock(ptp, &tstamp); + *ts = ns_to_timespec64(tstamp); return 0; } @@ -178,8 +206,6 @@ static void otx2_ptp_extts_check(struct work_struct *work) event.index = 0; event.timestamp = timecounter_cyc2time(&ptp->time_counter, tstmp); ptp_clock_event(ptp->ptp_clock, &event); - ptp->last_extts = tstmp; - new_thresh = tstmp % 500000000; if (ptp->thresh != new_thresh) { mutex_lock(&ptp->nic->mbox.lock); @@ -187,10 +213,28 @@ static void otx2_ptp_extts_check(struct work_struct *work) mutex_unlock(&ptp->nic->mbox.lock); ptp->thresh = new_thresh; } + ptp->last_extts = tstmp; } schedule_delayed_work(&ptp->extts_work, msecs_to_jiffies(200)); } +static void otx2_sync_tstamp(struct work_struct *work) +{ + struct otx2_ptp *ptp = container_of(work, struct otx2_ptp, + synctstamp_work.work); + struct otx2_nic *pfvf = ptp->nic; + u64 tstamp; + + mutex_lock(&pfvf->mbox.lock); + tstamp = otx2_ptp_get_clock(ptp); + mutex_unlock(&pfvf->mbox.lock); + + ptp->tstamp = timecounter_cyc2time(&pfvf->ptp->time_counter, tstamp); + ptp->base_ns = tstamp % NSEC_PER_SEC; + + schedule_delayed_work(&ptp->synctstamp_work, msecs_to_jiffies(250)); +} + static int otx2_ptp_enable(struct ptp_clock_info *ptp_info, struct ptp_clock_request *rq, int on) { @@ -207,10 +251,13 @@ static int otx2_ptp_enable(struct ptp_clock_info *ptp_info, rq->extts.index); if (pin < 0) return -EBUSY; - if (on) + if (on) { + ptp_extts_on(ptp, on); schedule_delayed_work(&ptp->extts_work, msecs_to_jiffies(200)); - else + } else { + ptp_extts_on(ptp, on); cancel_delayed_work_sync(&ptp->extts_work); + } return 0; default: break; @@ -302,6 +349,8 @@ int otx2_ptp_init(struct otx2_nic *pfvf) ptp_ptr->convert_tx_ptp_tstmp = &cn10k_ptp_convert_timestamp; } + INIT_DELAYED_WORK(&ptp_ptr->synctstamp_work, otx2_sync_tstamp); + pfvf->ptp = ptp_ptr; error: @@ -316,6 +365,8 @@ void otx2_ptp_destroy(struct otx2_nic *pfvf) if (!ptp) return; + cancel_delayed_work(&pfvf->ptp->synctstamp_work); + ptp_clock_unregister(ptp->ptp_clock); kfree(ptp); pfvf->ptp = NULL; diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_struct.h b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_struct.h index 4bbd12ff26e6..aa205a0d158f 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_struct.h +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_struct.h @@ -236,8 +236,15 @@ struct nix_sqe_sg_s { /* NIX send memory subdescriptor structure */ struct nix_sqe_mem_s { - u64 offset : 16; /* W0 */ - u64 rsvd_51_16 : 36; + u64 start_offset : 8; + u64 rsvd_11_8 : 4; + u64 rsvd_12 : 1; + u64 udp_csum_crt : 1; + u64 update64 : 1; + u64 rsvd_15_16 : 1; + u64 base_ns : 32; + u64 step_type : 1; + u64 rsvd_51_49 : 3; u64 per_lso_seg : 1; u64 wmem : 1; u64 dsz : 2; diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.c index a18e8efd0f1e..5ec11d71bf60 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.c @@ -19,6 +19,12 @@ #include "cn10k.h" #define CQE_ADDR(CQ, idx) ((CQ)->cqe_base + ((CQ)->cqe_size * (idx))) +#define PTP_PORT 0x13F +/* PTPv2 header Original Timestamp starts at byte offset 34 and + * contains 6 byte seconds field and 4 byte nano seconds field. + */ +#define PTP_SYNC_SEC_OFFSET 34 + static bool otx2_xdp_rcv_pkt_handler(struct otx2_nic *pfvf, struct bpf_prog *prog, struct nix_cqe_rx_s *cqe, @@ -686,7 +692,8 @@ static void otx2_sqe_add_ext(struct otx2_nic *pfvf, struct otx2_snd_queue *sq, } static void otx2_sqe_add_mem(struct otx2_snd_queue *sq, int *offset, - int alg, u64 iova) + int alg, u64 iova, int ptp_offset, + u64 base_ns, int udp_csum) { struct nix_sqe_mem_s *mem; @@ -696,6 +703,13 @@ static void otx2_sqe_add_mem(struct otx2_snd_queue *sq, int *offset, mem->wmem = 1; /* wait for the memory operation */ mem->addr = iova; + if (ptp_offset) { + mem->start_offset = ptp_offset; + mem->udp_csum_crt = udp_csum; + mem->base_ns = base_ns; + mem->step_type = 1; + } + *offset += sizeof(*mem); } @@ -952,16 +966,102 @@ static int otx2_get_sqe_count(struct otx2_nic *pfvf, struct sk_buff *skb) return skb_shinfo(skb)->gso_segs; } +static bool otx2_validate_network_transport(struct sk_buff *skb) +{ + if ((ip_hdr(skb)->protocol == IPPROTO_UDP) || + (ipv6_hdr(skb)->nexthdr == IPPROTO_UDP)) { + struct udphdr *udph = udp_hdr(skb); + + if (udph->source == htons(PTP_PORT) && + udph->dest == htons(PTP_PORT)) + return true; + } + + return false; +} + +static bool otx2_ptp_is_sync(struct sk_buff *skb, int *offset, int *udp_csum) +{ + struct ethhdr *eth = (struct ethhdr *)(skb->data); + u16 nix_offload_hlen = 0, inner_vhlen = 0; + u8 *data = skb->data, *msgtype; + __be16 proto = eth->h_proto; + int network_depth = 0; + + /* NIX is programmed to offload outer VLAN header + * in case of single vlan protocol field holds Network header ETH_IP/V6 + * in case of stacked vlan protocol field holds Inner vlan (8100) + */ + if (skb->dev->features & NETIF_F_HW_VLAN_CTAG_TX && + skb->dev->features & NETIF_F_HW_VLAN_STAG_TX) { + if (skb->vlan_proto == htons(ETH_P_8021AD)) { + /* Get vlan protocol */ + proto = __vlan_get_protocol(skb, eth->h_proto, NULL); + /* SKB APIs like skb_transport_offset does not include + * offloaded vlan header length. Need to explicitly add + * the length + */ + nix_offload_hlen = VLAN_HLEN; + inner_vhlen = VLAN_HLEN; + } else if (skb->vlan_proto == htons(ETH_P_8021Q)) { + nix_offload_hlen = VLAN_HLEN; + } + } else if (eth_type_vlan(eth->h_proto)) { + proto = __vlan_get_protocol(skb, eth->h_proto, &network_depth); + } + + switch (ntohs(proto)) { + case ETH_P_1588: + if (network_depth) + *offset = network_depth; + else + *offset = ETH_HLEN + nix_offload_hlen + + inner_vhlen; + break; + case ETH_P_IP: + case ETH_P_IPV6: + if (!otx2_validate_network_transport(skb)) + return false; + + *udp_csum = 1; + *offset = nix_offload_hlen + skb_transport_offset(skb) + + sizeof(struct udphdr); + } + + msgtype = data + *offset; + + /* Check PTP messageId is SYNC or not */ + return (*msgtype & 0xf) == 0; +} + static void otx2_set_txtstamp(struct otx2_nic *pfvf, struct sk_buff *skb, struct otx2_snd_queue *sq, int *offset) { + struct ptpv2_tstamp *origin_tstamp; + int ptp_offset = 0, udp_csum = 0; + struct timespec64 ts; u64 iova; - if (!skb_shinfo(skb)->gso_size && - skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) { - skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; + if (unlikely(!skb_shinfo(skb)->gso_size && + (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP))) { + if (unlikely(pfvf->flags & OTX2_FLAG_PTP_ONESTEP_SYNC)) { + if (otx2_ptp_is_sync(skb, &ptp_offset, &udp_csum)) { + origin_tstamp = (struct ptpv2_tstamp *) + ((u8 *)skb->data + ptp_offset + + PTP_SYNC_SEC_OFFSET); + ts = ns_to_timespec64(pfvf->ptp->tstamp); + origin_tstamp->seconds_msb = htons((ts.tv_sec >> 32) & 0xffff); + origin_tstamp->seconds_lsb = htonl(ts.tv_sec & 0xffffffff); + origin_tstamp->nanoseconds = htonl(ts.tv_nsec); + /* Point to correction field in PTP packet */ + ptp_offset += 8; + } + } else { + skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; + } iova = sq->timestamps->iova + (sq->head * sizeof(u64)); - otx2_sqe_add_mem(sq, offset, NIX_SENDMEMALG_E_SETTSTMP, iova); + otx2_sqe_add_mem(sq, offset, NIX_SENDMEMALG_E_SETTSTMP, iova, + ptp_offset, pfvf->ptp->base_ns, udp_csum); } else { skb_tx_timestamp(skb); } diff --git a/drivers/net/ethernet/marvell/prestera/Makefile b/drivers/net/ethernet/marvell/prestera/Makefile index d395f4131648..df14cee80153 100644 --- a/drivers/net/ethernet/marvell/prestera/Makefile +++ b/drivers/net/ethernet/marvell/prestera/Makefile @@ -4,6 +4,6 @@ prestera-objs := prestera_main.o prestera_hw.o prestera_dsa.o \ prestera_rxtx.o prestera_devlink.o prestera_ethtool.o \ prestera_switchdev.o prestera_acl.o prestera_flow.o \ prestera_flower.o prestera_span.o prestera_counter.o \ - prestera_router.o prestera_router_hw.o + prestera_router.o prestera_router_hw.o prestera_matchall.o obj-$(CONFIG_PRESTERA_PCI) += prestera_pci.o diff --git a/drivers/net/ethernet/marvell/prestera/prestera.h b/drivers/net/ethernet/marvell/prestera/prestera.h index 2f84d0fb4094..35554ee805cd 100644 --- a/drivers/net/ethernet/marvell/prestera/prestera.h +++ b/drivers/net/ethernet/marvell/prestera/prestera.h @@ -306,17 +306,27 @@ struct prestera_switch { struct prestera_counter *counter; u8 lag_member_max; u8 lag_max; + u32 size_tbl_router_nexthop; }; struct prestera_router { struct prestera_switch *sw; struct list_head vr_list; struct list_head rif_entry_list; + struct rhashtable nh_neigh_ht; + struct rhashtable nexthop_group_ht; struct rhashtable fib_ht; + struct rhashtable kern_neigh_cache_ht; struct rhashtable kern_fib_cache_ht; struct notifier_block inetaddr_nb; struct notifier_block inetaddr_valid_nb; struct notifier_block fib_nb; + struct notifier_block netevent_nb; + u8 *nhgrp_hw_state_cache; /* Bitmap cached hw state of nhs */ + unsigned long nhgrp_hw_cache_kick; /* jiffies */ + struct { + struct delayed_work dw; + } neighs_update; }; struct prestera_rxtx_params { @@ -362,11 +372,15 @@ int prestera_port_cfg_mac_write(struct prestera_port *port, struct prestera_port *prestera_port_dev_lower_find(struct net_device *dev); void prestera_queue_work(struct work_struct *work); +void prestera_queue_delayed_work(struct delayed_work *work, unsigned long delay); +void prestera_queue_drain(void); int prestera_port_learning_set(struct prestera_port *port, bool learn_enable); int prestera_port_uc_flood_set(struct prestera_port *port, bool flood); int prestera_port_mc_flood_set(struct prestera_port *port, bool flood); +int prestera_port_br_locked_set(struct prestera_port *port, bool br_locked); + int prestera_port_pvid_set(struct prestera_port *port, u16 vid); bool prestera_netdev_check(const struct net_device *dev); diff --git a/drivers/net/ethernet/marvell/prestera/prestera_acl.c b/drivers/net/ethernet/marvell/prestera/prestera_acl.c index 3d4b85f2d541..cba89fda504b 100644 --- a/drivers/net/ethernet/marvell/prestera/prestera_acl.c +++ b/drivers/net/ethernet/marvell/prestera/prestera_acl.c @@ -54,6 +54,10 @@ struct prestera_acl_ruleset { struct prestera_acl_ruleset_ht_key ht_key; struct rhashtable rule_ht; struct prestera_acl *acl; + struct { + u32 min; + u32 max; + } prio; unsigned long rule_count; refcount_t refcount; void *keymask; @@ -162,6 +166,9 @@ prestera_acl_ruleset_create(struct prestera_acl *acl, ruleset->pcl_id = PRESTERA_ACL_PCL_ID_MAKE((u8)uid, chain_index); ruleset->index = uid; + ruleset->prio.min = UINT_MAX; + ruleset->prio.max = 0; + err = rhashtable_insert_fast(&acl->ruleset_ht, &ruleset->ht_node, prestera_acl_ruleset_ht_params); if (err) @@ -178,10 +185,14 @@ err_rhashtable_init: return ERR_PTR(err); } -void prestera_acl_ruleset_keymask_set(struct prestera_acl_ruleset *ruleset, - void *keymask) +int prestera_acl_ruleset_keymask_set(struct prestera_acl_ruleset *ruleset, + void *keymask) { ruleset->keymask = kmemdup(keymask, ACL_KEYMASK_SIZE, GFP_KERNEL); + if (!ruleset->keymask) + return -ENOMEM; + + return 0; } int prestera_acl_ruleset_offload(struct prestera_acl_ruleset *ruleset) @@ -365,6 +376,26 @@ prestera_acl_ruleset_block_unbind(struct prestera_acl_ruleset *ruleset, block->ruleset_zero = NULL; } +static void +prestera_acl_ruleset_prio_refresh(struct prestera_acl *acl, + struct prestera_acl_ruleset *ruleset) +{ + struct prestera_acl_rule *rule; + + ruleset->prio.min = UINT_MAX; + ruleset->prio.max = 0; + + list_for_each_entry(rule, &acl->rules, list) { + if (ruleset->ingress != rule->ruleset->ingress) + continue; + if (ruleset->ht_key.chain_index != rule->chain_index) + continue; + + ruleset->prio.min = min(ruleset->prio.min, rule->priority); + ruleset->prio.max = max(ruleset->prio.max, rule->priority); + } +} + void prestera_acl_rule_keymask_pcl_id_set(struct prestera_acl_rule *rule, u16 pcl_id) { @@ -389,6 +420,13 @@ u32 prestera_acl_ruleset_index_get(const struct prestera_acl_ruleset *ruleset) return ruleset->index; } +void prestera_acl_ruleset_prio_get(struct prestera_acl_ruleset *ruleset, + u32 *prio_min, u32 *prio_max) +{ + *prio_min = ruleset->prio.min; + *prio_max = ruleset->prio.max; +} + bool prestera_acl_ruleset_is_offload(struct prestera_acl_ruleset *ruleset) { return ruleset->offload; @@ -429,6 +467,13 @@ void prestera_acl_rule_destroy(struct prestera_acl_rule *rule) kfree(rule); } +static void prestera_acl_ruleset_prio_update(struct prestera_acl_ruleset *ruleset, + u32 prio) +{ + ruleset->prio.min = min(ruleset->prio.min, prio); + ruleset->prio.max = max(ruleset->prio.max, prio); +} + int prestera_acl_rule_add(struct prestera_switch *sw, struct prestera_acl_rule *rule) { @@ -468,6 +513,7 @@ int prestera_acl_rule_add(struct prestera_switch *sw, list_add_tail(&rule->list, &sw->acl->rules); ruleset->rule_count++; + prestera_acl_ruleset_prio_update(ruleset, rule->priority); return 0; err_acl_block_bind: @@ -492,6 +538,7 @@ void prestera_acl_rule_del(struct prestera_switch *sw, list_del(&rule->list); prestera_acl_rule_entry_destroy(sw->acl, rule->re); + prestera_acl_ruleset_prio_refresh(sw->acl, ruleset); /* unbind block (all ports) */ if (!ruleset->ht_key.chain_index && !ruleset->rule_count) diff --git a/drivers/net/ethernet/marvell/prestera/prestera_acl.h b/drivers/net/ethernet/marvell/prestera/prestera_acl.h index 03fc5b9dc925..a35cc0609a1d 100644 --- a/drivers/net/ethernet/marvell/prestera/prestera_acl.h +++ b/drivers/net/ethernet/marvell/prestera/prestera_acl.h @@ -185,8 +185,8 @@ struct prestera_acl_ruleset * prestera_acl_ruleset_lookup(struct prestera_acl *acl, struct prestera_flow_block *block, u32 chain_index); -void prestera_acl_ruleset_keymask_set(struct prestera_acl_ruleset *ruleset, - void *keymask); +int prestera_acl_ruleset_keymask_set(struct prestera_acl_ruleset *ruleset, + void *keymask); bool prestera_acl_ruleset_is_offload(struct prestera_acl_ruleset *ruleset); int prestera_acl_ruleset_offload(struct prestera_acl_ruleset *ruleset); void prestera_acl_ruleset_put(struct prestera_acl_ruleset *ruleset); @@ -195,6 +195,8 @@ int prestera_acl_ruleset_bind(struct prestera_acl_ruleset *ruleset, int prestera_acl_ruleset_unbind(struct prestera_acl_ruleset *ruleset, struct prestera_port *port); u32 prestera_acl_ruleset_index_get(const struct prestera_acl_ruleset *ruleset); +void prestera_acl_ruleset_prio_get(struct prestera_acl_ruleset *ruleset, + u32 *prio_min, u32 *prio_max); void prestera_acl_rule_keymask_pcl_id_set(struct prestera_acl_rule *rule, u16 pcl_id); diff --git a/drivers/net/ethernet/marvell/prestera/prestera_ethtool.c b/drivers/net/ethernet/marvell/prestera/prestera_ethtool.c index 1da7ff889417..2f52daba58e6 100644 --- a/drivers/net/ethernet/marvell/prestera/prestera_ethtool.c +++ b/drivers/net/ethernet/marvell/prestera/prestera_ethtool.c @@ -300,8 +300,8 @@ static void prestera_ethtool_get_drvinfo(struct net_device *dev, struct prestera_port *port = netdev_priv(dev); struct prestera_switch *sw = port->sw; - strlcpy(drvinfo->driver, driver_kind, sizeof(drvinfo->driver)); - strlcpy(drvinfo->bus_info, dev_name(prestera_dev(sw)), + strscpy(drvinfo->driver, driver_kind, sizeof(drvinfo->driver)); + strscpy(drvinfo->bus_info, dev_name(prestera_dev(sw)), sizeof(drvinfo->bus_info)); snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version), "%d.%d.%d", diff --git a/drivers/net/ethernet/marvell/prestera/prestera_flow.c b/drivers/net/ethernet/marvell/prestera/prestera_flow.c index 2262693bd5cf..9f4267f326b0 100644 --- a/drivers/net/ethernet/marvell/prestera/prestera_flow.c +++ b/drivers/net/ethernet/marvell/prestera/prestera_flow.c @@ -7,8 +7,9 @@ #include "prestera.h" #include "prestera_acl.h" #include "prestera_flow.h" -#include "prestera_span.h" #include "prestera_flower.h" +#include "prestera_matchall.h" +#include "prestera_span.h" static LIST_HEAD(prestera_block_cb_list); @@ -17,9 +18,9 @@ static int prestera_flow_block_mall_cb(struct prestera_flow_block *block, { switch (f->command) { case TC_CLSMATCHALL_REPLACE: - return prestera_span_replace(block, f); + return prestera_mall_replace(block, f); case TC_CLSMATCHALL_DESTROY: - prestera_span_destroy(block); + prestera_mall_destroy(block); return 0; default: return -EOPNOTSUPP; @@ -89,6 +90,9 @@ prestera_flow_block_create(struct prestera_switch *sw, INIT_LIST_HEAD(&block->template_list); block->net = net; block->sw = sw; + block->mall.prio_min = UINT_MAX; + block->mall.prio_max = 0; + block->mall.bound = false; block->ingress = ingress; return block; @@ -263,7 +267,7 @@ static void prestera_setup_flow_block_unbind(struct prestera_port *port, block = flow_block_cb_priv(block_cb); - prestera_span_destroy(block); + prestera_mall_destroy(block); err = prestera_flow_block_unbind(block, port); if (err) diff --git a/drivers/net/ethernet/marvell/prestera/prestera_flow.h b/drivers/net/ethernet/marvell/prestera/prestera_flow.h index 0c9e13263261..a85a3eb40279 100644 --- a/drivers/net/ethernet/marvell/prestera/prestera_flow.h +++ b/drivers/net/ethernet/marvell/prestera/prestera_flow.h @@ -22,6 +22,11 @@ struct prestera_flow_block { struct prestera_acl_ruleset *ruleset_zero; struct flow_block_cb *block_cb; struct list_head template_list; + struct { + u32 prio_min; + u32 prio_max; + bool bound; + } mall; unsigned int rule_count; bool ingress; }; diff --git a/drivers/net/ethernet/marvell/prestera/prestera_flower.c b/drivers/net/ethernet/marvell/prestera/prestera_flower.c index 19d3b55c578e..91a478b75cbf 100644 --- a/drivers/net/ethernet/marvell/prestera/prestera_flower.c +++ b/drivers/net/ethernet/marvell/prestera/prestera_flower.c @@ -5,6 +5,7 @@ #include "prestera_acl.h" #include "prestera_flow.h" #include "prestera_flower.h" +#include "prestera_matchall.h" struct prestera_flower_template { struct prestera_acl_ruleset *ruleset; @@ -360,6 +361,49 @@ static int prestera_flower_parse(struct prestera_flow_block *block, f->common.extack); } +static int prestera_flower_prio_check(struct prestera_flow_block *block, + struct flow_cls_offload *f) +{ + u32 mall_prio_min; + u32 mall_prio_max; + int err; + + err = prestera_mall_prio_get(block, &mall_prio_min, &mall_prio_max); + if (err == -ENOENT) + /* No matchall filters installed on this chain. */ + return 0; + + if (err) { + NL_SET_ERR_MSG(f->common.extack, "Failed to get matchall priorities"); + return err; + } + + if (f->common.prio <= mall_prio_max && block->ingress) { + NL_SET_ERR_MSG(f->common.extack, + "Failed to add in front of existing matchall rules"); + return -EOPNOTSUPP; + } + if (f->common.prio >= mall_prio_min && !block->ingress) { + NL_SET_ERR_MSG(f->common.extack, "Failed to add behind of existing matchall rules"); + return -EOPNOTSUPP; + } + + return 0; +} + +int prestera_flower_prio_get(struct prestera_flow_block *block, u32 chain_index, + u32 *prio_min, u32 *prio_max) +{ + struct prestera_acl_ruleset *ruleset; + + ruleset = prestera_acl_ruleset_lookup(block->sw->acl, block, chain_index); + if (IS_ERR(ruleset)) + return PTR_ERR(ruleset); + + prestera_acl_ruleset_prio_get(ruleset, prio_min, prio_max); + return 0; +} + int prestera_flower_replace(struct prestera_flow_block *block, struct flow_cls_offload *f) { @@ -368,6 +412,10 @@ int prestera_flower_replace(struct prestera_flow_block *block, struct prestera_acl_rule *rule; int err; + err = prestera_flower_prio_check(block, f); + if (err) + return err; + ruleset = prestera_acl_ruleset_get(acl, block, f->common.chain_index); if (IS_ERR(ruleset)) return PTR_ERR(ruleset); @@ -452,7 +500,9 @@ int prestera_flower_tmplt_create(struct prestera_flow_block *block, } /* preserve keymask/template to this ruleset */ - prestera_acl_ruleset_keymask_set(ruleset, rule.re_key.match.mask); + err = prestera_acl_ruleset_keymask_set(ruleset, rule.re_key.match.mask); + if (err) + goto err_ruleset_keymask_set; /* skip error, as it is not possible to reject template operation, * so, keep the reference to the ruleset for rules to be added @@ -468,6 +518,8 @@ int prestera_flower_tmplt_create(struct prestera_flow_block *block, list_add_rcu(&template->list, &block->template_list); return 0; +err_ruleset_keymask_set: + prestera_acl_ruleset_put(ruleset); err_ruleset_get: kfree(template); err_malloc: diff --git a/drivers/net/ethernet/marvell/prestera/prestera_flower.h b/drivers/net/ethernet/marvell/prestera/prestera_flower.h index 495f151e6fa9..1181115fe6fa 100644 --- a/drivers/net/ethernet/marvell/prestera/prestera_flower.h +++ b/drivers/net/ethernet/marvell/prestera/prestera_flower.h @@ -19,5 +19,7 @@ int prestera_flower_tmplt_create(struct prestera_flow_block *block, void prestera_flower_tmplt_destroy(struct prestera_flow_block *block, struct flow_cls_offload *f); void prestera_flower_template_cleanup(struct prestera_flow_block *block); +int prestera_flower_prio_get(struct prestera_flow_block *block, u32 chain_index, + u32 *prio_min, u32 *prio_max); #endif /* _PRESTERA_FLOWER_H_ */ diff --git a/drivers/net/ethernet/marvell/prestera/prestera_hw.c b/drivers/net/ethernet/marvell/prestera/prestera_hw.c index 962d7e0c0cb5..fc6f7d2746e8 100644 --- a/drivers/net/ethernet/marvell/prestera/prestera_hw.c +++ b/drivers/net/ethernet/marvell/prestera/prestera_hw.c @@ -10,11 +10,14 @@ #include "prestera_hw.h" #include "prestera_acl.h" #include "prestera_counter.h" +#include "prestera_router_hw.h" #define PRESTERA_SWITCH_INIT_TIMEOUT_MS (30 * 1000) #define PRESTERA_MIN_MTU 64 +#define PRESTERA_MSG_CHUNK_SIZE 1024 + enum prestera_cmd_type_t { PRESTERA_CMD_TYPE_SWITCH_INIT = 0x1, PRESTERA_CMD_TYPE_SWITCH_ATTR_SET = 0x2, @@ -57,6 +60,10 @@ enum prestera_cmd_type_t { PRESTERA_CMD_TYPE_ROUTER_RIF_DELETE = 0x601, PRESTERA_CMD_TYPE_ROUTER_LPM_ADD = 0x610, PRESTERA_CMD_TYPE_ROUTER_LPM_DELETE = 0x611, + PRESTERA_CMD_TYPE_ROUTER_NH_GRP_SET = 0x622, + PRESTERA_CMD_TYPE_ROUTER_NH_GRP_BLK_GET = 0x645, + PRESTERA_CMD_TYPE_ROUTER_NH_GRP_ADD = 0x623, + PRESTERA_CMD_TYPE_ROUTER_NH_GRP_DELETE = 0x624, PRESTERA_CMD_TYPE_ROUTER_VR_CREATE = 0x630, PRESTERA_CMD_TYPE_ROUTER_VR_DELETE = 0x631, @@ -78,9 +85,11 @@ enum prestera_cmd_type_t { PRESTERA_CMD_TYPE_STP_PORT_SET = 0x1000, PRESTERA_CMD_TYPE_SPAN_GET = 0x1100, - PRESTERA_CMD_TYPE_SPAN_BIND = 0x1101, - PRESTERA_CMD_TYPE_SPAN_UNBIND = 0x1102, + PRESTERA_CMD_TYPE_SPAN_INGRESS_BIND = 0x1101, + PRESTERA_CMD_TYPE_SPAN_INGRESS_UNBIND = 0x1102, PRESTERA_CMD_TYPE_SPAN_RELEASE = 0x1103, + PRESTERA_CMD_TYPE_SPAN_EGRESS_BIND = 0x1104, + PRESTERA_CMD_TYPE_SPAN_EGRESS_UNBIND = 0x1105, PRESTERA_CMD_TYPE_POLICER_CREATE = 0x1500, PRESTERA_CMD_TYPE_POLICER_RELEASE = 0x1501, @@ -101,6 +110,7 @@ enum { PRESTERA_CMD_PORT_ATTR_LEARNING = 7, PRESTERA_CMD_PORT_ATTR_FLOOD = 8, PRESTERA_CMD_PORT_ATTR_CAPABILITY = 9, + PRESTERA_CMD_PORT_ATTR_LOCKED = 10, PRESTERA_CMD_PORT_ATTR_PHY_MODE = 12, PRESTERA_CMD_PORT_ATTR_TYPE = 13, PRESTERA_CMD_PORT_ATTR_STATS = 17, @@ -285,6 +295,7 @@ union prestera_msg_port_param { u8 duplex; u8 fec; u8 fc; + u8 br_locked; union { struct { u8 admin; @@ -538,6 +549,14 @@ struct prestera_msg_ip_addr { u8 __pad[3]; }; +struct prestera_msg_nh { + struct prestera_msg_iface oif; + __le32 hw_id; + u8 mac[ETH_ALEN]; + u8 is_active; + u8 pad; +}; + struct prestera_msg_rif_req { struct prestera_msg_cmd cmd; struct prestera_msg_iface iif; @@ -563,6 +582,34 @@ struct prestera_msg_lpm_req { u8 __pad[2]; }; +struct prestera_msg_nh_req { + struct prestera_msg_cmd cmd; + struct prestera_msg_nh nh[PRESTERA_NHGR_SIZE_MAX]; + __le32 size; + __le32 grp_id; +}; + +struct prestera_msg_nh_chunk_req { + struct prestera_msg_cmd cmd; + __le32 offset; +}; + +struct prestera_msg_nh_chunk_resp { + struct prestera_msg_ret ret; + u8 hw_state[PRESTERA_MSG_CHUNK_SIZE]; +}; + +struct prestera_msg_nh_grp_req { + struct prestera_msg_cmd cmd; + __le32 grp_id; + __le32 size; +}; + +struct prestera_msg_nh_grp_resp { + struct prestera_msg_ret ret; + __le32 grp_id; +}; + struct prestera_msg_vr_req { struct prestera_msg_cmd cmd; __le16 vr_id; @@ -725,11 +772,15 @@ static void prestera_hw_build_tests(void) BUILD_BUG_ON(sizeof(struct prestera_msg_flood_domain_ports_reset_req) != 8); BUILD_BUG_ON(sizeof(struct prestera_msg_mdb_create_req) != 16); BUILD_BUG_ON(sizeof(struct prestera_msg_mdb_destroy_req) != 16); + BUILD_BUG_ON(sizeof(struct prestera_msg_nh_req) != 124); + BUILD_BUG_ON(sizeof(struct prestera_msg_nh_chunk_req) != 8); + BUILD_BUG_ON(sizeof(struct prestera_msg_nh_grp_req) != 12); /* structure that are part of req/resp fw messages */ BUILD_BUG_ON(sizeof(struct prestera_msg_iface) != 16); BUILD_BUG_ON(sizeof(struct prestera_msg_ip_addr) != 20); BUILD_BUG_ON(sizeof(struct prestera_msg_flood_domain_port) != 12); + BUILD_BUG_ON(sizeof(struct prestera_msg_nh) != 28); /* check responses */ BUILD_BUG_ON(sizeof(struct prestera_msg_common_resp) != 8); @@ -745,6 +796,9 @@ static void prestera_hw_build_tests(void) BUILD_BUG_ON(sizeof(struct prestera_msg_rif_resp) != 12); BUILD_BUG_ON(sizeof(struct prestera_msg_vr_resp) != 12); BUILD_BUG_ON(sizeof(struct prestera_msg_policer_resp) != 12); + BUILD_BUG_ON(sizeof(struct prestera_msg_flood_domain_create_resp) != 12); + BUILD_BUG_ON(sizeof(struct prestera_msg_nh_chunk_resp) != 1032); + BUILD_BUG_ON(sizeof(struct prestera_msg_nh_grp_resp) != 12); /* check events */ BUILD_BUG_ON(sizeof(struct prestera_msg_event_port) != 20); @@ -1022,6 +1076,8 @@ int prestera_hw_switch_init(struct prestera_switch *sw) sw->id = resp.switch_id; sw->lag_member_max = resp.lag_member_max; sw->lag_max = resp.lag_max; + sw->size_tbl_router_nexthop = + __le32_to_cpu(resp.size_tbl_router_nexthop); return 0; } @@ -1431,27 +1487,39 @@ int prestera_hw_span_get(const struct prestera_port *port, u8 *span_id) return 0; } -int prestera_hw_span_bind(const struct prestera_port *port, u8 span_id) +int prestera_hw_span_bind(const struct prestera_port *port, u8 span_id, + bool ingress) { struct prestera_msg_span_req req = { .port = __cpu_to_le32(port->hw_id), .dev = __cpu_to_le32(port->dev_id), .id = span_id, }; + enum prestera_cmd_type_t cmd_type; + + if (ingress) + cmd_type = PRESTERA_CMD_TYPE_SPAN_INGRESS_BIND; + else + cmd_type = PRESTERA_CMD_TYPE_SPAN_EGRESS_BIND; + + return prestera_cmd(port->sw, cmd_type, &req.cmd, sizeof(req)); - return prestera_cmd(port->sw, PRESTERA_CMD_TYPE_SPAN_BIND, - &req.cmd, sizeof(req)); } -int prestera_hw_span_unbind(const struct prestera_port *port) +int prestera_hw_span_unbind(const struct prestera_port *port, bool ingress) { struct prestera_msg_span_req req = { .port = __cpu_to_le32(port->hw_id), .dev = __cpu_to_le32(port->dev_id), }; + enum prestera_cmd_type_t cmd_type; - return prestera_cmd(port->sw, PRESTERA_CMD_TYPE_SPAN_UNBIND, - &req.cmd, sizeof(req)); + if (ingress) + cmd_type = PRESTERA_CMD_TYPE_SPAN_INGRESS_UNBIND; + else + cmd_type = PRESTERA_CMD_TYPE_SPAN_EGRESS_UNBIND; + + return prestera_cmd(port->sw, cmd_type, &req.cmd, sizeof(req)); } int prestera_hw_span_release(struct prestera_switch *sw, u8 span_id) @@ -1639,6 +1707,22 @@ int prestera_hw_port_mc_flood_set(const struct prestera_port *port, bool flood) &req.cmd, sizeof(req)); } +int prestera_hw_port_br_locked_set(const struct prestera_port *port, + bool br_locked) +{ + struct prestera_msg_port_attr_req req = { + .attr = __cpu_to_le32(PRESTERA_CMD_PORT_ATTR_LOCKED), + .port = __cpu_to_le32(port->hw_id), + .dev = __cpu_to_le32(port->dev_id), + .param = { + .br_locked = br_locked, + } + }; + + return prestera_cmd(port->sw, PRESTERA_CMD_TYPE_PORT_ATTR_SET, + &req.cmd, sizeof(req)); +} + int prestera_hw_vlan_create(struct prestera_switch *sw, u16 vid) { struct prestera_msg_vlan_req req = { @@ -2004,6 +2088,85 @@ int prestera_hw_lpm_del(struct prestera_switch *sw, u16 vr_id, sizeof(req)); } +int prestera_hw_nh_entries_set(struct prestera_switch *sw, int count, + struct prestera_neigh_info *nhs, u32 grp_id) +{ + struct prestera_msg_nh_req req = { .size = __cpu_to_le32((u32)count), + .grp_id = __cpu_to_le32(grp_id) }; + int i, err; + + for (i = 0; i < count; i++) { + req.nh[i].is_active = nhs[i].connected; + memcpy(&req.nh[i].mac, nhs[i].ha, ETH_ALEN); + err = prestera_iface_to_msg(&nhs[i].iface, &req.nh[i].oif); + if (err) + return err; + } + + return prestera_cmd(sw, PRESTERA_CMD_TYPE_ROUTER_NH_GRP_SET, &req.cmd, + sizeof(req)); +} + +int prestera_hw_nhgrp_blk_get(struct prestera_switch *sw, + u8 *hw_state, u32 buf_size /* Buffer in bytes */) +{ + static struct prestera_msg_nh_chunk_resp resp; + struct prestera_msg_nh_chunk_req req; + u32 buf_offset; + int err; + + memset(&hw_state[0], 0, buf_size); + buf_offset = 0; + while (1) { + if (buf_offset >= buf_size) + break; + + memset(&req, 0, sizeof(req)); + req.offset = __cpu_to_le32(buf_offset * 8); /* 8 bits in u8 */ + err = prestera_cmd_ret(sw, + PRESTERA_CMD_TYPE_ROUTER_NH_GRP_BLK_GET, + &req.cmd, sizeof(req), &resp.ret, + sizeof(resp)); + if (err) + return err; + + memcpy(&hw_state[buf_offset], &resp.hw_state[0], + buf_offset + PRESTERA_MSG_CHUNK_SIZE > buf_size ? + buf_size - buf_offset : PRESTERA_MSG_CHUNK_SIZE); + buf_offset += PRESTERA_MSG_CHUNK_SIZE; + } + + return 0; +} + +int prestera_hw_nh_group_create(struct prestera_switch *sw, u16 nh_count, + u32 *grp_id) +{ + struct prestera_msg_nh_grp_req req = { .size = __cpu_to_le32((u32)nh_count) }; + struct prestera_msg_nh_grp_resp resp; + int err; + + err = prestera_cmd_ret(sw, PRESTERA_CMD_TYPE_ROUTER_NH_GRP_ADD, + &req.cmd, sizeof(req), &resp.ret, sizeof(resp)); + if (err) + return err; + + *grp_id = __le32_to_cpu(resp.grp_id); + return err; +} + +int prestera_hw_nh_group_delete(struct prestera_switch *sw, u16 nh_count, + u32 grp_id) +{ + struct prestera_msg_nh_grp_req req = { + .grp_id = __cpu_to_le32(grp_id), + .size = __cpu_to_le32(nh_count) + }; + + return prestera_cmd(sw, PRESTERA_CMD_TYPE_ROUTER_NH_GRP_DELETE, + &req.cmd, sizeof(req)); +} + int prestera_hw_rxtx_init(struct prestera_switch *sw, struct prestera_rxtx_params *params) { diff --git a/drivers/net/ethernet/marvell/prestera/prestera_hw.h b/drivers/net/ethernet/marvell/prestera/prestera_hw.h index 56e043146dd2..0a929279e1ce 100644 --- a/drivers/net/ethernet/marvell/prestera/prestera_hw.h +++ b/drivers/net/ethernet/marvell/prestera/prestera_hw.h @@ -146,6 +146,7 @@ struct prestera_counter_stats; struct prestera_iface; struct prestera_flood_domain; struct prestera_mdb_entry; +struct prestera_neigh_info; /* Switch API */ int prestera_hw_switch_init(struct prestera_switch *sw); @@ -183,6 +184,8 @@ int prestera_hw_port_speed_get(const struct prestera_port *port, u32 *speed); int prestera_hw_port_learning_set(struct prestera_port *port, bool enable); int prestera_hw_port_uc_flood_set(const struct prestera_port *port, bool flood); int prestera_hw_port_mc_flood_set(const struct prestera_port *port, bool flood); +int prestera_hw_port_br_locked_set(const struct prestera_port *port, + bool br_locked); int prestera_hw_port_accept_frm_type(struct prestera_port *port, enum prestera_accept_frm_type type); /* Vlan API */ @@ -243,8 +246,9 @@ int prestera_hw_counter_clear(struct prestera_switch *sw, u32 block_id, /* SPAN API */ int prestera_hw_span_get(const struct prestera_port *port, u8 *span_id); -int prestera_hw_span_bind(const struct prestera_port *port, u8 span_id); -int prestera_hw_span_unbind(const struct prestera_port *port); +int prestera_hw_span_bind(const struct prestera_port *port, u8 span_id, + bool ingress); +int prestera_hw_span_unbind(const struct prestera_port *port, bool ingress); int prestera_hw_span_release(struct prestera_switch *sw, u8 span_id); /* Router API */ @@ -263,6 +267,16 @@ int prestera_hw_lpm_add(struct prestera_switch *sw, u16 vr_id, int prestera_hw_lpm_del(struct prestera_switch *sw, u16 vr_id, __be32 dst, u32 dst_len); +/* NH API */ +int prestera_hw_nh_entries_set(struct prestera_switch *sw, int count, + struct prestera_neigh_info *nhs, u32 grp_id); +int prestera_hw_nhgrp_blk_get(struct prestera_switch *sw, + u8 *hw_state, u32 buf_size /* Buffer in bytes */); +int prestera_hw_nh_group_create(struct prestera_switch *sw, u16 nh_count, + u32 *grp_id); +int prestera_hw_nh_group_delete(struct prestera_switch *sw, u16 nh_count, + u32 grp_id); + /* Event handlers */ int prestera_hw_event_handler_register(struct prestera_switch *sw, enum prestera_event_type type, diff --git a/drivers/net/ethernet/marvell/prestera/prestera_main.c b/drivers/net/ethernet/marvell/prestera/prestera_main.c index a895862b4821..24f9d6024745 100644 --- a/drivers/net/ethernet/marvell/prestera/prestera_main.c +++ b/drivers/net/ethernet/marvell/prestera/prestera_main.c @@ -36,6 +36,17 @@ void prestera_queue_work(struct work_struct *work) queue_work(prestera_owq, work); } +void prestera_queue_delayed_work(struct delayed_work *work, unsigned long delay) +{ + queue_delayed_work(prestera_wq, work, delay); +} + +void prestera_queue_drain(void) +{ + drain_workqueue(prestera_wq); + drain_workqueue(prestera_owq); +} + int prestera_port_learning_set(struct prestera_port *port, bool learn) { return prestera_hw_port_learning_set(port, learn); @@ -51,6 +62,11 @@ int prestera_port_mc_flood_set(struct prestera_port *port, bool flood) return prestera_hw_port_mc_flood_set(port, flood); } +int prestera_port_br_locked_set(struct prestera_port *port, bool br_locked) +{ + return prestera_hw_port_br_locked_set(port, br_locked); +} + int prestera_port_pvid_set(struct prestera_port *port, u16 vid) { enum prestera_accept_frm_type frm_type; @@ -799,32 +815,30 @@ static void prestera_port_handle_event(struct prestera_switch *sw, caching_dw = &port->cached_hw_stats.caching_dw; - if (port->phy_link) { - memset(&smac, 0, sizeof(smac)); - smac.valid = true; - smac.oper = pevt->data.mac.oper; - if (smac.oper) { - smac.mode = pevt->data.mac.mode; - smac.speed = pevt->data.mac.speed; - smac.duplex = pevt->data.mac.duplex; - smac.fc = pevt->data.mac.fc; - smac.fec = pevt->data.mac.fec; - phylink_mac_change(port->phy_link, true); - } else { - phylink_mac_change(port->phy_link, false); - } - prestera_port_mac_state_cache_write(port, &smac); + memset(&smac, 0, sizeof(smac)); + smac.valid = true; + smac.oper = pevt->data.mac.oper; + if (smac.oper) { + smac.mode = pevt->data.mac.mode; + smac.speed = pevt->data.mac.speed; + smac.duplex = pevt->data.mac.duplex; + smac.fc = pevt->data.mac.fc; + smac.fec = pevt->data.mac.fec; } + prestera_port_mac_state_cache_write(port, &smac); if (port->state_mac.oper) { - if (!port->phy_link) + if (port->phy_link) + phylink_mac_change(port->phy_link, true); + else netif_carrier_on(port->dev); if (!delayed_work_pending(caching_dw)) queue_delayed_work(prestera_wq, caching_dw, 0); - } else if (netif_running(port->dev) && - netif_carrier_ok(port->dev)) { - if (!port->phy_link) + } else { + if (port->phy_link) + phylink_mac_change(port->phy_link, false); + else if (netif_running(port->dev) && netif_carrier_ok(port->dev)) netif_carrier_off(port->dev); if (delayed_work_pending(caching_dw)) diff --git a/drivers/net/ethernet/marvell/prestera/prestera_matchall.c b/drivers/net/ethernet/marvell/prestera/prestera_matchall.c new file mode 100644 index 000000000000..6f2b95a5263e --- /dev/null +++ b/drivers/net/ethernet/marvell/prestera/prestera_matchall.c @@ -0,0 +1,125 @@ +// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 +/* Copyright (c) 2019-2022 Marvell International Ltd. All rights reserved */ + +#include <linux/kernel.h> +#include <linux/list.h> + +#include "prestera.h" +#include "prestera_hw.h" +#include "prestera_flow.h" +#include "prestera_flower.h" +#include "prestera_matchall.h" +#include "prestera_span.h" + +static int prestera_mall_prio_check(struct prestera_flow_block *block, + struct tc_cls_matchall_offload *f) +{ + u32 flower_prio_min; + u32 flower_prio_max; + int err; + + err = prestera_flower_prio_get(block, f->common.chain_index, + &flower_prio_min, &flower_prio_max); + if (err == -ENOENT) + /* No flower filters installed on this chain. */ + return 0; + + if (err) { + NL_SET_ERR_MSG(f->common.extack, "Failed to get flower priorities"); + return err; + } + + if (f->common.prio <= flower_prio_max && !block->ingress) { + NL_SET_ERR_MSG(f->common.extack, "Failed to add in front of existing flower rules"); + return -EOPNOTSUPP; + } + if (f->common.prio >= flower_prio_min && block->ingress) { + NL_SET_ERR_MSG(f->common.extack, "Failed to add behind of existing flower rules"); + return -EOPNOTSUPP; + } + + return 0; +} + +int prestera_mall_prio_get(struct prestera_flow_block *block, + u32 *prio_min, u32 *prio_max) +{ + if (!block->mall.bound) + return -ENOENT; + + *prio_min = block->mall.prio_min; + *prio_max = block->mall.prio_max; + return 0; +} + +static void prestera_mall_prio_update(struct prestera_flow_block *block, + struct tc_cls_matchall_offload *f) +{ + block->mall.prio_min = min(block->mall.prio_min, f->common.prio); + block->mall.prio_max = max(block->mall.prio_max, f->common.prio); +} + +int prestera_mall_replace(struct prestera_flow_block *block, + struct tc_cls_matchall_offload *f) +{ + struct prestera_flow_block_binding *binding; + __be16 protocol = f->common.protocol; + struct flow_action_entry *act; + struct prestera_port *port; + int err; + + if (!flow_offload_has_one_action(&f->rule->action)) { + NL_SET_ERR_MSG(f->common.extack, + "Only singular actions are supported"); + return -EOPNOTSUPP; + } + + act = &f->rule->action.entries[0]; + + if (!prestera_netdev_check(act->dev)) { + NL_SET_ERR_MSG(f->common.extack, + "Only Marvell Prestera port is supported"); + return -EINVAL; + } + if (!tc_cls_can_offload_and_chain0(act->dev, &f->common)) + return -EOPNOTSUPP; + if (act->id != FLOW_ACTION_MIRRED) + return -EOPNOTSUPP; + if (protocol != htons(ETH_P_ALL)) + return -EOPNOTSUPP; + + err = prestera_mall_prio_check(block, f); + if (err) + return err; + + port = netdev_priv(act->dev); + + list_for_each_entry(binding, &block->binding_list, list) { + err = prestera_span_rule_add(binding, port, block->ingress); + if (err) + goto rollback; + } + + prestera_mall_prio_update(block, f); + + block->mall.bound = true; + return 0; + +rollback: + list_for_each_entry_continue_reverse(binding, + &block->binding_list, list) + prestera_span_rule_del(binding, block->ingress); + return err; +} + +void prestera_mall_destroy(struct prestera_flow_block *block) +{ + struct prestera_flow_block_binding *binding; + + list_for_each_entry(binding, &block->binding_list, list) + prestera_span_rule_del(binding, block->ingress); + + block->mall.prio_min = UINT_MAX; + block->mall.prio_max = 0; + block->mall.bound = false; +} diff --git a/drivers/net/ethernet/marvell/prestera/prestera_matchall.h b/drivers/net/ethernet/marvell/prestera/prestera_matchall.h new file mode 100644 index 000000000000..fed08be80257 --- /dev/null +++ b/drivers/net/ethernet/marvell/prestera/prestera_matchall.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 */ +/* Copyright (c) 2022 Marvell International Ltd. All rights reserved. */ + +#ifndef _PRESTERA_MATCHALL_H_ +#define _PRESTERA_MATCHALL_H_ + +#include <net/pkt_cls.h> + +struct prestera_flow_block; + +int prestera_mall_replace(struct prestera_flow_block *block, + struct tc_cls_matchall_offload *f); +void prestera_mall_destroy(struct prestera_flow_block *block); +int prestera_mall_prio_get(struct prestera_flow_block *block, + u32 *prio_min, u32 *prio_max); + +#endif /* _PRESTERA_MATCHALL_H_ */ diff --git a/drivers/net/ethernet/marvell/prestera/prestera_router.c b/drivers/net/ethernet/marvell/prestera/prestera_router.c index 58f4e44d5ad7..4046be0e86ff 100644 --- a/drivers/net/ethernet/marvell/prestera/prestera_router.c +++ b/drivers/net/ethernet/marvell/prestera/prestera_router.c @@ -7,10 +7,35 @@ #include <net/inet_dscp.h> #include <net/switchdev.h> #include <linux/rhashtable.h> +#include <net/nexthop.h> +#include <net/arp.h> +#include <linux/if_vlan.h> +#include <linux/if_macvlan.h> +#include <net/netevent.h> #include "prestera.h" #include "prestera_router_hw.h" +#define PRESTERA_IMPLICITY_RESOLVE_DEAD_NEIGH +#define PRESTERA_NH_PROBE_INTERVAL 5000 /* ms */ + +struct prestera_kern_neigh_cache_key { + struct prestera_ip_addr addr; + struct net_device *dev; +}; + +struct prestera_kern_neigh_cache { + struct prestera_kern_neigh_cache_key key; + struct rhash_head ht_node; + struct list_head kern_fib_cache_list; + /* Hold prepared nh_neigh info if is in_kernel */ + struct prestera_neigh_info nh_neigh_info; + /* Indicate if neighbour is reachable by direct route */ + bool reachable; + /* Lock cache if neigh is present in kernel */ + bool in_kernel; +}; + struct prestera_kern_fib_cache_key { struct prestera_ip_addr addr; u32 prefix_len; @@ -23,15 +48,29 @@ struct prestera_kern_fib_cache { struct { struct prestera_fib_key fib_key; enum prestera_fib_type fib_type; + struct prestera_nexthop_group_key nh_grp_key; } lpm_info; /* hold prepared lpm info */ /* Indicate if route is not overlapped by another table */ struct rhash_head ht_node; /* node of prestera_router */ - struct fib_info *fi; - dscp_t kern_dscp; - u8 kern_type; + struct prestera_kern_neigh_cache_head { + struct prestera_kern_fib_cache *this; + struct list_head head; + struct prestera_kern_neigh_cache *n_cache; + } kern_neigh_cache_head[PRESTERA_NHGR_SIZE_MAX]; + union { + struct fib_notifier_info info; /* point to any of 4/6 */ + struct fib_entry_notifier_info fen4_info; + }; bool reachable; }; +static const struct rhashtable_params __prestera_kern_neigh_cache_ht_params = { + .key_offset = offsetof(struct prestera_kern_neigh_cache, key), + .head_offset = offsetof(struct prestera_kern_neigh_cache, ht_node), + .key_len = sizeof(struct prestera_kern_neigh_cache_key), + .automatic_shrinking = true, +}; + static const struct rhashtable_params __prestera_kern_fib_cache_ht_params = { .key_offset = offsetof(struct prestera_kern_fib_cache, key), .head_offset = offsetof(struct prestera_kern_fib_cache, ht_node), @@ -51,15 +90,450 @@ static u32 prestera_fix_tb_id(u32 tb_id) } static void -prestera_util_fen_info2fib_cache_key(struct fib_entry_notifier_info *fen_info, +prestera_util_fen_info2fib_cache_key(struct fib_notifier_info *info, struct prestera_kern_fib_cache_key *key) { + struct fib_entry_notifier_info *fen_info = + container_of(info, struct fib_entry_notifier_info, info); + memset(key, 0, sizeof(*key)); + key->addr.v = PRESTERA_IPV4; key->addr.u.ipv4 = cpu_to_be32(fen_info->dst); key->prefix_len = fen_info->dst_len; key->kern_tb_id = fen_info->tb_id; } +static int prestera_util_nhc2nc_key(struct prestera_switch *sw, + struct fib_nh_common *nhc, + struct prestera_kern_neigh_cache_key *nk) +{ + memset(nk, 0, sizeof(*nk)); + if (nhc->nhc_gw_family == AF_INET) { + nk->addr.v = PRESTERA_IPV4; + nk->addr.u.ipv4 = nhc->nhc_gw.ipv4; + } else { + nk->addr.v = PRESTERA_IPV6; + nk->addr.u.ipv6 = nhc->nhc_gw.ipv6; + } + + nk->dev = nhc->nhc_dev; + return 0; +} + +static void +prestera_util_nc_key2nh_key(struct prestera_kern_neigh_cache_key *ck, + struct prestera_nh_neigh_key *nk) +{ + memset(nk, 0, sizeof(*nk)); + nk->addr = ck->addr; + nk->rif = (void *)ck->dev; +} + +static bool +prestera_util_nhc_eq_n_cache_key(struct prestera_switch *sw, + struct fib_nh_common *nhc, + struct prestera_kern_neigh_cache_key *nk) +{ + struct prestera_kern_neigh_cache_key tk; + int err; + + err = prestera_util_nhc2nc_key(sw, nhc, &tk); + if (err) + return false; + + if (memcmp(&tk, nk, sizeof(tk))) + return false; + + return true; +} + +static int +prestera_util_neigh2nc_key(struct prestera_switch *sw, struct neighbour *n, + struct prestera_kern_neigh_cache_key *key) +{ + memset(key, 0, sizeof(*key)); + if (n->tbl->family == AF_INET) { + key->addr.v = PRESTERA_IPV4; + key->addr.u.ipv4 = *(__be32 *)n->primary_key; + } else { + return -ENOENT; + } + + key->dev = n->dev; + + return 0; +} + +static bool __prestera_fi_is_direct(struct fib_info *fi) +{ + struct fib_nh *fib_nh; + + if (fib_info_num_path(fi) == 1) { + fib_nh = fib_info_nh(fi, 0); + if (fib_nh->fib_nh_gw_family == AF_UNSPEC) + return true; + } + + return false; +} + +static bool prestera_fi_is_direct(struct fib_info *fi) +{ + if (fi->fib_type != RTN_UNICAST) + return false; + + return __prestera_fi_is_direct(fi); +} + +static bool prestera_fi_is_nh(struct fib_info *fi) +{ + if (fi->fib_type != RTN_UNICAST) + return false; + + return !__prestera_fi_is_direct(fi); +} + +static bool __prestera_fi6_is_direct(struct fib6_info *fi) +{ + if (!fi->fib6_nh->nh_common.nhc_gw_family) + return true; + + return false; +} + +static bool prestera_fi6_is_direct(struct fib6_info *fi) +{ + if (fi->fib6_type != RTN_UNICAST) + return false; + + return __prestera_fi6_is_direct(fi); +} + +static bool prestera_fi6_is_nh(struct fib6_info *fi) +{ + if (fi->fib6_type != RTN_UNICAST) + return false; + + return !__prestera_fi6_is_direct(fi); +} + +static bool prestera_fib_info_is_direct(struct fib_notifier_info *info) +{ + struct fib6_entry_notifier_info *fen6_info = + container_of(info, struct fib6_entry_notifier_info, info); + struct fib_entry_notifier_info *fen_info = + container_of(info, struct fib_entry_notifier_info, info); + + if (info->family == AF_INET) + return prestera_fi_is_direct(fen_info->fi); + else + return prestera_fi6_is_direct(fen6_info->rt); +} + +static bool prestera_fib_info_is_nh(struct fib_notifier_info *info) +{ + struct fib6_entry_notifier_info *fen6_info = + container_of(info, struct fib6_entry_notifier_info, info); + struct fib_entry_notifier_info *fen_info = + container_of(info, struct fib_entry_notifier_info, info); + + if (info->family == AF_INET) + return prestera_fi_is_nh(fen_info->fi); + else + return prestera_fi6_is_nh(fen6_info->rt); +} + +/* must be called with rcu_read_lock() */ +static int prestera_util_kern_get_route(struct fib_result *res, u32 tb_id, + __be32 *addr) +{ + struct flowi4 fl4; + + /* TODO: walkthrough appropriate tables in kernel + * to know if the same prefix exists in several tables + */ + memset(&fl4, 0, sizeof(fl4)); + fl4.daddr = *addr; + return fib_lookup(&init_net, &fl4, res, 0 /* FIB_LOOKUP_NOREF */); +} + +static bool +__prestera_util_kern_n_is_reachable_v4(u32 tb_id, __be32 *addr, + struct net_device *dev) +{ + struct fib_nh *fib_nh; + struct fib_result res; + bool reachable; + + reachable = false; + + if (!prestera_util_kern_get_route(&res, tb_id, addr)) + if (prestera_fi_is_direct(res.fi)) { + fib_nh = fib_info_nh(res.fi, 0); + if (dev == fib_nh->fib_nh_dev) + reachable = true; + } + + return reachable; +} + +/* Check if neigh route is reachable */ +static bool +prestera_util_kern_n_is_reachable(u32 tb_id, + struct prestera_ip_addr *addr, + struct net_device *dev) +{ + if (addr->v == PRESTERA_IPV4) + return __prestera_util_kern_n_is_reachable_v4(tb_id, + &addr->u.ipv4, + dev); + else + return false; +} + +static void prestera_util_kern_set_neigh_offload(struct neighbour *n, + bool offloaded) +{ + if (offloaded) + n->flags |= NTF_OFFLOADED; + else + n->flags &= ~NTF_OFFLOADED; +} + +static void +prestera_util_kern_set_nh_offload(struct fib_nh_common *nhc, bool offloaded, bool trap) +{ + if (offloaded) + nhc->nhc_flags |= RTNH_F_OFFLOAD; + else + nhc->nhc_flags &= ~RTNH_F_OFFLOAD; + + if (trap) + nhc->nhc_flags |= RTNH_F_TRAP; + else + nhc->nhc_flags &= ~RTNH_F_TRAP; +} + +static struct fib_nh_common * +prestera_kern_fib_info_nhc(struct fib_notifier_info *info, int n) +{ + struct fib6_entry_notifier_info *fen6_info; + struct fib_entry_notifier_info *fen4_info; + struct fib6_info *iter; + + if (info->family == AF_INET) { + fen4_info = container_of(info, struct fib_entry_notifier_info, + info); + return &fib_info_nh(fen4_info->fi, n)->nh_common; + } else if (info->family == AF_INET6) { + fen6_info = container_of(info, struct fib6_entry_notifier_info, + info); + if (!n) + return &fen6_info->rt->fib6_nh->nh_common; + + list_for_each_entry(iter, &fen6_info->rt->fib6_siblings, + fib6_siblings) { + if (!--n) + return &iter->fib6_nh->nh_common; + } + } + + /* if family is incorrect - than upper functions has BUG */ + /* if doesn't find requested index - there is alsi bug, because + * valid index must be produced by nhs, which checks list length + */ + WARN(1, "Invalid parameters passed to %s n=%d i=%p", + __func__, n, info); + return NULL; +} + +static int prestera_kern_fib_info_nhs(struct fib_notifier_info *info) +{ + struct fib6_entry_notifier_info *fen6_info; + struct fib_entry_notifier_info *fen4_info; + + if (info->family == AF_INET) { + fen4_info = container_of(info, struct fib_entry_notifier_info, + info); + return fib_info_num_path(fen4_info->fi); + } else if (info->family == AF_INET6) { + fen6_info = container_of(info, struct fib6_entry_notifier_info, + info); + return fen6_info->rt->fib6_nsiblings + 1; + } + + return 0; +} + +static unsigned char +prestera_kern_fib_info_type(struct fib_notifier_info *info) +{ + struct fib6_entry_notifier_info *fen6_info; + struct fib_entry_notifier_info *fen4_info; + + if (info->family == AF_INET) { + fen4_info = container_of(info, struct fib_entry_notifier_info, + info); + return fen4_info->fi->fib_type; + } else if (info->family == AF_INET6) { + fen6_info = container_of(info, struct fib6_entry_notifier_info, + info); + /* TODO: ECMP in ipv6 is several routes. + * Every route has single nh. + */ + return fen6_info->rt->fib6_type; + } + + return RTN_UNSPEC; +} + +/* Decided, that uc_nh route with key==nh is obviously neighbour route */ +static bool +prestera_fib_node_util_is_neighbour(struct prestera_fib_node *fib_node) +{ + if (fib_node->info.type != PRESTERA_FIB_TYPE_UC_NH) + return false; + + if (fib_node->info.nh_grp->nh_neigh_head[1].neigh) + return false; + + if (!fib_node->info.nh_grp->nh_neigh_head[0].neigh) + return false; + + if (memcmp(&fib_node->info.nh_grp->nh_neigh_head[0].neigh->key.addr, + &fib_node->key.addr, sizeof(struct prestera_ip_addr))) + return false; + + return true; +} + +static int prestera_dev_if_type(const struct net_device *dev) +{ + struct macvlan_dev *vlan; + + if (is_vlan_dev(dev) && + netif_is_bridge_master(vlan_dev_real_dev(dev))) { + return PRESTERA_IF_VID_E; + } else if (netif_is_bridge_master(dev)) { + return PRESTERA_IF_VID_E; + } else if (netif_is_lag_master(dev)) { + return PRESTERA_IF_LAG_E; + } else if (netif_is_macvlan(dev)) { + vlan = netdev_priv(dev); + return prestera_dev_if_type(vlan->lowerdev); + } else { + return PRESTERA_IF_PORT_E; + } +} + +static int +prestera_neigh_iface_init(struct prestera_switch *sw, + struct prestera_iface *iface, + struct neighbour *n) +{ + struct prestera_port *port; + + iface->vlan_id = 0; /* TODO: vlan egress */ + iface->type = prestera_dev_if_type(n->dev); + if (iface->type != PRESTERA_IF_PORT_E) + return -EINVAL; + + if (!prestera_netdev_check(n->dev)) + return -EINVAL; + + port = netdev_priv(n->dev); + iface->dev_port.hw_dev_num = port->dev_id; + iface->dev_port.port_num = port->hw_id; + + return 0; +} + +static struct prestera_kern_neigh_cache * +prestera_kern_neigh_cache_find(struct prestera_switch *sw, + struct prestera_kern_neigh_cache_key *key) +{ + struct prestera_kern_neigh_cache *n_cache; + + n_cache = + rhashtable_lookup_fast(&sw->router->kern_neigh_cache_ht, key, + __prestera_kern_neigh_cache_ht_params); + return IS_ERR(n_cache) ? NULL : n_cache; +} + +static void +__prestera_kern_neigh_cache_destruct(struct prestera_switch *sw, + struct prestera_kern_neigh_cache *n_cache) +{ + dev_put(n_cache->key.dev); +} + +static void +__prestera_kern_neigh_cache_destroy(struct prestera_switch *sw, + struct prestera_kern_neigh_cache *n_cache) +{ + rhashtable_remove_fast(&sw->router->kern_neigh_cache_ht, + &n_cache->ht_node, + __prestera_kern_neigh_cache_ht_params); + __prestera_kern_neigh_cache_destruct(sw, n_cache); + kfree(n_cache); +} + +static struct prestera_kern_neigh_cache * +__prestera_kern_neigh_cache_create(struct prestera_switch *sw, + struct prestera_kern_neigh_cache_key *key) +{ + struct prestera_kern_neigh_cache *n_cache; + int err; + + n_cache = kzalloc(sizeof(*n_cache), GFP_KERNEL); + if (!n_cache) + goto err_kzalloc; + + memcpy(&n_cache->key, key, sizeof(*key)); + dev_hold(n_cache->key.dev); + + INIT_LIST_HEAD(&n_cache->kern_fib_cache_list); + err = rhashtable_insert_fast(&sw->router->kern_neigh_cache_ht, + &n_cache->ht_node, + __prestera_kern_neigh_cache_ht_params); + if (err) + goto err_ht_insert; + + return n_cache; + +err_ht_insert: + dev_put(n_cache->key.dev); + kfree(n_cache); +err_kzalloc: + return NULL; +} + +static struct prestera_kern_neigh_cache * +prestera_kern_neigh_cache_get(struct prestera_switch *sw, + struct prestera_kern_neigh_cache_key *key) +{ + struct prestera_kern_neigh_cache *n_cache; + + n_cache = prestera_kern_neigh_cache_find(sw, key); + if (!n_cache) + n_cache = __prestera_kern_neigh_cache_create(sw, key); + + return n_cache; +} + +static struct prestera_kern_neigh_cache * +prestera_kern_neigh_cache_put(struct prestera_switch *sw, + struct prestera_kern_neigh_cache *n_cache) +{ + if (!n_cache->in_kernel && + list_empty(&n_cache->kern_fib_cache_list)) { + __prestera_kern_neigh_cache_destroy(sw, n_cache); + return NULL; + } + + return n_cache; +} + static struct prestera_kern_fib_cache * prestera_kern_fib_cache_find(struct prestera_switch *sw, struct prestera_kern_fib_cache_key *key) @@ -73,24 +547,79 @@ prestera_kern_fib_cache_find(struct prestera_switch *sw, } static void +__prestera_kern_fib_cache_destruct(struct prestera_switch *sw, + struct prestera_kern_fib_cache *fib_cache) +{ + struct prestera_kern_neigh_cache *n_cache; + int i; + + for (i = 0; i < PRESTERA_NHGR_SIZE_MAX; i++) { + n_cache = fib_cache->kern_neigh_cache_head[i].n_cache; + if (n_cache) { + list_del(&fib_cache->kern_neigh_cache_head[i].head); + prestera_kern_neigh_cache_put(sw, n_cache); + } + } + + fib_info_put(fib_cache->fen4_info.fi); +} + +static void prestera_kern_fib_cache_destroy(struct prestera_switch *sw, struct prestera_kern_fib_cache *fib_cache) { - fib_info_put(fib_cache->fi); rhashtable_remove_fast(&sw->router->kern_fib_cache_ht, &fib_cache->ht_node, __prestera_kern_fib_cache_ht_params); + __prestera_kern_fib_cache_destruct(sw, fib_cache); kfree(fib_cache); } +static int +__prestera_kern_fib_cache_create_nhs(struct prestera_switch *sw, + struct prestera_kern_fib_cache *fc) +{ + struct prestera_kern_neigh_cache_key nc_key; + struct prestera_kern_neigh_cache *n_cache; + struct fib_nh_common *nhc; + int i, nhs, err; + + if (!prestera_fib_info_is_nh(&fc->info)) + return 0; + + nhs = prestera_kern_fib_info_nhs(&fc->info); + if (nhs > PRESTERA_NHGR_SIZE_MAX) + return 0; + + for (i = 0; i < nhs; i++) { + nhc = prestera_kern_fib_info_nhc(&fc->fen4_info.info, i); + err = prestera_util_nhc2nc_key(sw, nhc, &nc_key); + if (err) + return 0; + + n_cache = prestera_kern_neigh_cache_get(sw, &nc_key); + if (!n_cache) + return 0; + + fc->kern_neigh_cache_head[i].this = fc; + fc->kern_neigh_cache_head[i].n_cache = n_cache; + list_add(&fc->kern_neigh_cache_head[i].head, + &n_cache->kern_fib_cache_list); + } + + return 0; +} + /* Operations on fi (offload, etc) must be wrapped in utils. * This function just create storage. */ static struct prestera_kern_fib_cache * prestera_kern_fib_cache_create(struct prestera_switch *sw, struct prestera_kern_fib_cache_key *key, - struct fib_info *fi, dscp_t dscp, u8 type) + struct fib_notifier_info *info) { + struct fib_entry_notifier_info *fen_info = + container_of(info, struct fib_entry_notifier_info, info); struct prestera_kern_fib_cache *fib_cache; int err; @@ -99,10 +628,8 @@ prestera_kern_fib_cache_create(struct prestera_switch *sw, goto err_kzalloc; memcpy(&fib_cache->key, key, sizeof(*key)); - fib_info_hold(fi); - fib_cache->fi = fi; - fib_cache->kern_dscp = dscp; - fib_cache->kern_type = type; + fib_info_hold(fen_info->fi); + memcpy(&fib_cache->fen4_info, fen_info, sizeof(*fen_info)); err = rhashtable_insert_fast(&sw->router->kern_fib_cache_ht, &fib_cache->ht_node, @@ -110,48 +637,270 @@ prestera_kern_fib_cache_create(struct prestera_switch *sw, if (err) goto err_ht_insert; + /* Handle nexthops */ + err = __prestera_kern_fib_cache_create_nhs(sw, fib_cache); + if (err) + goto out; /* Not critical */ + +out: return fib_cache; err_ht_insert: - fib_info_put(fi); + fib_info_put(fen_info->fi); kfree(fib_cache); err_kzalloc: return NULL; } static void +__prestera_k_arb_fib_nh_offload_set(struct prestera_switch *sw, + struct prestera_kern_fib_cache *fibc, + struct prestera_kern_neigh_cache *nc, + bool offloaded, bool trap) +{ + struct fib_nh_common *nhc; + int i, nhs; + + nhs = prestera_kern_fib_info_nhs(&fibc->info); + for (i = 0; i < nhs; i++) { + nhc = prestera_kern_fib_info_nhc(&fibc->info, i); + if (!nc) { + prestera_util_kern_set_nh_offload(nhc, offloaded, trap); + continue; + } + + if (prestera_util_nhc_eq_n_cache_key(sw, nhc, &nc->key)) { + prestera_util_kern_set_nh_offload(nhc, offloaded, trap); + break; + } + } +} + +static void +__prestera_k_arb_n_offload_set(struct prestera_switch *sw, + struct prestera_kern_neigh_cache *nc, + bool offloaded) +{ + struct neighbour *n; + + n = neigh_lookup(&arp_tbl, &nc->key.addr.u.ipv4, + nc->key.dev); + if (!n) + return; + + prestera_util_kern_set_neigh_offload(n, offloaded); + neigh_release(n); +} + +static void __prestera_k_arb_fib_lpm_offload_set(struct prestera_switch *sw, struct prestera_kern_fib_cache *fc, bool fail, bool offload, bool trap) { struct fib_rt_info fri; - if (fc->key.addr.v != PRESTERA_IPV4) + switch (fc->key.addr.v) { + case PRESTERA_IPV4: + fri.fi = fc->fen4_info.fi; + fri.tb_id = fc->key.kern_tb_id; + fri.dst = fc->key.addr.u.ipv4; + fri.dst_len = fc->key.prefix_len; + fri.dscp = fc->fen4_info.dscp; + fri.type = fc->fen4_info.type; + /* flags begin */ + fri.offload = offload; + fri.trap = trap; + fri.offload_failed = fail; + /* flags end */ + fib_alias_hw_flags_set(&init_net, &fri); + return; + case PRESTERA_IPV6: + /* TODO */ return; + } +} + +static void +__prestera_k_arb_n_lpm_set(struct prestera_switch *sw, + struct prestera_kern_neigh_cache *n_cache, + bool enabled) +{ + struct prestera_nexthop_group_key nh_grp_key; + struct prestera_kern_fib_cache_key fc_key; + struct prestera_kern_fib_cache *fib_cache; + struct prestera_fib_node *fib_node; + struct prestera_fib_key fib_key; + + /* Exception for fc with prefix 32: LPM entry is already used by fib */ + memset(&fc_key, 0, sizeof(fc_key)); + fc_key.addr = n_cache->key.addr; + fc_key.prefix_len = PRESTERA_IP_ADDR_PLEN(n_cache->key.addr.v); + /* But better to use tb_id of route, which pointed to this neighbour. */ + /* We take it from rif, because rif inconsistent. + * Must be separated in_rif and out_rif. + * Also note: for each fib pointed to this neigh should be separated + * neigh lpm entry (for each ingress vr) + */ + fc_key.kern_tb_id = l3mdev_fib_table(n_cache->key.dev); + fib_cache = prestera_kern_fib_cache_find(sw, &fc_key); + memset(&fib_key, 0, sizeof(fib_key)); + fib_key.addr = n_cache->key.addr; + fib_key.prefix_len = PRESTERA_IP_ADDR_PLEN(n_cache->key.addr.v); + fib_key.tb_id = prestera_fix_tb_id(fc_key.kern_tb_id); + fib_node = prestera_fib_node_find(sw, &fib_key); + if (!fib_cache || !fib_cache->reachable) { + if (!enabled && fib_node) { + if (prestera_fib_node_util_is_neighbour(fib_node)) + prestera_fib_node_destroy(sw, fib_node); + return; + } + } + + if (enabled && !fib_node) { + memset(&nh_grp_key, 0, sizeof(nh_grp_key)); + prestera_util_nc_key2nh_key(&n_cache->key, + &nh_grp_key.neigh[0]); + fib_node = prestera_fib_node_create(sw, &fib_key, + PRESTERA_FIB_TYPE_UC_NH, + &nh_grp_key); + if (!fib_node) + pr_err("%s failed ip=%pI4n", "prestera_fib_node_create", + &fib_key.addr.u.ipv4); + return; + } +} + +static void +__prestera_k_arb_nc_kern_fib_fetch(struct prestera_switch *sw, + struct prestera_kern_neigh_cache *nc) +{ + if (prestera_util_kern_n_is_reachable(l3mdev_fib_table(nc->key.dev), + &nc->key.addr, nc->key.dev)) + nc->reachable = true; + else + nc->reachable = false; +} + +/* Kernel neighbour -> neigh_cache info */ +static void +__prestera_k_arb_nc_kern_n_fetch(struct prestera_switch *sw, + struct prestera_kern_neigh_cache *nc) +{ + struct neighbour *n; + int err; + + memset(&nc->nh_neigh_info, 0, sizeof(nc->nh_neigh_info)); + n = neigh_lookup(&arp_tbl, &nc->key.addr.u.ipv4, nc->key.dev); + if (!n) + goto out; + + read_lock_bh(&n->lock); + if (n->nud_state & NUD_VALID && !n->dead) { + err = prestera_neigh_iface_init(sw, &nc->nh_neigh_info.iface, + n); + if (err) + goto n_read_out; - fri.fi = fc->fi; - fri.tb_id = fc->key.kern_tb_id; - fri.dst = fc->key.addr.u.ipv4; - fri.dst_len = fc->key.prefix_len; - fri.dscp = fc->kern_dscp; - fri.type = fc->kern_type; - /* flags begin */ - fri.offload = offload; - fri.trap = trap; - fri.offload_failed = fail; - /* flags end */ - fib_alias_hw_flags_set(&init_net, &fri); + memcpy(&nc->nh_neigh_info.ha[0], &n->ha[0], ETH_ALEN); + nc->nh_neigh_info.connected = true; + } +n_read_out: + read_unlock_bh(&n->lock); +out: + nc->in_kernel = nc->nh_neigh_info.connected; + if (n) + neigh_release(n); +} + +/* neigh_cache info -> lpm update */ +static void +__prestera_k_arb_nc_apply(struct prestera_switch *sw, + struct prestera_kern_neigh_cache *nc) +{ + struct prestera_kern_neigh_cache_head *nhead; + struct prestera_nh_neigh_key nh_key; + struct prestera_nh_neigh *nh_neigh; + int err; + + __prestera_k_arb_n_lpm_set(sw, nc, nc->reachable && nc->in_kernel); + __prestera_k_arb_n_offload_set(sw, nc, nc->reachable && nc->in_kernel); + + prestera_util_nc_key2nh_key(&nc->key, &nh_key); + nh_neigh = prestera_nh_neigh_find(sw, &nh_key); + if (!nh_neigh) + goto out; + + /* Do hw update only if something changed to prevent nh flap */ + if (memcmp(&nc->nh_neigh_info, &nh_neigh->info, + sizeof(nh_neigh->info))) { + memcpy(&nh_neigh->info, &nc->nh_neigh_info, + sizeof(nh_neigh->info)); + err = prestera_nh_neigh_set(sw, nh_neigh); + if (err) { + pr_err("%s failed with err=%d ip=%pI4n mac=%pM", + "prestera_nh_neigh_set", err, + &nh_neigh->key.addr.u.ipv4, + &nh_neigh->info.ha[0]); + goto out; + } + } + +out: + list_for_each_entry(nhead, &nc->kern_fib_cache_list, head) { + __prestera_k_arb_fib_nh_offload_set(sw, nhead->this, nc, + nc->in_kernel, + !nc->in_kernel); + } } static int __prestera_pr_k_arb_fc_lpm_info_calc(struct prestera_switch *sw, struct prestera_kern_fib_cache *fc) { + struct fib_nh_common *nhc; + int nh_cnt; + memset(&fc->lpm_info, 0, sizeof(fc->lpm_info)); - switch (fc->fi->fib_type) { + switch (prestera_kern_fib_info_type(&fc->info)) { case RTN_UNICAST: - fc->lpm_info.fib_type = PRESTERA_FIB_TYPE_TRAP; + if (prestera_fib_info_is_direct(&fc->info) && + fc->key.prefix_len == + PRESTERA_IP_ADDR_PLEN(fc->key.addr.v)) { + /* This is special case. + * When prefix is 32. Than we will have conflict in lpm + * for direct route - once TRAP added, there is no + * place for neighbour entry. So represent direct route + * with prefix 32, as NH. So neighbour will be resolved + * as nexthop of this route. + */ + nhc = prestera_kern_fib_info_nhc(&fc->info, 0); + fc->lpm_info.fib_type = PRESTERA_FIB_TYPE_UC_NH; + fc->lpm_info.nh_grp_key.neigh[0].addr = + fc->key.addr; + fc->lpm_info.nh_grp_key.neigh[0].rif = + nhc->nhc_dev; + + break; + } + + /* We can also get nh_grp_key from fi. This will be correct to + * because cache not always represent, what actually written to + * lpm. But we use nh cache, as well for now (for this case). + */ + for (nh_cnt = 0; nh_cnt < PRESTERA_NHGR_SIZE_MAX; nh_cnt++) { + if (!fc->kern_neigh_cache_head[nh_cnt].n_cache) + break; + + fc->lpm_info.nh_grp_key.neigh[nh_cnt].addr = + fc->kern_neigh_cache_head[nh_cnt].n_cache->key.addr; + fc->lpm_info.nh_grp_key.neigh[nh_cnt].rif = + fc->kern_neigh_cache_head[nh_cnt].n_cache->key.dev; + } + + fc->lpm_info.fib_type = nh_cnt ? + PRESTERA_FIB_TYPE_UC_NH : + PRESTERA_FIB_TYPE_TRAP; break; /* Unsupported. Leave it for kernel: */ case RTN_BROADCAST: @@ -191,7 +940,8 @@ static int __prestera_k_arb_f_lpm_set(struct prestera_switch *sw, return 0; fib_node = prestera_fib_node_create(sw, &fc->lpm_info.fib_key, - fc->lpm_info.fib_type); + fc->lpm_info.fib_type, + &fc->lpm_info.nh_grp_key); if (!fib_node) { dev_err(sw->dev->dev, "fib_node=NULL %pI4n/%d kern_tb_id = %d", @@ -220,6 +970,10 @@ static int __prestera_k_arb_fc_apply(struct prestera_switch *sw, } switch (fc->lpm_info.fib_type) { + case PRESTERA_FIB_TYPE_UC_NH: + __prestera_k_arb_fib_lpm_offload_set(sw, fc, false, + fc->reachable, false); + break; case PRESTERA_FIB_TYPE_TRAP: __prestera_k_arb_fib_lpm_offload_set(sw, fc, false, false, fc->reachable); @@ -271,17 +1025,140 @@ __prestera_k_arb_util_fib_overlapped(struct prestera_switch *sw, return rfc; } +static void __prestera_k_arb_hw_state_upd(struct prestera_switch *sw, + struct prestera_kern_neigh_cache *nc) +{ + struct prestera_nh_neigh_key nh_key; + struct prestera_nh_neigh *nh_neigh; + struct neighbour *n; + bool hw_active; + + prestera_util_nc_key2nh_key(&nc->key, &nh_key); + nh_neigh = prestera_nh_neigh_find(sw, &nh_key); + if (!nh_neigh) { + pr_err("Cannot find nh_neigh for cached %pI4n", + &nc->key.addr.u.ipv4); + return; + } + + hw_active = prestera_nh_neigh_util_hw_state(sw, nh_neigh); + +#ifdef PRESTERA_IMPLICITY_RESOLVE_DEAD_NEIGH + if (!hw_active && nc->in_kernel) + goto out; +#else /* PRESTERA_IMPLICITY_RESOLVE_DEAD_NEIGH */ + if (!hw_active) + goto out; +#endif /* PRESTERA_IMPLICITY_RESOLVE_DEAD_NEIGH */ + + if (nc->key.addr.v == PRESTERA_IPV4) { + n = neigh_lookup(&arp_tbl, &nc->key.addr.u.ipv4, + nc->key.dev); + if (!n) + n = neigh_create(&arp_tbl, &nc->key.addr.u.ipv4, + nc->key.dev); + } else { + n = NULL; + } + + if (!IS_ERR(n) && n) { + neigh_event_send(n, NULL); + neigh_release(n); + } else { + pr_err("Cannot create neighbour %pI4n", &nc->key.addr.u.ipv4); + } + +out: + return; +} + +/* Propagate hw state to kernel */ +static void prestera_k_arb_hw_evt(struct prestera_switch *sw) +{ + struct prestera_kern_neigh_cache *n_cache; + struct rhashtable_iter iter; + + rhashtable_walk_enter(&sw->router->kern_neigh_cache_ht, &iter); + rhashtable_walk_start(&iter); + while (1) { + n_cache = rhashtable_walk_next(&iter); + + if (!n_cache) + break; + + if (IS_ERR(n_cache)) + continue; + + rhashtable_walk_stop(&iter); + __prestera_k_arb_hw_state_upd(sw, n_cache); + rhashtable_walk_start(&iter); + } + rhashtable_walk_stop(&iter); + rhashtable_walk_exit(&iter); +} + +/* Propagate kernel event to hw */ +static void prestera_k_arb_n_evt(struct prestera_switch *sw, + struct neighbour *n) +{ + struct prestera_kern_neigh_cache_key n_key; + struct prestera_kern_neigh_cache *n_cache; + int err; + + err = prestera_util_neigh2nc_key(sw, n, &n_key); + if (err) + return; + + n_cache = prestera_kern_neigh_cache_find(sw, &n_key); + if (!n_cache) { + n_cache = prestera_kern_neigh_cache_get(sw, &n_key); + if (!n_cache) + return; + __prestera_k_arb_nc_kern_fib_fetch(sw, n_cache); + } + + __prestera_k_arb_nc_kern_n_fetch(sw, n_cache); + __prestera_k_arb_nc_apply(sw, n_cache); + + prestera_kern_neigh_cache_put(sw, n_cache); +} + +static void __prestera_k_arb_fib_evt2nc(struct prestera_switch *sw) +{ + struct prestera_kern_neigh_cache *n_cache; + struct rhashtable_iter iter; + + rhashtable_walk_enter(&sw->router->kern_neigh_cache_ht, &iter); + rhashtable_walk_start(&iter); + while (1) { + n_cache = rhashtable_walk_next(&iter); + + if (!n_cache) + break; + + if (IS_ERR(n_cache)) + continue; + + rhashtable_walk_stop(&iter); + __prestera_k_arb_nc_kern_fib_fetch(sw, n_cache); + __prestera_k_arb_nc_apply(sw, n_cache); + rhashtable_walk_start(&iter); + } + rhashtable_walk_stop(&iter); + rhashtable_walk_exit(&iter); +} + static int prestera_k_arb_fib_evt(struct prestera_switch *sw, bool replace, /* replace or del */ - struct fib_entry_notifier_info *fen_info) + struct fib_notifier_info *info) { struct prestera_kern_fib_cache *tfib_cache, *bfib_cache; /* top/btm */ struct prestera_kern_fib_cache_key fc_key; struct prestera_kern_fib_cache *fib_cache; int err; - prestera_util_fen_info2fib_cache_key(fen_info, &fc_key); + prestera_util_fen_info2fib_cache_key(info, &fc_key); fib_cache = prestera_kern_fib_cache_find(sw, &fc_key); if (fib_cache) { fib_cache->reachable = false; @@ -304,10 +1181,7 @@ prestera_k_arb_fib_evt(struct prestera_switch *sw, } if (replace) { - fib_cache = prestera_kern_fib_cache_create(sw, &fc_key, - fen_info->fi, - fen_info->dscp, - fen_info->type); + fib_cache = prestera_kern_fib_cache_create(sw, &fc_key, info); if (!fib_cache) { dev_err(sw->dev->dev, "fib_cache == NULL"); return -ENOENT; @@ -331,9 +1205,65 @@ prestera_k_arb_fib_evt(struct prestera_switch *sw, dev_err(sw->dev->dev, "Applying fib_cache failed"); } + /* Update all neighs to resolve overlapped and apply related */ + __prestera_k_arb_fib_evt2nc(sw); + return 0; } +static void __prestera_k_arb_abort_neigh_ht_cb(void *ptr, void *arg) +{ + struct prestera_kern_neigh_cache *n_cache = ptr; + struct prestera_switch *sw = arg; + + if (!list_empty(&n_cache->kern_fib_cache_list)) { + WARN_ON(1); /* BUG */ + return; + } + __prestera_k_arb_n_offload_set(sw, n_cache, false); + n_cache->in_kernel = false; + /* No need to destroy lpm. + * It will be aborted by destroy_ht + */ + __prestera_kern_neigh_cache_destruct(sw, n_cache); + kfree(n_cache); +} + +static void __prestera_k_arb_abort_fib_ht_cb(void *ptr, void *arg) +{ + struct prestera_kern_fib_cache *fib_cache = ptr; + struct prestera_switch *sw = arg; + + __prestera_k_arb_fib_lpm_offload_set(sw, fib_cache, + false, false, + false); + __prestera_k_arb_fib_nh_offload_set(sw, fib_cache, NULL, + false, false); + /* No need to destroy lpm. + * It will be aborted by destroy_ht + */ + __prestera_kern_fib_cache_destruct(sw, fib_cache); + kfree(fib_cache); +} + +static void prestera_k_arb_abort(struct prestera_switch *sw) +{ + /* Function to remove all arbiter entries and related hw objects. */ + /* Sequence: + * 1) Clear arbiter tables, but don't touch hw + * 2) Clear hw + * We use such approach, because arbiter object is not directly mapped + * to hw. So deletion of one arbiter object may even lead to creation of + * hw object (e.g. in case of overlapped routes). + */ + rhashtable_free_and_destroy(&sw->router->kern_fib_cache_ht, + __prestera_k_arb_abort_fib_ht_cb, + sw); + rhashtable_free_and_destroy(&sw->router->kern_neigh_cache_ht, + __prestera_k_arb_abort_neigh_ht_cb, + sw); +} + static int __prestera_inetaddr_port_event(struct net_device *port_dev, unsigned long event, struct netlink_ext_ack *extack) @@ -469,13 +1399,15 @@ static void __prestera_router_fib_event_work(struct work_struct *work) switch (fib_work->event) { case FIB_EVENT_ENTRY_REPLACE: - err = prestera_k_arb_fib_evt(sw, true, &fib_work->fen_info); + err = prestera_k_arb_fib_evt(sw, true, + &fib_work->fen_info.info); if (err) goto err_out; break; case FIB_EVENT_ENTRY_DEL: - err = prestera_k_arb_fib_evt(sw, false, &fib_work->fen_info); + err = prestera_k_arb_fib_evt(sw, false, + &fib_work->fen_info.info); if (err) goto err_out; @@ -534,10 +1466,89 @@ static int __prestera_router_fib_event(struct notifier_block *nb, return NOTIFY_DONE; } +struct prestera_netevent_work { + struct work_struct work; + struct prestera_switch *sw; + struct neighbour *n; +}; + +static void prestera_router_neigh_event_work(struct work_struct *work) +{ + struct prestera_netevent_work *net_work = + container_of(work, struct prestera_netevent_work, work); + struct prestera_switch *sw = net_work->sw; + struct neighbour *n = net_work->n; + + /* neigh - its not hw related object. It stored only in kernel. So... */ + rtnl_lock(); + + prestera_k_arb_n_evt(sw, n); + + neigh_release(n); + rtnl_unlock(); + kfree(net_work); +} + +static int prestera_router_netevent_event(struct notifier_block *nb, + unsigned long event, void *ptr) +{ + struct prestera_netevent_work *net_work; + struct prestera_router *router; + struct neighbour *n = ptr; + + router = container_of(nb, struct prestera_router, netevent_nb); + + switch (event) { + case NETEVENT_NEIGH_UPDATE: + if (n->tbl->family != AF_INET) + return NOTIFY_DONE; + + net_work = kzalloc(sizeof(*net_work), GFP_ATOMIC); + if (WARN_ON(!net_work)) + return NOTIFY_BAD; + + neigh_clone(n); + net_work->n = n; + net_work->sw = router->sw; + INIT_WORK(&net_work->work, prestera_router_neigh_event_work); + prestera_queue_work(&net_work->work); + } + + return NOTIFY_DONE; +} + +static void prestera_router_update_neighs_work(struct work_struct *work) +{ + struct prestera_router *router; + + router = container_of(work, struct prestera_router, + neighs_update.dw.work); + rtnl_lock(); + + prestera_k_arb_hw_evt(router->sw); + + rtnl_unlock(); + prestera_queue_delayed_work(&router->neighs_update.dw, + msecs_to_jiffies(PRESTERA_NH_PROBE_INTERVAL)); +} + +static int prestera_neigh_work_init(struct prestera_switch *sw) +{ + INIT_DELAYED_WORK(&sw->router->neighs_update.dw, + prestera_router_update_neighs_work); + prestera_queue_delayed_work(&sw->router->neighs_update.dw, 0); + return 0; +} + +static void prestera_neigh_work_fini(struct prestera_switch *sw) +{ + cancel_delayed_work_sync(&sw->router->neighs_update.dw); +} + int prestera_router_init(struct prestera_switch *sw) { struct prestera_router *router; - int err; + int err, nhgrp_cache_bytes; router = kzalloc(sizeof(*sw->router), GFP_KERNEL); if (!router) @@ -555,6 +1566,22 @@ int prestera_router_init(struct prestera_switch *sw) if (err) goto err_kern_fib_cache_ht_init; + err = rhashtable_init(&router->kern_neigh_cache_ht, + &__prestera_kern_neigh_cache_ht_params); + if (err) + goto err_kern_neigh_cache_ht_init; + + nhgrp_cache_bytes = sw->size_tbl_router_nexthop / 8 + 1; + router->nhgrp_hw_state_cache = kzalloc(nhgrp_cache_bytes, GFP_KERNEL); + if (!router->nhgrp_hw_state_cache) { + err = -ENOMEM; + goto err_nh_state_cache_alloc; + } + + err = prestera_neigh_work_init(sw); + if (err) + goto err_neigh_work_init; + router->inetaddr_valid_nb.notifier_call = __prestera_inetaddr_valid_cb; err = register_inetaddr_validator_notifier(&router->inetaddr_valid_nb); if (err) @@ -565,6 +1592,11 @@ int prestera_router_init(struct prestera_switch *sw) if (err) goto err_register_inetaddr_notifier; + router->netevent_nb.notifier_call = prestera_router_netevent_event; + err = register_netevent_notifier(&router->netevent_nb); + if (err) + goto err_register_netevent_notifier; + router->fib_nb.notifier_call = __prestera_router_fib_event; err = register_fib_notifier(&init_net, &router->fib_nb, /* TODO: flush fib entries */ NULL, NULL); @@ -574,10 +1606,18 @@ int prestera_router_init(struct prestera_switch *sw) return 0; err_register_fib_notifier: + unregister_netevent_notifier(&router->netevent_nb); +err_register_netevent_notifier: unregister_inetaddr_notifier(&router->inetaddr_nb); err_register_inetaddr_notifier: unregister_inetaddr_validator_notifier(&router->inetaddr_valid_nb); err_register_inetaddr_validator_notifier: + prestera_neigh_work_fini(sw); +err_neigh_work_init: + kfree(router->nhgrp_hw_state_cache); +err_nh_state_cache_alloc: + rhashtable_destroy(&router->kern_neigh_cache_ht); +err_kern_neigh_cache_ht_init: rhashtable_destroy(&router->kern_fib_cache_ht); err_kern_fib_cache_ht_init: prestera_router_hw_fini(sw); @@ -589,8 +1629,15 @@ err_router_lib_init: void prestera_router_fini(struct prestera_switch *sw) { unregister_fib_notifier(&init_net, &sw->router->fib_nb); + unregister_netevent_notifier(&sw->router->netevent_nb); unregister_inetaddr_notifier(&sw->router->inetaddr_nb); unregister_inetaddr_validator_notifier(&sw->router->inetaddr_valid_nb); + prestera_neigh_work_fini(sw); + prestera_queue_drain(); + + prestera_k_arb_abort(sw); + + kfree(sw->router->nhgrp_hw_state_cache); rhashtable_destroy(&sw->router->kern_fib_cache_ht); prestera_router_hw_fini(sw); kfree(sw->router); diff --git a/drivers/net/ethernet/marvell/prestera/prestera_router_hw.c b/drivers/net/ethernet/marvell/prestera/prestera_router_hw.c index 5b0cf3be9a9e..4f65df0ae5e8 100644 --- a/drivers/net/ethernet/marvell/prestera/prestera_router_hw.c +++ b/drivers/net/ethernet/marvell/prestera/prestera_router_hw.c @@ -8,10 +8,16 @@ #include "prestera_router_hw.h" #include "prestera_acl.h" -/* +--+ - * +------->|vr|<-+ - * | +--+ | - * | | +/* Nexthop is pointed + * to port (not rif) + * +-------+ + * +>|nexthop| + * | +-------+ + * | + * +--+ +-----++ + * +------->|vr|<-+ +>|nh_grp| + * | +--+ | | +------+ + * | | | * +-+-------+ +--+---+-+ * |rif_entry| |fib_node| * +---------+ +--------+ @@ -23,6 +29,8 @@ #define PRESTERA_NHGR_UNUSED (0) #define PRESTERA_NHGR_DROP (0xFFFFFFFF) +/* Need to merge it with router_manager */ +#define PRESTERA_NH_ACTIVE_JIFFER_FILTER 3000 /* ms */ static const struct rhashtable_params __prestera_fib_ht_params = { .key_offset = offsetof(struct prestera_fib_node, key), @@ -31,10 +39,45 @@ static const struct rhashtable_params __prestera_fib_ht_params = { .automatic_shrinking = true, }; +static const struct rhashtable_params __prestera_nh_neigh_ht_params = { + .key_offset = offsetof(struct prestera_nh_neigh, key), + .key_len = sizeof(struct prestera_nh_neigh_key), + .head_offset = offsetof(struct prestera_nh_neigh, ht_node), +}; + +static const struct rhashtable_params __prestera_nexthop_group_ht_params = { + .key_offset = offsetof(struct prestera_nexthop_group, key), + .key_len = sizeof(struct prestera_nexthop_group_key), + .head_offset = offsetof(struct prestera_nexthop_group, ht_node), +}; + +static int prestera_nexthop_group_set(struct prestera_switch *sw, + struct prestera_nexthop_group *nh_grp); +static bool +prestera_nexthop_group_util_hw_state(struct prestera_switch *sw, + struct prestera_nexthop_group *nh_grp); +static void prestera_fib_node_destroy_ht_cb(void *ptr, void *arg); + +/* TODO: move to router.h as macros */ +static bool prestera_nh_neigh_key_is_valid(struct prestera_nh_neigh_key *key) +{ + return memchr_inv(key, 0, sizeof(*key)) ? true : false; +} + int prestera_router_hw_init(struct prestera_switch *sw) { int err; + err = rhashtable_init(&sw->router->nh_neigh_ht, + &__prestera_nh_neigh_ht_params); + if (err) + goto err_nh_neigh_ht_init; + + err = rhashtable_init(&sw->router->nexthop_group_ht, + &__prestera_nexthop_group_ht_params); + if (err) + goto err_nexthop_grp_ht_init; + err = rhashtable_init(&sw->router->fib_ht, &__prestera_fib_ht_params); if (err) @@ -43,15 +86,25 @@ int prestera_router_hw_init(struct prestera_switch *sw) INIT_LIST_HEAD(&sw->router->vr_list); INIT_LIST_HEAD(&sw->router->rif_entry_list); + return 0; + err_fib_ht_init: + rhashtable_destroy(&sw->router->nexthop_group_ht); +err_nexthop_grp_ht_init: + rhashtable_destroy(&sw->router->nh_neigh_ht); +err_nh_neigh_ht_init: return 0; } void prestera_router_hw_fini(struct prestera_switch *sw) { + rhashtable_free_and_destroy(&sw->router->fib_ht, + prestera_fib_node_destroy_ht_cb, sw); WARN_ON(!list_empty(&sw->router->vr_list)); WARN_ON(!list_empty(&sw->router->rif_entry_list)); rhashtable_destroy(&sw->router->fib_ht); + rhashtable_destroy(&sw->router->nexthop_group_ht); + rhashtable_destroy(&sw->router->nh_neigh_ht); } static struct prestera_vr *__prestera_vr_find(struct prestera_switch *sw, @@ -232,6 +285,286 @@ err_kzalloc: return NULL; } +static void __prestera_nh_neigh_destroy(struct prestera_switch *sw, + struct prestera_nh_neigh *neigh) +{ + rhashtable_remove_fast(&sw->router->nh_neigh_ht, + &neigh->ht_node, + __prestera_nh_neigh_ht_params); + kfree(neigh); +} + +static struct prestera_nh_neigh * +__prestera_nh_neigh_create(struct prestera_switch *sw, + struct prestera_nh_neigh_key *key) +{ + struct prestera_nh_neigh *neigh; + int err; + + neigh = kzalloc(sizeof(*neigh), GFP_KERNEL); + if (!neigh) + goto err_kzalloc; + + memcpy(&neigh->key, key, sizeof(*key)); + neigh->info.connected = false; + INIT_LIST_HEAD(&neigh->nexthop_group_list); + err = rhashtable_insert_fast(&sw->router->nh_neigh_ht, + &neigh->ht_node, + __prestera_nh_neigh_ht_params); + if (err) + goto err_rhashtable_insert; + + return neigh; + +err_rhashtable_insert: + kfree(neigh); +err_kzalloc: + return NULL; +} + +struct prestera_nh_neigh * +prestera_nh_neigh_find(struct prestera_switch *sw, + struct prestera_nh_neigh_key *key) +{ + struct prestera_nh_neigh *nh_neigh; + + nh_neigh = rhashtable_lookup_fast(&sw->router->nh_neigh_ht, + key, __prestera_nh_neigh_ht_params); + return IS_ERR(nh_neigh) ? NULL : nh_neigh; +} + +struct prestera_nh_neigh * +prestera_nh_neigh_get(struct prestera_switch *sw, + struct prestera_nh_neigh_key *key) +{ + struct prestera_nh_neigh *neigh; + + neigh = prestera_nh_neigh_find(sw, key); + if (!neigh) + return __prestera_nh_neigh_create(sw, key); + + return neigh; +} + +void prestera_nh_neigh_put(struct prestera_switch *sw, + struct prestera_nh_neigh *neigh) +{ + if (list_empty(&neigh->nexthop_group_list)) + __prestera_nh_neigh_destroy(sw, neigh); +} + +/* Updates new prestera_neigh_info */ +int prestera_nh_neigh_set(struct prestera_switch *sw, + struct prestera_nh_neigh *neigh) +{ + struct prestera_nh_neigh_head *nh_head; + struct prestera_nexthop_group *nh_grp; + int err; + + list_for_each_entry(nh_head, &neigh->nexthop_group_list, head) { + nh_grp = nh_head->this; + err = prestera_nexthop_group_set(sw, nh_grp); + if (err) + return err; + } + + return 0; +} + +bool prestera_nh_neigh_util_hw_state(struct prestera_switch *sw, + struct prestera_nh_neigh *nh_neigh) +{ + bool state; + struct prestera_nh_neigh_head *nh_head, *tmp; + + state = false; + list_for_each_entry_safe(nh_head, tmp, + &nh_neigh->nexthop_group_list, head) { + state = prestera_nexthop_group_util_hw_state(sw, nh_head->this); + if (state) + goto out; + } + +out: + return state; +} + +static struct prestera_nexthop_group * +__prestera_nexthop_group_create(struct prestera_switch *sw, + struct prestera_nexthop_group_key *key) +{ + struct prestera_nexthop_group *nh_grp; + struct prestera_nh_neigh *nh_neigh; + int nh_cnt, err, gid; + + nh_grp = kzalloc(sizeof(*nh_grp), GFP_KERNEL); + if (!nh_grp) + goto err_kzalloc; + + memcpy(&nh_grp->key, key, sizeof(*key)); + for (nh_cnt = 0; nh_cnt < PRESTERA_NHGR_SIZE_MAX; nh_cnt++) { + if (!prestera_nh_neigh_key_is_valid(&nh_grp->key.neigh[nh_cnt])) + break; + + nh_neigh = prestera_nh_neigh_get(sw, + &nh_grp->key.neigh[nh_cnt]); + if (!nh_neigh) + goto err_nh_neigh_get; + + nh_grp->nh_neigh_head[nh_cnt].neigh = nh_neigh; + nh_grp->nh_neigh_head[nh_cnt].this = nh_grp; + list_add(&nh_grp->nh_neigh_head[nh_cnt].head, + &nh_neigh->nexthop_group_list); + } + + err = prestera_hw_nh_group_create(sw, nh_cnt, &nh_grp->grp_id); + if (err) + goto err_nh_group_create; + + err = prestera_nexthop_group_set(sw, nh_grp); + if (err) + goto err_nexthop_group_set; + + err = rhashtable_insert_fast(&sw->router->nexthop_group_ht, + &nh_grp->ht_node, + __prestera_nexthop_group_ht_params); + if (err) + goto err_ht_insert; + + /* reset cache for created group */ + gid = nh_grp->grp_id; + sw->router->nhgrp_hw_state_cache[gid / 8] &= ~BIT(gid % 8); + + return nh_grp; + +err_ht_insert: +err_nexthop_group_set: + prestera_hw_nh_group_delete(sw, nh_cnt, nh_grp->grp_id); +err_nh_group_create: +err_nh_neigh_get: + for (nh_cnt--; nh_cnt >= 0; nh_cnt--) { + list_del(&nh_grp->nh_neigh_head[nh_cnt].head); + prestera_nh_neigh_put(sw, nh_grp->nh_neigh_head[nh_cnt].neigh); + } + + kfree(nh_grp); +err_kzalloc: + return NULL; +} + +static void +__prestera_nexthop_group_destroy(struct prestera_switch *sw, + struct prestera_nexthop_group *nh_grp) +{ + struct prestera_nh_neigh *nh_neigh; + int nh_cnt; + + rhashtable_remove_fast(&sw->router->nexthop_group_ht, + &nh_grp->ht_node, + __prestera_nexthop_group_ht_params); + + for (nh_cnt = 0; nh_cnt < PRESTERA_NHGR_SIZE_MAX; nh_cnt++) { + nh_neigh = nh_grp->nh_neigh_head[nh_cnt].neigh; + if (!nh_neigh) + break; + + list_del(&nh_grp->nh_neigh_head[nh_cnt].head); + prestera_nh_neigh_put(sw, nh_neigh); + } + + prestera_hw_nh_group_delete(sw, nh_cnt, nh_grp->grp_id); + kfree(nh_grp); +} + +static struct prestera_nexthop_group * +__prestera_nexthop_group_find(struct prestera_switch *sw, + struct prestera_nexthop_group_key *key) +{ + struct prestera_nexthop_group *nh_grp; + + nh_grp = rhashtable_lookup_fast(&sw->router->nexthop_group_ht, + key, __prestera_nexthop_group_ht_params); + return IS_ERR(nh_grp) ? NULL : nh_grp; +} + +static struct prestera_nexthop_group * +prestera_nexthop_group_get(struct prestera_switch *sw, + struct prestera_nexthop_group_key *key) +{ + struct prestera_nexthop_group *nh_grp; + + nh_grp = __prestera_nexthop_group_find(sw, key); + if (nh_grp) { + refcount_inc(&nh_grp->refcount); + } else { + nh_grp = __prestera_nexthop_group_create(sw, key); + if (IS_ERR(nh_grp)) + return ERR_CAST(nh_grp); + + refcount_set(&nh_grp->refcount, 1); + } + + return nh_grp; +} + +static void prestera_nexthop_group_put(struct prestera_switch *sw, + struct prestera_nexthop_group *nh_grp) +{ + if (refcount_dec_and_test(&nh_grp->refcount)) + __prestera_nexthop_group_destroy(sw, nh_grp); +} + +/* Updates with new nh_neigh's info */ +static int prestera_nexthop_group_set(struct prestera_switch *sw, + struct prestera_nexthop_group *nh_grp) +{ + struct prestera_neigh_info info[PRESTERA_NHGR_SIZE_MAX]; + struct prestera_nh_neigh *neigh; + int nh_cnt; + + memset(&info[0], 0, sizeof(info)); + for (nh_cnt = 0; nh_cnt < PRESTERA_NHGR_SIZE_MAX; nh_cnt++) { + neigh = nh_grp->nh_neigh_head[nh_cnt].neigh; + if (!neigh) + break; + + memcpy(&info[nh_cnt], &neigh->info, sizeof(neigh->info)); + } + + return prestera_hw_nh_entries_set(sw, nh_cnt, &info[0], nh_grp->grp_id); +} + +static bool +prestera_nexthop_group_util_hw_state(struct prestera_switch *sw, + struct prestera_nexthop_group *nh_grp) +{ + int err; + u32 buf_size = sw->size_tbl_router_nexthop / 8 + 1; + u32 gid = nh_grp->grp_id; + u8 *cache = sw->router->nhgrp_hw_state_cache; + + /* Antijitter + * Prevent situation, when we read state of nh_grp twice in short time, + * and state bit is still cleared on second call. So just stuck active + * state for PRESTERA_NH_ACTIVE_JIFFER_FILTER, after last occurred. + */ + if (!time_before(jiffies, sw->router->nhgrp_hw_cache_kick + + msecs_to_jiffies(PRESTERA_NH_ACTIVE_JIFFER_FILTER))) { + err = prestera_hw_nhgrp_blk_get(sw, cache, buf_size); + if (err) { + pr_err("Failed to get hw state nh_grp's"); + return false; + } + + sw->router->nhgrp_hw_cache_kick = jiffies; + } + + if (cache[gid / 8] & BIT(gid % 8)) + return true; + + return false; +} + struct prestera_fib_node * prestera_fib_node_find(struct prestera_switch *sw, struct prestera_fib_key *key) { @@ -251,6 +584,9 @@ static void __prestera_fib_node_destruct(struct prestera_switch *sw, prestera_hw_lpm_del(sw, vr->hw_vr_id, fib_node->key.addr.u.ipv4, fib_node->key.prefix_len); switch (fib_node->info.type) { + case PRESTERA_FIB_TYPE_UC_NH: + prestera_nexthop_group_put(sw, fib_node->info.nh_grp); + break; case PRESTERA_FIB_TYPE_TRAP: break; case PRESTERA_FIB_TYPE_DROP: @@ -272,10 +608,20 @@ void prestera_fib_node_destroy(struct prestera_switch *sw, kfree(fib_node); } +static void prestera_fib_node_destroy_ht_cb(void *ptr, void *arg) +{ + struct prestera_fib_node *node = ptr; + struct prestera_switch *sw = arg; + + __prestera_fib_node_destruct(sw, node); + kfree(node); +} + struct prestera_fib_node * prestera_fib_node_create(struct prestera_switch *sw, struct prestera_fib_key *key, - enum prestera_fib_type fib_type) + enum prestera_fib_type fib_type, + struct prestera_nexthop_group_key *nh_grp_key) { struct prestera_fib_node *fib_node; u32 grp_id; @@ -302,6 +648,14 @@ prestera_fib_node_create(struct prestera_switch *sw, case PRESTERA_FIB_TYPE_DROP: grp_id = PRESTERA_NHGR_DROP; break; + case PRESTERA_FIB_TYPE_UC_NH: + fib_node->info.nh_grp = prestera_nexthop_group_get(sw, + nh_grp_key); + if (!fib_node->info.nh_grp) + goto err_nh_grp_get; + + grp_id = fib_node->info.nh_grp->grp_id; + break; default: pr_err("Unsupported fib_type %d", fib_type); goto err_nh_grp_get; @@ -323,6 +677,8 @@ err_ht_insert: prestera_hw_lpm_del(sw, vr->hw_vr_id, key->addr.u.ipv4, key->prefix_len); err_lpm_add: + if (fib_type == PRESTERA_FIB_TYPE_UC_NH) + prestera_nexthop_group_put(sw, fib_node->info.nh_grp); err_nh_grp_get: prestera_vr_put(sw, vr); err_vr_get: diff --git a/drivers/net/ethernet/marvell/prestera/prestera_router_hw.h b/drivers/net/ethernet/marvell/prestera/prestera_router_hw.h index 67dbb49c8bd4..9ca97919c863 100644 --- a/drivers/net/ethernet/marvell/prestera/prestera_router_hw.h +++ b/drivers/net/ethernet/marvell/prestera/prestera_router_hw.h @@ -31,6 +31,63 @@ struct prestera_ip_addr { PRESTERA_IPV4 = 0, PRESTERA_IPV6 } v; +#define PRESTERA_IP_ADDR_PLEN(V) ((V) == PRESTERA_IPV4 ? 32 : \ + /* (V) == PRESTERA_IPV6 ? */ 128 /* : 0 */) +}; + +struct prestera_nh_neigh_key { + struct prestera_ip_addr addr; + /* Seems like rif is obsolete, because there is iface in info ? + * Key can contain functional fields, or fields, which is used to + * filter duplicate objects on logical level (before you pass it to + * HW)... also key can be used to cover hardware restrictions. + * In our case rif - is logical interface (even can be VLAN), which + * is used in combination with IP address (which is also not related to + * hardware nexthop) to provide logical compression of created nexthops. + * You even can imagine, that rif+IPaddr is just cookie. + */ + /* struct prestera_rif *rif; */ + /* Use just as cookie, to divide ARP domains (in order with addr) */ + void *rif; +}; + +/* Used for hw call */ +struct prestera_neigh_info { + struct prestera_iface iface; + unsigned char ha[ETH_ALEN]; + u8 connected; /* bool. indicate, if mac/oif valid */ + u8 __pad[1]; +}; + +/* Used to notify nh about neigh change */ +struct prestera_nh_neigh { + struct prestera_nh_neigh_key key; + struct prestera_neigh_info info; + struct rhash_head ht_node; /* node of prestera_vr */ + struct list_head nexthop_group_list; +}; + +#define PRESTERA_NHGR_SIZE_MAX 4 + +struct prestera_nexthop_group { + struct prestera_nexthop_group_key { + struct prestera_nh_neigh_key neigh[PRESTERA_NHGR_SIZE_MAX]; + } key; + /* Store intermediate object here. + * This prevent overhead kzalloc call. + */ + /* nh_neigh is used only to notify nexthop_group */ + struct prestera_nh_neigh_head { + struct prestera_nexthop_group *this; + struct list_head head; + /* ptr to neigh is not necessary. + * It used to prevent lookup of nh_neigh by key (n) on destroy + */ + struct prestera_nh_neigh *neigh; + } nh_neigh_head[PRESTERA_NHGR_SIZE_MAX]; + struct rhash_head ht_node; /* node of prestera_vr */ + refcount_t refcount; + u32 grp_id; /* hw */ }; struct prestera_fib_key { @@ -44,12 +101,16 @@ struct prestera_fib_info { struct list_head vr_node; enum prestera_fib_type { PRESTERA_FIB_TYPE_INVALID = 0, + /* must be pointer to nh_grp id */ + PRESTERA_FIB_TYPE_UC_NH, /* It can be connected route * and will be overlapped with neighbours */ PRESTERA_FIB_TYPE_TRAP, PRESTERA_FIB_TYPE_DROP } type; + /* Valid only if type = UC_NH*/ + struct prestera_nexthop_group *nh_grp; }; struct prestera_fib_node { @@ -67,6 +128,18 @@ struct prestera_rif_entry * prestera_rif_entry_create(struct prestera_switch *sw, struct prestera_rif_entry_key *k, u32 tb_id, const unsigned char *addr); +struct prestera_nh_neigh * +prestera_nh_neigh_find(struct prestera_switch *sw, + struct prestera_nh_neigh_key *key); +struct prestera_nh_neigh * +prestera_nh_neigh_get(struct prestera_switch *sw, + struct prestera_nh_neigh_key *key); +void prestera_nh_neigh_put(struct prestera_switch *sw, + struct prestera_nh_neigh *neigh); +int prestera_nh_neigh_set(struct prestera_switch *sw, + struct prestera_nh_neigh *neigh); +bool prestera_nh_neigh_util_hw_state(struct prestera_switch *sw, + struct prestera_nh_neigh *nh_neigh); struct prestera_fib_node *prestera_fib_node_find(struct prestera_switch *sw, struct prestera_fib_key *key); void prestera_fib_node_destroy(struct prestera_switch *sw, @@ -74,7 +147,8 @@ void prestera_fib_node_destroy(struct prestera_switch *sw, struct prestera_fib_node * prestera_fib_node_create(struct prestera_switch *sw, struct prestera_fib_key *key, - enum prestera_fib_type fib_type); + enum prestera_fib_type fib_type, + struct prestera_nexthop_group_key *nh_grp_key); int prestera_router_hw_init(struct prestera_switch *sw); void prestera_router_hw_fini(struct prestera_switch *sw); diff --git a/drivers/net/ethernet/marvell/prestera/prestera_rxtx.c b/drivers/net/ethernet/marvell/prestera/prestera_rxtx.c index dc3e3ddc60bf..42ee963e9f75 100644 --- a/drivers/net/ethernet/marvell/prestera/prestera_rxtx.c +++ b/drivers/net/ethernet/marvell/prestera/prestera_rxtx.c @@ -659,7 +659,7 @@ static int prestera_sdma_switch_init(struct prestera_switch *sw) init_dummy_netdev(&sdma->napi_dev); - netif_napi_add(&sdma->napi_dev, &sdma->rx_napi, prestera_sdma_rx_poll, 64); + netif_napi_add(&sdma->napi_dev, &sdma->rx_napi, prestera_sdma_rx_poll); napi_enable(&sdma->rx_napi); return 0; diff --git a/drivers/net/ethernet/marvell/prestera/prestera_span.c b/drivers/net/ethernet/marvell/prestera/prestera_span.c index 845e9d8c8cc7..f0e9d6ea88c5 100644 --- a/drivers/net/ethernet/marvell/prestera/prestera_span.c +++ b/drivers/net/ethernet/marvell/prestera/prestera_span.c @@ -120,8 +120,9 @@ static int prestera_span_put(struct prestera_switch *sw, u8 span_id) return 0; } -static int prestera_span_rule_add(struct prestera_flow_block_binding *binding, - struct prestera_port *to_port) +int prestera_span_rule_add(struct prestera_flow_block_binding *binding, + struct prestera_port *to_port, + bool ingress) { struct prestera_switch *sw = binding->port->sw; u8 span_id; @@ -135,7 +136,7 @@ static int prestera_span_rule_add(struct prestera_flow_block_binding *binding, if (err) return err; - err = prestera_hw_span_bind(binding->port, span_id); + err = prestera_hw_span_bind(binding->port, span_id, ingress); if (err) { prestera_span_put(sw, span_id); return err; @@ -145,11 +146,12 @@ static int prestera_span_rule_add(struct prestera_flow_block_binding *binding, return 0; } -static int prestera_span_rule_del(struct prestera_flow_block_binding *binding) +int prestera_span_rule_del(struct prestera_flow_block_binding *binding, + bool ingress) { int err; - err = prestera_hw_span_unbind(binding->port); + err = prestera_hw_span_unbind(binding->port, ingress); if (err) return err; @@ -161,60 +163,6 @@ static int prestera_span_rule_del(struct prestera_flow_block_binding *binding) return 0; } -int prestera_span_replace(struct prestera_flow_block *block, - struct tc_cls_matchall_offload *f) -{ - struct prestera_flow_block_binding *binding; - __be16 protocol = f->common.protocol; - struct flow_action_entry *act; - struct prestera_port *port; - int err; - - if (!flow_offload_has_one_action(&f->rule->action)) { - NL_SET_ERR_MSG(f->common.extack, - "Only singular actions are supported"); - return -EOPNOTSUPP; - } - - act = &f->rule->action.entries[0]; - - if (!prestera_netdev_check(act->dev)) { - NL_SET_ERR_MSG(f->common.extack, - "Only Marvell Prestera port is supported"); - return -EINVAL; - } - if (!tc_cls_can_offload_and_chain0(act->dev, &f->common)) - return -EOPNOTSUPP; - if (act->id != FLOW_ACTION_MIRRED) - return -EOPNOTSUPP; - if (protocol != htons(ETH_P_ALL)) - return -EOPNOTSUPP; - - port = netdev_priv(act->dev); - - list_for_each_entry(binding, &block->binding_list, list) { - err = prestera_span_rule_add(binding, port); - if (err) - goto rollback; - } - - return 0; - -rollback: - list_for_each_entry_continue_reverse(binding, - &block->binding_list, list) - prestera_span_rule_del(binding); - return err; -} - -void prestera_span_destroy(struct prestera_flow_block *block) -{ - struct prestera_flow_block_binding *binding; - - list_for_each_entry(binding, &block->binding_list, list) - prestera_span_rule_del(binding); -} - int prestera_span_init(struct prestera_switch *sw) { struct prestera_span *span; diff --git a/drivers/net/ethernet/marvell/prestera/prestera_span.h b/drivers/net/ethernet/marvell/prestera/prestera_span.h index f0644521f78a..493b68524bcb 100644 --- a/drivers/net/ethernet/marvell/prestera/prestera_span.h +++ b/drivers/net/ethernet/marvell/prestera/prestera_span.h @@ -8,13 +8,17 @@ #define PRESTERA_SPAN_INVALID_ID -1 +struct prestera_port; struct prestera_switch; -struct prestera_flow_block; +struct prestera_flow_block_binding; int prestera_span_init(struct prestera_switch *sw); void prestera_span_fini(struct prestera_switch *sw); -int prestera_span_replace(struct prestera_flow_block *block, - struct tc_cls_matchall_offload *f); -void prestera_span_destroy(struct prestera_flow_block *block); + +int prestera_span_rule_add(struct prestera_flow_block_binding *binding, + struct prestera_port *to_port, + bool ingress); +int prestera_span_rule_del(struct prestera_flow_block_binding *binding, + bool ingress); #endif /* _PRESTERA_SPAN_H_ */ diff --git a/drivers/net/ethernet/marvell/prestera/prestera_switchdev.c b/drivers/net/ethernet/marvell/prestera/prestera_switchdev.c index 71cde97d85c8..e548cd32582e 100644 --- a/drivers/net/ethernet/marvell/prestera/prestera_switchdev.c +++ b/drivers/net/ethernet/marvell/prestera/prestera_switchdev.c @@ -143,6 +143,7 @@ prestera_br_port_flags_reset(struct prestera_bridge_port *br_port, prestera_port_uc_flood_set(port, false); prestera_port_mc_flood_set(port, false); prestera_port_learning_set(port, false); + prestera_port_br_locked_set(port, false); } static int prestera_br_port_flags_set(struct prestera_bridge_port *br_port, @@ -162,6 +163,11 @@ static int prestera_br_port_flags_set(struct prestera_bridge_port *br_port, if (err) goto err_out; + err = prestera_port_br_locked_set(port, + br_port->flags & BR_PORT_LOCKED); + if (err) + goto err_out; + return 0; err_out: @@ -1163,7 +1169,7 @@ static int prestera_port_obj_attr_set(struct net_device *dev, const void *ctx, break; case SWITCHDEV_ATTR_ID_PORT_PRE_BRIDGE_FLAGS: if (attr->u.brport_flags.mask & - ~(BR_LEARNING | BR_FLOOD | BR_MCAST_FLOOD)) + ~(BR_LEARNING | BR_FLOOD | BR_MCAST_FLOOD | BR_PORT_LOCKED)) err = -EINVAL; break; case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS: diff --git a/drivers/net/ethernet/marvell/pxa168_eth.c b/drivers/net/ethernet/marvell/pxa168_eth.c index 349b8a94e939..cf456d62677f 100644 --- a/drivers/net/ethernet/marvell/pxa168_eth.c +++ b/drivers/net/ethernet/marvell/pxa168_eth.c @@ -1354,10 +1354,10 @@ static void pxa168_eth_netpoll(struct net_device *dev) static void pxa168_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { - strlcpy(info->driver, DRIVER_NAME, sizeof(info->driver)); - strlcpy(info->version, DRIVER_VERSION, sizeof(info->version)); - strlcpy(info->fw_version, "N/A", sizeof(info->fw_version)); - strlcpy(info->bus_info, "N/A", sizeof(info->bus_info)); + strscpy(info->driver, DRIVER_NAME, sizeof(info->driver)); + strscpy(info->version, DRIVER_VERSION, sizeof(info->version)); + strscpy(info->fw_version, "N/A", sizeof(info->fw_version)); + strscpy(info->bus_info, "N/A", sizeof(info->bus_info)); } static const struct ethtool_ops pxa168_ethtool_ops = { diff --git a/drivers/net/ethernet/marvell/skge.c b/drivers/net/ethernet/marvell/skge.c index c1e985416c0e..1b43704baceb 100644 --- a/drivers/net/ethernet/marvell/skge.c +++ b/drivers/net/ethernet/marvell/skge.c @@ -394,9 +394,9 @@ static void skge_get_drvinfo(struct net_device *dev, { struct skge_port *skge = netdev_priv(dev); - strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); - strlcpy(info->version, DRV_VERSION, sizeof(info->version)); - strlcpy(info->bus_info, pci_name(skge->hw->pdev), + strscpy(info->driver, DRV_NAME, sizeof(info->driver)); + strscpy(info->version, DRV_VERSION, sizeof(info->version)); + strscpy(info->bus_info, pci_name(skge->hw->pdev), sizeof(info->bus_info)); } @@ -3832,7 +3832,7 @@ static struct net_device *skge_devinit(struct skge_hw *hw, int port, dev->features |= NETIF_F_HIGHDMA; skge = netdev_priv(dev); - netif_napi_add(dev, &skge->napi, skge_poll, NAPI_POLL_WEIGHT); + netif_napi_add(dev, &skge->napi, skge_poll); skge->netdev = dev; skge->hw = hw; skge->msg_enable = netif_msg_init(debug, default_msg); diff --git a/drivers/net/ethernet/marvell/sky2.c b/drivers/net/ethernet/marvell/sky2.c index bbea5458000b..ab33ba1c3023 100644 --- a/drivers/net/ethernet/marvell/sky2.c +++ b/drivers/net/ethernet/marvell/sky2.c @@ -3687,9 +3687,9 @@ static void sky2_get_drvinfo(struct net_device *dev, { struct sky2_port *sky2 = netdev_priv(dev); - strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); - strlcpy(info->version, DRV_VERSION, sizeof(info->version)); - strlcpy(info->bus_info, pci_name(sky2->hw->pdev), + strscpy(info->driver, DRV_NAME, sizeof(info->driver)); + strscpy(info->version, DRV_VERSION, sizeof(info->version)); + strscpy(info->bus_info, pci_name(sky2->hw->pdev), sizeof(info->bus_info)); } @@ -4937,7 +4937,7 @@ static int sky2_probe(struct pci_dev *pdev, const struct pci_device_id *ent) } } - netif_napi_add(dev, &hw->napi, sky2_poll, NAPI_POLL_WEIGHT); + netif_napi_add(dev, &hw->napi, sky2_poll); err = register_netdev(dev); if (err) { diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c index b344632beadd..4fba7cb0144b 100644 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c @@ -73,6 +73,12 @@ static const struct mtk_reg_map mtk_reg_map = { .fq_blen = 0x1b2c, }, .gdm1_cnt = 0x2400, + .gdma_to_ppe = 0x4444, + .ppe_base = 0x0c00, + .wdma_base = { + [0] = 0x2800, + [1] = 0x2c00, + }, }; static const struct mtk_reg_map mt7628_reg_map = { @@ -126,6 +132,12 @@ static const struct mtk_reg_map mt7986_reg_map = { .fq_blen = 0x472c, }, .gdm1_cnt = 0x1c00, + .gdma_to_ppe = 0x3333, + .ppe_base = 0x2000, + .wdma_base = { + [0] = 0x4800, + [1] = 0x4c00, + }, }; /* strings used by ethtool */ @@ -1573,8 +1585,8 @@ static int mtk_xdp_submit_frame(struct mtk_eth *eth, struct xdp_frame *xdpf, .last = !xdp_frame_has_frags(xdpf), }; int err, index = 0, n_desc = 1, nr_frags; - struct mtk_tx_dma *htxd, *txd, *txd_pdma; struct mtk_tx_buf *htx_buf, *tx_buf; + struct mtk_tx_dma *htxd, *txd; void *data = xdpf->data; if (unlikely(test_bit(MTK_RESETTING, ð->state))) @@ -1608,7 +1620,6 @@ static int mtk_xdp_submit_frame(struct mtk_eth *eth, struct xdp_frame *xdpf, if (MTK_HAS_CAPS(soc->caps, MTK_QDMA) || (index & 0x1)) { txd = mtk_qdma_phys_to_virt(ring, txd->txd2); - txd_pdma = qdma_to_pdma(ring, txd); if (txd == ring->last_free) goto unmap; @@ -1629,7 +1640,8 @@ static int mtk_xdp_submit_frame(struct mtk_eth *eth, struct xdp_frame *xdpf, htx_buf->data = xdpf; if (!MTK_HAS_CAPS(soc->caps, MTK_QDMA)) { - txd_pdma = qdma_to_pdma(ring, txd); + struct mtk_tx_dma *txd_pdma = qdma_to_pdma(ring, txd); + if (index & 1) txd_pdma->txd2 |= TX_DMA_LS0; else @@ -1660,13 +1672,15 @@ static int mtk_xdp_submit_frame(struct mtk_eth *eth, struct xdp_frame *xdpf, unmap: while (htxd != txd) { - txd_pdma = qdma_to_pdma(ring, htxd); tx_buf = mtk_desc_to_tx_buf(ring, htxd, soc->txrx.txd_size); mtk_tx_unmap(eth, tx_buf, NULL, false); htxd->txd3 = TX_DMA_LS0 | TX_DMA_OWNER_CPU; - if (!MTK_HAS_CAPS(soc->caps, MTK_QDMA)) + if (!MTK_HAS_CAPS(soc->caps, MTK_QDMA)) { + struct mtk_tx_dma *txd_pdma = qdma_to_pdma(ring, htxd); + txd_pdma->txd2 = TX_DMA_DESP2_DEF; + } htxd = mtk_qdma_phys_to_virt(ring, htxd->txd2); } @@ -1892,12 +1906,14 @@ static int mtk_poll_rx(struct napi_struct *napi, int budget, bytes += skb->len; if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) { + reason = FIELD_GET(MTK_RXD5_PPE_CPU_REASON, trxd.rxd5); hash = trxd.rxd5 & MTK_RXD5_FOE_ENTRY; if (hash != MTK_RXD5_FOE_ENTRY) skb_set_hash(skb, jhash_1word(hash, 0), PKT_HASH_TYPE_L4); rxdcsum = &trxd.rxd3; } else { + reason = FIELD_GET(MTK_RXD4_PPE_CPU_REASON, trxd.rxd4); hash = trxd.rxd4 & MTK_RXD4_FOE_ENTRY; if (hash != MTK_RXD4_FOE_ENTRY) skb_set_hash(skb, jhash_1word(hash, 0), @@ -1911,9 +1927,8 @@ static int mtk_poll_rx(struct napi_struct *napi, int budget, skb_checksum_none_assert(skb); skb->protocol = eth_type_trans(skb, netdev); - reason = FIELD_GET(MTK_RXD4_PPE_CPU_REASON, trxd.rxd4); if (reason == MTK_PPE_CPU_REASON_HIT_UNBIND_RATE_REACHED) - mtk_ppe_check_skb(eth->ppe, skb, hash); + mtk_ppe_check_skb(eth->ppe[0], skb, hash); if (netdev->features & NETIF_F_HW_VLAN_CTAG_RX) { if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) { @@ -2976,21 +2991,25 @@ static int mtk_open(struct net_device *dev) /* we run 2 netdevs on the same dma ring so we only bring it up once */ if (!refcount_read(ð->dma_refcnt)) { - u32 gdm_config = MTK_GDMA_TO_PDMA; + const struct mtk_soc_data *soc = eth->soc; + u32 gdm_config; + int i; err = mtk_start_dma(eth); if (err) return err; - if (eth->soc->offload_version && mtk_ppe_start(eth->ppe) == 0) - gdm_config = MTK_GDMA_TO_PPE; + for (i = 0; i < ARRAY_SIZE(eth->ppe); i++) + mtk_ppe_start(eth->ppe[i]); + gdm_config = soc->offload_version ? soc->reg_map->gdma_to_ppe + : MTK_GDMA_TO_PDMA; mtk_gdm_config(eth, gdm_config); napi_enable(ð->tx_napi); napi_enable(ð->rx_napi); mtk_tx_irq_enable(eth, MTK_TX_DONE_INT); - mtk_rx_irq_enable(eth, eth->soc->txrx.rx_irq_done_mask); + mtk_rx_irq_enable(eth, soc->txrx.rx_irq_done_mask); refcount_set(ð->dma_refcnt, 1); } else @@ -3028,6 +3047,7 @@ static int mtk_stop(struct net_device *dev) { struct mtk_mac *mac = netdev_priv(dev); struct mtk_eth *eth = mac->hw; + int i; phylink_stop(mac->phylink); @@ -3055,8 +3075,8 @@ static int mtk_stop(struct net_device *dev) mtk_dma_free(eth); - if (eth->soc->offload_version) - mtk_ppe_stop(eth->ppe); + for (i = 0; i < ARRAY_SIZE(eth->ppe); i++) + mtk_ppe_stop(eth->ppe[i]); return 0; } @@ -3556,8 +3576,8 @@ static void mtk_get_drvinfo(struct net_device *dev, { struct mtk_mac *mac = netdev_priv(dev); - strlcpy(info->driver, mac->hw->dev->driver->name, sizeof(info->driver)); - strlcpy(info->bus_info, dev_name(mac->hw->dev), sizeof(info->bus_info)); + strscpy(info->driver, mac->hw->dev->driver->name, sizeof(info->driver)); + strscpy(info->bus_info, dev_name(mac->hw->dev), sizeof(info->bus_info)); info->n_stats = ARRAY_SIZE(mtk_ethtool_stats); } @@ -3925,6 +3945,7 @@ void mtk_eth_set_dma_device(struct mtk_eth *eth, struct device *dma_dev) static int mtk_probe(struct platform_device *pdev) { + struct resource *res = NULL; struct device_node *mac_np; struct mtk_eth *eth; int err, i; @@ -4005,20 +4026,31 @@ static int mtk_probe(struct platform_device *pdev) } } - for (i = 0;; i++) { - struct device_node *np = of_parse_phandle(pdev->dev.of_node, - "mediatek,wed", i); - static const u32 wdma_regs[] = { - MTK_WDMA0_BASE, - MTK_WDMA1_BASE - }; - void __iomem *wdma; - - if (!np || i >= ARRAY_SIZE(wdma_regs)) - break; + if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) { + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -EINVAL; + } - wdma = eth->base + wdma_regs[i]; - mtk_wed_add_hw(np, eth, wdma, i); + if (eth->soc->offload_version) { + for (i = 0;; i++) { + struct device_node *np; + phys_addr_t wdma_phy; + u32 wdma_base; + + if (i >= ARRAY_SIZE(eth->soc->reg_map->wdma_base)) + break; + + np = of_parse_phandle(pdev->dev.of_node, + "mediatek,wed", i); + if (!np) + break; + + wdma_base = eth->soc->reg_map->wdma_base[i]; + wdma_phy = res ? res->start + wdma_base : 0; + mtk_wed_add_hw(np, eth, eth->base + wdma_base, + wdma_phy, i); + } } for (i = 0; i < 3; i++) { @@ -4096,10 +4128,19 @@ static int mtk_probe(struct platform_device *pdev) } if (eth->soc->offload_version) { - eth->ppe = mtk_ppe_init(eth, eth->base + MTK_ETH_PPE_BASE, 2); - if (!eth->ppe) { - err = -ENOMEM; - goto err_free_dev; + u32 num_ppe; + + num_ppe = MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2) ? 2 : 1; + num_ppe = min_t(u32, ARRAY_SIZE(eth->ppe), num_ppe); + for (i = 0; i < num_ppe; i++) { + u32 ppe_addr = eth->soc->reg_map->ppe_base + i * 0x400; + + eth->ppe[i] = mtk_ppe_init(eth, eth->base + ppe_addr, + eth->soc->offload_version, i); + if (!eth->ppe[i]) { + err = -ENOMEM; + goto err_free_dev; + } } err = mtk_eth_offload_init(eth); @@ -4125,10 +4166,8 @@ static int mtk_probe(struct platform_device *pdev) * for NAPI to work */ init_dummy_netdev(ð->dummy_dev); - netif_napi_add(ð->dummy_dev, ð->tx_napi, mtk_napi_tx, - NAPI_POLL_WEIGHT); - netif_napi_add(ð->dummy_dev, ð->rx_napi, mtk_napi_rx, - NAPI_POLL_WEIGHT); + netif_napi_add(ð->dummy_dev, ð->tx_napi, mtk_napi_tx); + netif_napi_add(ð->dummy_dev, ð->rx_napi, mtk_napi_rx); platform_set_drvdata(pdev, eth); @@ -4192,6 +4231,8 @@ static const struct mtk_soc_data mt7621_data = { .required_clks = MT7621_CLKS_BITMAP, .required_pctl = false, .offload_version = 2, + .hash_offset = 2, + .foe_entry_size = sizeof(struct mtk_foe_entry) - 16, .txrx = { .txd_size = sizeof(struct mtk_tx_dma), .rxd_size = sizeof(struct mtk_rx_dma), @@ -4210,6 +4251,8 @@ static const struct mtk_soc_data mt7622_data = { .required_clks = MT7622_CLKS_BITMAP, .required_pctl = false, .offload_version = 2, + .hash_offset = 2, + .foe_entry_size = sizeof(struct mtk_foe_entry) - 16, .txrx = { .txd_size = sizeof(struct mtk_tx_dma), .rxd_size = sizeof(struct mtk_rx_dma), @@ -4227,6 +4270,8 @@ static const struct mtk_soc_data mt7623_data = { .required_clks = MT7623_CLKS_BITMAP, .required_pctl = true, .offload_version = 2, + .hash_offset = 2, + .foe_entry_size = sizeof(struct mtk_foe_entry) - 16, .txrx = { .txd_size = sizeof(struct mtk_tx_dma), .rxd_size = sizeof(struct mtk_rx_dma), @@ -4258,8 +4303,11 @@ static const struct mtk_soc_data mt7986_data = { .reg_map = &mt7986_reg_map, .ana_rgc3 = 0x128, .caps = MT7986_CAPS, + .hw_features = MTK_HW_FEATURES, .required_clks = MT7986_CLKS_BITMAP, .required_pctl = false, + .hash_offset = 4, + .foe_entry_size = sizeof(struct mtk_foe_entry), .txrx = { .txd_size = sizeof(struct mtk_tx_dma_v2), .rxd_size = sizeof(struct mtk_rx_dma_v2), diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.h b/drivers/net/ethernet/mediatek/mtk_eth_soc.h index 0f9668a4079d..b52f3b0177ef 100644 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h @@ -105,7 +105,6 @@ #define MTK_GDMA_TCS_EN BIT(21) #define MTK_GDMA_UCS_EN BIT(20) #define MTK_GDMA_TO_PDMA 0x0 -#define MTK_GDMA_TO_PPE 0x4444 #define MTK_GDMA_DROP_ALL 0x7777 /* Unicast Filter MAC Address Register - Low */ @@ -269,9 +268,6 @@ #define TX_DMA_FPORT_MASK_V2 0xf #define TX_DMA_SWC_V2 BIT(30) -#define MTK_WDMA0_BASE 0x2800 -#define MTK_WDMA1_BASE 0x2c00 - /* QDMA descriptor txd4 */ #define TX_DMA_CHKSUM (0x7 << 29) #define TX_DMA_TSO BIT(28) @@ -955,6 +951,9 @@ struct mtk_reg_map { u32 fq_blen; /* fq free page buffer length */ } qdma; u32 gdm1_cnt; + u32 gdma_to_ppe; + u32 ppe_base; + u32 wdma_base[2]; }; /* struct mtk_eth_data - This is the structure holding all differences @@ -968,6 +967,8 @@ struct mtk_reg_map { * the target SoC * @required_pctl A bool value to show whether the SoC requires * the extra setup for those pins used by GMAC. + * @hash_offset Flow table hash offset. + * @foe_entry_size Foe table entry size. * @txd_size Tx DMA descriptor size. * @rxd_size Rx DMA descriptor size. * @rx_irq_done_mask Rx irq done register mask. @@ -982,6 +983,8 @@ struct mtk_soc_data { u32 required_clks; bool required_pctl; u8 offload_version; + u8 hash_offset; + u16 foe_entry_size; netdev_features_t hw_features; struct { u32 txd_size; @@ -1111,7 +1114,7 @@ struct mtk_eth { int ip_align; - struct mtk_ppe *ppe; + struct mtk_ppe *ppe[2]; struct rhashtable flow_table; struct bpf_prog __rcu *prog; @@ -1142,6 +1145,86 @@ struct mtk_mac { /* the struct describing the SoC. these are declared in the soc_xyz.c files */ extern const struct of_device_id of_mtk_match[]; +static inline struct mtk_foe_entry * +mtk_foe_get_entry(struct mtk_ppe *ppe, u16 hash) +{ + const struct mtk_soc_data *soc = ppe->eth->soc; + + return ppe->foe_table + hash * soc->foe_entry_size; +} + +static inline u32 mtk_get_ib1_ts_mask(struct mtk_eth *eth) +{ + if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) + return MTK_FOE_IB1_BIND_TIMESTAMP_V2; + + return MTK_FOE_IB1_BIND_TIMESTAMP; +} + +static inline u32 mtk_get_ib1_ppoe_mask(struct mtk_eth *eth) +{ + if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) + return MTK_FOE_IB1_BIND_PPPOE_V2; + + return MTK_FOE_IB1_BIND_PPPOE; +} + +static inline u32 mtk_get_ib1_vlan_tag_mask(struct mtk_eth *eth) +{ + if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) + return MTK_FOE_IB1_BIND_VLAN_TAG_V2; + + return MTK_FOE_IB1_BIND_VLAN_TAG; +} + +static inline u32 mtk_get_ib1_vlan_layer_mask(struct mtk_eth *eth) +{ + if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) + return MTK_FOE_IB1_BIND_VLAN_LAYER_V2; + + return MTK_FOE_IB1_BIND_VLAN_LAYER; +} + +static inline u32 mtk_prep_ib1_vlan_layer(struct mtk_eth *eth, u32 val) +{ + if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) + return FIELD_PREP(MTK_FOE_IB1_BIND_VLAN_LAYER_V2, val); + + return FIELD_PREP(MTK_FOE_IB1_BIND_VLAN_LAYER, val); +} + +static inline u32 mtk_get_ib1_vlan_layer(struct mtk_eth *eth, u32 val) +{ + if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) + return FIELD_GET(MTK_FOE_IB1_BIND_VLAN_LAYER_V2, val); + + return FIELD_GET(MTK_FOE_IB1_BIND_VLAN_LAYER, val); +} + +static inline u32 mtk_get_ib1_pkt_type_mask(struct mtk_eth *eth) +{ + if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) + return MTK_FOE_IB1_PACKET_TYPE_V2; + + return MTK_FOE_IB1_PACKET_TYPE; +} + +static inline u32 mtk_get_ib1_pkt_type(struct mtk_eth *eth, u32 val) +{ + if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) + return FIELD_GET(MTK_FOE_IB1_PACKET_TYPE_V2, val); + + return FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, val); +} + +static inline u32 mtk_get_ib2_multicast_mask(struct mtk_eth *eth) +{ + if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) + return MTK_FOE_IB2_MULTICAST_V2; + + return MTK_FOE_IB2_MULTICAST; +} + /* read the hardware status register */ void mtk_stats_update_mac(struct mtk_mac *mac); diff --git a/drivers/net/ethernet/mediatek/mtk_ppe.c b/drivers/net/ethernet/mediatek/mtk_ppe.c index cfe804bc8d20..ae00e572390d 100644 --- a/drivers/net/ethernet/mediatek/mtk_ppe.c +++ b/drivers/net/ethernet/mediatek/mtk_ppe.c @@ -56,7 +56,7 @@ static u32 ppe_clear(struct mtk_ppe *ppe, u32 reg, u32 val) static u32 mtk_eth_timestamp(struct mtk_eth *eth) { - return mtk_r32(eth, 0x0010) & MTK_FOE_IB1_BIND_TIMESTAMP; + return mtk_r32(eth, 0x0010) & mtk_get_ib1_ts_mask(eth); } static int mtk_ppe_wait_busy(struct mtk_ppe *ppe) @@ -88,12 +88,12 @@ static void mtk_ppe_cache_enable(struct mtk_ppe *ppe, bool enable) enable * MTK_PPE_CACHE_CTL_EN); } -static u32 mtk_ppe_hash_entry(struct mtk_foe_entry *e) +static u32 mtk_ppe_hash_entry(struct mtk_eth *eth, struct mtk_foe_entry *e) { u32 hv1, hv2, hv3; u32 hash; - switch (FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, e->ib1)) { + switch (mtk_get_ib1_pkt_type(eth, e->ib1)) { case MTK_PPE_PKT_TYPE_IPV4_ROUTE: case MTK_PPE_PKT_TYPE_IPV4_HNAPT: hv1 = e->ipv4.orig.ports; @@ -122,16 +122,16 @@ static u32 mtk_ppe_hash_entry(struct mtk_foe_entry *e) hash = (hash >> 24) | ((hash & 0xffffff) << 8); hash ^= hv1 ^ hv2 ^ hv3; hash ^= hash >> 16; - hash <<= 1; + hash <<= (ffs(eth->soc->hash_offset) - 1); hash &= MTK_PPE_ENTRIES - 1; return hash; } static inline struct mtk_foe_mac_info * -mtk_foe_entry_l2(struct mtk_foe_entry *entry) +mtk_foe_entry_l2(struct mtk_eth *eth, struct mtk_foe_entry *entry) { - int type = FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, entry->ib1); + int type = mtk_get_ib1_pkt_type(eth, entry->ib1); if (type == MTK_PPE_PKT_TYPE_BRIDGE) return &entry->bridge.l2; @@ -143,9 +143,9 @@ mtk_foe_entry_l2(struct mtk_foe_entry *entry) } static inline u32 * -mtk_foe_entry_ib2(struct mtk_foe_entry *entry) +mtk_foe_entry_ib2(struct mtk_eth *eth, struct mtk_foe_entry *entry) { - int type = FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, entry->ib1); + int type = mtk_get_ib1_pkt_type(eth, entry->ib1); if (type == MTK_PPE_PKT_TYPE_BRIDGE) return &entry->bridge.ib2; @@ -156,27 +156,38 @@ mtk_foe_entry_ib2(struct mtk_foe_entry *entry) return &entry->ipv4.ib2; } -int mtk_foe_entry_prepare(struct mtk_foe_entry *entry, int type, int l4proto, - u8 pse_port, u8 *src_mac, u8 *dest_mac) +int mtk_foe_entry_prepare(struct mtk_eth *eth, struct mtk_foe_entry *entry, + int type, int l4proto, u8 pse_port, u8 *src_mac, + u8 *dest_mac) { struct mtk_foe_mac_info *l2; u32 ports_pad, val; memset(entry, 0, sizeof(*entry)); - val = FIELD_PREP(MTK_FOE_IB1_STATE, MTK_FOE_STATE_BIND) | - FIELD_PREP(MTK_FOE_IB1_PACKET_TYPE, type) | - FIELD_PREP(MTK_FOE_IB1_UDP, l4proto == IPPROTO_UDP) | - MTK_FOE_IB1_BIND_TTL | - MTK_FOE_IB1_BIND_CACHE; - entry->ib1 = val; + if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) { + val = FIELD_PREP(MTK_FOE_IB1_STATE, MTK_FOE_STATE_BIND) | + FIELD_PREP(MTK_FOE_IB1_PACKET_TYPE_V2, type) | + FIELD_PREP(MTK_FOE_IB1_UDP, l4proto == IPPROTO_UDP) | + MTK_FOE_IB1_BIND_CACHE_V2 | MTK_FOE_IB1_BIND_TTL_V2; + entry->ib1 = val; - val = FIELD_PREP(MTK_FOE_IB2_PORT_MG, 0x3f) | - FIELD_PREP(MTK_FOE_IB2_PORT_AG, 0x1f) | - FIELD_PREP(MTK_FOE_IB2_DEST_PORT, pse_port); + val = FIELD_PREP(MTK_FOE_IB2_DEST_PORT_V2, pse_port) | + FIELD_PREP(MTK_FOE_IB2_PORT_AG_V2, 0xf); + } else { + val = FIELD_PREP(MTK_FOE_IB1_STATE, MTK_FOE_STATE_BIND) | + FIELD_PREP(MTK_FOE_IB1_PACKET_TYPE, type) | + FIELD_PREP(MTK_FOE_IB1_UDP, l4proto == IPPROTO_UDP) | + MTK_FOE_IB1_BIND_CACHE | MTK_FOE_IB1_BIND_TTL; + entry->ib1 = val; + + val = FIELD_PREP(MTK_FOE_IB2_DEST_PORT, pse_port) | + FIELD_PREP(MTK_FOE_IB2_PORT_MG, 0x3f) | + FIELD_PREP(MTK_FOE_IB2_PORT_AG, 0x1f); + } if (is_multicast_ether_addr(dest_mac)) - val |= MTK_FOE_IB2_MULTICAST; + val |= mtk_get_ib2_multicast_mask(eth); ports_pad = 0xa5a5a500 | (l4proto & 0xff); if (type == MTK_PPE_PKT_TYPE_IPV4_ROUTE) @@ -210,24 +221,30 @@ int mtk_foe_entry_prepare(struct mtk_foe_entry *entry, int type, int l4proto, return 0; } -int mtk_foe_entry_set_pse_port(struct mtk_foe_entry *entry, u8 port) +int mtk_foe_entry_set_pse_port(struct mtk_eth *eth, + struct mtk_foe_entry *entry, u8 port) { - u32 *ib2 = mtk_foe_entry_ib2(entry); - u32 val; + u32 *ib2 = mtk_foe_entry_ib2(eth, entry); + u32 val = *ib2; - val = *ib2; - val &= ~MTK_FOE_IB2_DEST_PORT; - val |= FIELD_PREP(MTK_FOE_IB2_DEST_PORT, port); + if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) { + val &= ~MTK_FOE_IB2_DEST_PORT_V2; + val |= FIELD_PREP(MTK_FOE_IB2_DEST_PORT_V2, port); + } else { + val &= ~MTK_FOE_IB2_DEST_PORT; + val |= FIELD_PREP(MTK_FOE_IB2_DEST_PORT, port); + } *ib2 = val; return 0; } -int mtk_foe_entry_set_ipv4_tuple(struct mtk_foe_entry *entry, bool egress, +int mtk_foe_entry_set_ipv4_tuple(struct mtk_eth *eth, + struct mtk_foe_entry *entry, bool egress, __be32 src_addr, __be16 src_port, __be32 dest_addr, __be16 dest_port) { - int type = FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, entry->ib1); + int type = mtk_get_ib1_pkt_type(eth, entry->ib1); struct mtk_ipv4_tuple *t; switch (type) { @@ -262,11 +279,12 @@ int mtk_foe_entry_set_ipv4_tuple(struct mtk_foe_entry *entry, bool egress, return 0; } -int mtk_foe_entry_set_ipv6_tuple(struct mtk_foe_entry *entry, +int mtk_foe_entry_set_ipv6_tuple(struct mtk_eth *eth, + struct mtk_foe_entry *entry, __be32 *src_addr, __be16 src_port, __be32 *dest_addr, __be16 dest_port) { - int type = FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, entry->ib1); + int type = mtk_get_ib1_pkt_type(eth, entry->ib1); u32 *src, *dest; int i; @@ -297,39 +315,41 @@ int mtk_foe_entry_set_ipv6_tuple(struct mtk_foe_entry *entry, return 0; } -int mtk_foe_entry_set_dsa(struct mtk_foe_entry *entry, int port) +int mtk_foe_entry_set_dsa(struct mtk_eth *eth, struct mtk_foe_entry *entry, + int port) { - struct mtk_foe_mac_info *l2 = mtk_foe_entry_l2(entry); + struct mtk_foe_mac_info *l2 = mtk_foe_entry_l2(eth, entry); l2->etype = BIT(port); - if (!(entry->ib1 & MTK_FOE_IB1_BIND_VLAN_LAYER)) - entry->ib1 |= FIELD_PREP(MTK_FOE_IB1_BIND_VLAN_LAYER, 1); + if (!(entry->ib1 & mtk_get_ib1_vlan_layer_mask(eth))) + entry->ib1 |= mtk_prep_ib1_vlan_layer(eth, 1); else l2->etype |= BIT(8); - entry->ib1 &= ~MTK_FOE_IB1_BIND_VLAN_TAG; + entry->ib1 &= ~mtk_get_ib1_vlan_tag_mask(eth); return 0; } -int mtk_foe_entry_set_vlan(struct mtk_foe_entry *entry, int vid) +int mtk_foe_entry_set_vlan(struct mtk_eth *eth, struct mtk_foe_entry *entry, + int vid) { - struct mtk_foe_mac_info *l2 = mtk_foe_entry_l2(entry); + struct mtk_foe_mac_info *l2 = mtk_foe_entry_l2(eth, entry); - switch (FIELD_GET(MTK_FOE_IB1_BIND_VLAN_LAYER, entry->ib1)) { + switch (mtk_get_ib1_vlan_layer(eth, entry->ib1)) { case 0: - entry->ib1 |= MTK_FOE_IB1_BIND_VLAN_TAG | - FIELD_PREP(MTK_FOE_IB1_BIND_VLAN_LAYER, 1); + entry->ib1 |= mtk_get_ib1_vlan_tag_mask(eth) | + mtk_prep_ib1_vlan_layer(eth, 1); l2->vlan1 = vid; return 0; case 1: - if (!(entry->ib1 & MTK_FOE_IB1_BIND_VLAN_TAG)) { + if (!(entry->ib1 & mtk_get_ib1_vlan_tag_mask(eth))) { l2->vlan1 = vid; l2->etype |= BIT(8); } else { l2->vlan2 = vid; - entry->ib1 += FIELD_PREP(MTK_FOE_IB1_BIND_VLAN_LAYER, 1); + entry->ib1 += mtk_prep_ib1_vlan_layer(eth, 1); } return 0; default: @@ -337,34 +357,42 @@ int mtk_foe_entry_set_vlan(struct mtk_foe_entry *entry, int vid) } } -int mtk_foe_entry_set_pppoe(struct mtk_foe_entry *entry, int sid) +int mtk_foe_entry_set_pppoe(struct mtk_eth *eth, struct mtk_foe_entry *entry, + int sid) { - struct mtk_foe_mac_info *l2 = mtk_foe_entry_l2(entry); + struct mtk_foe_mac_info *l2 = mtk_foe_entry_l2(eth, entry); - if (!(entry->ib1 & MTK_FOE_IB1_BIND_VLAN_LAYER) || - (entry->ib1 & MTK_FOE_IB1_BIND_VLAN_TAG)) + if (!(entry->ib1 & mtk_get_ib1_vlan_layer_mask(eth)) || + (entry->ib1 & mtk_get_ib1_vlan_tag_mask(eth))) l2->etype = ETH_P_PPP_SES; - entry->ib1 |= MTK_FOE_IB1_BIND_PPPOE; + entry->ib1 |= mtk_get_ib1_ppoe_mask(eth); l2->pppoe_id = sid; return 0; } -int mtk_foe_entry_set_wdma(struct mtk_foe_entry *entry, int wdma_idx, int txq, - int bss, int wcid) +int mtk_foe_entry_set_wdma(struct mtk_eth *eth, struct mtk_foe_entry *entry, + int wdma_idx, int txq, int bss, int wcid) { - struct mtk_foe_mac_info *l2 = mtk_foe_entry_l2(entry); - u32 *ib2 = mtk_foe_entry_ib2(entry); - - *ib2 &= ~MTK_FOE_IB2_PORT_MG; - *ib2 |= MTK_FOE_IB2_WDMA_WINFO; - if (wdma_idx) - *ib2 |= MTK_FOE_IB2_WDMA_DEVIDX; + struct mtk_foe_mac_info *l2 = mtk_foe_entry_l2(eth, entry); + u32 *ib2 = mtk_foe_entry_ib2(eth, entry); - l2->vlan2 = FIELD_PREP(MTK_FOE_VLAN2_WINFO_BSS, bss) | - FIELD_PREP(MTK_FOE_VLAN2_WINFO_WCID, wcid) | - FIELD_PREP(MTK_FOE_VLAN2_WINFO_RING, txq); + if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) { + *ib2 &= ~MTK_FOE_IB2_PORT_MG_V2; + *ib2 |= FIELD_PREP(MTK_FOE_IB2_RX_IDX, txq) | + MTK_FOE_IB2_WDMA_WINFO_V2; + l2->winfo = FIELD_PREP(MTK_FOE_WINFO_WCID, wcid) | + FIELD_PREP(MTK_FOE_WINFO_BSS, bss); + } else { + *ib2 &= ~MTK_FOE_IB2_PORT_MG; + *ib2 |= MTK_FOE_IB2_WDMA_WINFO; + if (wdma_idx) + *ib2 |= MTK_FOE_IB2_WDMA_DEVIDX; + l2->vlan2 = FIELD_PREP(MTK_FOE_VLAN2_WINFO_BSS, bss) | + FIELD_PREP(MTK_FOE_VLAN2_WINFO_WCID, wcid) | + FIELD_PREP(MTK_FOE_VLAN2_WINFO_RING, txq); + } return 0; } @@ -376,14 +404,15 @@ static inline bool mtk_foe_entry_usable(struct mtk_foe_entry *entry) } static bool -mtk_flow_entry_match(struct mtk_flow_entry *entry, struct mtk_foe_entry *data) +mtk_flow_entry_match(struct mtk_eth *eth, struct mtk_flow_entry *entry, + struct mtk_foe_entry *data) { int type, len; if ((data->ib1 ^ entry->data.ib1) & MTK_FOE_IB1_UDP) return false; - type = FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, entry->data.ib1); + type = mtk_get_ib1_pkt_type(eth, entry->data.ib1); if (type > MTK_PPE_PKT_TYPE_IPV4_DSLITE) len = offsetof(struct mtk_foe_entry, ipv6._rsv); else @@ -410,9 +439,10 @@ __mtk_foe_entry_clear(struct mtk_ppe *ppe, struct mtk_flow_entry *entry) hlist_del_init(&entry->list); if (entry->hash != 0xffff) { - ppe->foe_table[entry->hash].ib1 &= ~MTK_FOE_IB1_STATE; - ppe->foe_table[entry->hash].ib1 |= FIELD_PREP(MTK_FOE_IB1_STATE, - MTK_FOE_STATE_UNBIND); + struct mtk_foe_entry *hwe = mtk_foe_get_entry(ppe, entry->hash); + + hwe->ib1 &= ~MTK_FOE_IB1_STATE; + hwe->ib1 |= FIELD_PREP(MTK_FOE_IB1_STATE, MTK_FOE_STATE_INVALID); dma_wmb(); } entry->hash = 0xffff; @@ -426,14 +456,12 @@ __mtk_foe_entry_clear(struct mtk_ppe *ppe, struct mtk_flow_entry *entry) static int __mtk_foe_entry_idle_time(struct mtk_ppe *ppe, u32 ib1) { - u16 timestamp; - u16 now; - - now = mtk_eth_timestamp(ppe->eth) & MTK_FOE_IB1_BIND_TIMESTAMP; - timestamp = ib1 & MTK_FOE_IB1_BIND_TIMESTAMP; + u32 ib1_ts_mask = mtk_get_ib1_ts_mask(ppe->eth); + u16 now = mtk_eth_timestamp(ppe->eth); + u16 timestamp = ib1 & ib1_ts_mask; if (timestamp > now) - return MTK_FOE_IB1_BIND_TIMESTAMP + 1 - timestamp + now; + return ib1_ts_mask + 1 - timestamp + now; else return now - timestamp; } @@ -441,6 +469,7 @@ static int __mtk_foe_entry_idle_time(struct mtk_ppe *ppe, u32 ib1) static void mtk_flow_entry_update_l2(struct mtk_ppe *ppe, struct mtk_flow_entry *entry) { + u32 ib1_ts_mask = mtk_get_ib1_ts_mask(ppe->eth); struct mtk_flow_entry *cur; struct mtk_foe_entry *hwe; struct hlist_node *tmp; @@ -451,7 +480,7 @@ mtk_flow_entry_update_l2(struct mtk_ppe *ppe, struct mtk_flow_entry *entry) int cur_idle; u32 ib1; - hwe = &ppe->foe_table[cur->hash]; + hwe = mtk_foe_get_entry(ppe, cur->hash); ib1 = READ_ONCE(hwe->ib1); if (FIELD_GET(MTK_FOE_IB1_STATE, ib1) != MTK_FOE_STATE_BIND) { @@ -465,16 +494,16 @@ mtk_flow_entry_update_l2(struct mtk_ppe *ppe, struct mtk_flow_entry *entry) continue; idle = cur_idle; - entry->data.ib1 &= ~MTK_FOE_IB1_BIND_TIMESTAMP; - entry->data.ib1 |= hwe->ib1 & MTK_FOE_IB1_BIND_TIMESTAMP; + entry->data.ib1 &= ~ib1_ts_mask; + entry->data.ib1 |= hwe->ib1 & ib1_ts_mask; } } static void mtk_flow_entry_update(struct mtk_ppe *ppe, struct mtk_flow_entry *entry) { + struct mtk_foe_entry foe = {}; struct mtk_foe_entry *hwe; - struct mtk_foe_entry foe; spin_lock_bh(&ppe_lock); @@ -486,9 +515,9 @@ mtk_flow_entry_update(struct mtk_ppe *ppe, struct mtk_flow_entry *entry) if (entry->hash == 0xffff) goto out; - hwe = &ppe->foe_table[entry->hash]; - memcpy(&foe, hwe, sizeof(foe)); - if (!mtk_flow_entry_match(entry, &foe)) { + hwe = mtk_foe_get_entry(ppe, entry->hash); + memcpy(&foe, hwe, ppe->eth->soc->foe_entry_size); + if (!mtk_flow_entry_match(ppe->eth, entry, &foe)) { entry->hash = 0xffff; goto out; } @@ -503,16 +532,22 @@ static void __mtk_foe_entry_commit(struct mtk_ppe *ppe, struct mtk_foe_entry *entry, u16 hash) { + struct mtk_eth *eth = ppe->eth; + u16 timestamp = mtk_eth_timestamp(eth); struct mtk_foe_entry *hwe; - u16 timestamp; - timestamp = mtk_eth_timestamp(ppe->eth); - timestamp &= MTK_FOE_IB1_BIND_TIMESTAMP; - entry->ib1 &= ~MTK_FOE_IB1_BIND_TIMESTAMP; - entry->ib1 |= FIELD_PREP(MTK_FOE_IB1_BIND_TIMESTAMP, timestamp); + if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) { + entry->ib1 &= ~MTK_FOE_IB1_BIND_TIMESTAMP_V2; + entry->ib1 |= FIELD_PREP(MTK_FOE_IB1_BIND_TIMESTAMP_V2, + timestamp); + } else { + entry->ib1 &= ~MTK_FOE_IB1_BIND_TIMESTAMP; + entry->ib1 |= FIELD_PREP(MTK_FOE_IB1_BIND_TIMESTAMP, + timestamp); + } - hwe = &ppe->foe_table[hash]; - memcpy(&hwe->data, &entry->data, sizeof(hwe->data)); + hwe = mtk_foe_get_entry(ppe, hash); + memcpy(&hwe->data, &entry->data, eth->soc->foe_entry_size - sizeof(hwe->ib1)); wmb(); hwe->ib1 = entry->ib1; @@ -539,16 +574,17 @@ mtk_foe_entry_commit_l2(struct mtk_ppe *ppe, struct mtk_flow_entry *entry) int mtk_foe_entry_commit(struct mtk_ppe *ppe, struct mtk_flow_entry *entry) { - int type = FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, entry->data.ib1); + const struct mtk_soc_data *soc = ppe->eth->soc; + int type = mtk_get_ib1_pkt_type(ppe->eth, entry->data.ib1); u32 hash; if (type == MTK_PPE_PKT_TYPE_BRIDGE) return mtk_foe_entry_commit_l2(ppe, entry); - hash = mtk_ppe_hash_entry(&entry->data); + hash = mtk_ppe_hash_entry(ppe->eth, &entry->data); entry->hash = 0xffff; spin_lock_bh(&ppe_lock); - hlist_add_head(&entry->list, &ppe->foe_flow[hash / 2]); + hlist_add_head(&entry->list, &ppe->foe_flow[hash / soc->hash_offset]); spin_unlock_bh(&ppe_lock); return 0; @@ -558,10 +594,11 @@ static void mtk_foe_entry_commit_subflow(struct mtk_ppe *ppe, struct mtk_flow_entry *entry, u16 hash) { + const struct mtk_soc_data *soc = ppe->eth->soc; struct mtk_flow_entry *flow_info; - struct mtk_foe_entry foe, *hwe; + struct mtk_foe_entry foe = {}, *hwe; struct mtk_foe_mac_info *l2; - u32 ib1_mask = MTK_FOE_IB1_PACKET_TYPE | MTK_FOE_IB1_UDP; + u32 ib1_mask = mtk_get_ib1_pkt_type_mask(ppe->eth) | MTK_FOE_IB1_UDP; int type; flow_info = kzalloc(offsetof(struct mtk_flow_entry, l2_data.end), @@ -572,32 +609,34 @@ mtk_foe_entry_commit_subflow(struct mtk_ppe *ppe, struct mtk_flow_entry *entry, flow_info->l2_data.base_flow = entry; flow_info->type = MTK_FLOW_TYPE_L2_SUBFLOW; flow_info->hash = hash; - hlist_add_head(&flow_info->list, &ppe->foe_flow[hash / 2]); + hlist_add_head(&flow_info->list, + &ppe->foe_flow[hash / soc->hash_offset]); hlist_add_head(&flow_info->l2_data.list, &entry->l2_flows); - hwe = &ppe->foe_table[hash]; - memcpy(&foe, hwe, sizeof(foe)); + hwe = mtk_foe_get_entry(ppe, hash); + memcpy(&foe, hwe, soc->foe_entry_size); foe.ib1 &= ib1_mask; foe.ib1 |= entry->data.ib1 & ~ib1_mask; - l2 = mtk_foe_entry_l2(&foe); + l2 = mtk_foe_entry_l2(ppe->eth, &foe); memcpy(l2, &entry->data.bridge.l2, sizeof(*l2)); - type = FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, foe.ib1); + type = mtk_get_ib1_pkt_type(ppe->eth, foe.ib1); if (type == MTK_PPE_PKT_TYPE_IPV4_HNAPT) memcpy(&foe.ipv4.new, &foe.ipv4.orig, sizeof(foe.ipv4.new)); else if (type >= MTK_PPE_PKT_TYPE_IPV6_ROUTE_3T && l2->etype == ETH_P_IP) l2->etype = ETH_P_IPV6; - *mtk_foe_entry_ib2(&foe) = entry->data.bridge.ib2; + *mtk_foe_entry_ib2(ppe->eth, &foe) = entry->data.bridge.ib2; __mtk_foe_entry_commit(ppe, &foe, hash); } void __mtk_ppe_check_skb(struct mtk_ppe *ppe, struct sk_buff *skb, u16 hash) { - struct hlist_head *head = &ppe->foe_flow[hash / 2]; - struct mtk_foe_entry *hwe = &ppe->foe_table[hash]; + const struct mtk_soc_data *soc = ppe->eth->soc; + struct hlist_head *head = &ppe->foe_flow[hash / soc->hash_offset]; + struct mtk_foe_entry *hwe = mtk_foe_get_entry(ppe, hash); struct mtk_flow_entry *entry; struct mtk_foe_bridge key = {}; struct hlist_node *n; @@ -621,7 +660,7 @@ void __mtk_ppe_check_skb(struct mtk_ppe *ppe, struct sk_buff *skb, u16 hash) continue; } - if (found || !mtk_flow_entry_match(entry, hwe)) { + if (found || !mtk_flow_entry_match(ppe->eth, entry, hwe)) { if (entry->hash != 0xffff) entry->hash = 0xffff; continue; @@ -678,11 +717,13 @@ int mtk_foe_entry_idle_time(struct mtk_ppe *ppe, struct mtk_flow_entry *entry) } struct mtk_ppe *mtk_ppe_init(struct mtk_eth *eth, void __iomem *base, - int version) + int version, int index) { + const struct mtk_soc_data *soc = eth->soc; struct device *dev = eth->dev; - struct mtk_foe_entry *foe; struct mtk_ppe *ppe; + u32 foe_flow_size; + void *foe; ppe = devm_kzalloc(dev, sizeof(*ppe), GFP_KERNEL); if (!ppe) @@ -698,14 +739,21 @@ struct mtk_ppe *mtk_ppe_init(struct mtk_eth *eth, void __iomem *base, ppe->dev = dev; ppe->version = version; - foe = dmam_alloc_coherent(ppe->dev, MTK_PPE_ENTRIES * sizeof(*foe), + foe = dmam_alloc_coherent(ppe->dev, + MTK_PPE_ENTRIES * soc->foe_entry_size, &ppe->foe_phys, GFP_KERNEL); if (!foe) return NULL; ppe->foe_table = foe; - mtk_ppe_debugfs_init(ppe); + foe_flow_size = (MTK_PPE_ENTRIES / soc->hash_offset) * + sizeof(*ppe->foe_flow); + ppe->foe_flow = devm_kzalloc(dev, foe_flow_size, GFP_KERNEL); + if (!ppe->foe_flow) + return NULL; + + mtk_ppe_debugfs_init(ppe, index); return ppe; } @@ -715,21 +763,30 @@ static void mtk_ppe_init_foe_table(struct mtk_ppe *ppe) static const u8 skip[] = { 12, 25, 38, 51, 76, 89, 102 }; int i, k; - memset(ppe->foe_table, 0, MTK_PPE_ENTRIES * sizeof(*ppe->foe_table)); + memset(ppe->foe_table, 0, + MTK_PPE_ENTRIES * ppe->eth->soc->foe_entry_size); if (!IS_ENABLED(CONFIG_SOC_MT7621)) return; /* skip all entries that cross the 1024 byte boundary */ - for (i = 0; i < MTK_PPE_ENTRIES; i += 128) - for (k = 0; k < ARRAY_SIZE(skip); k++) - ppe->foe_table[i + skip[k]].ib1 |= MTK_FOE_IB1_STATIC; + for (i = 0; i < MTK_PPE_ENTRIES; i += 128) { + for (k = 0; k < ARRAY_SIZE(skip); k++) { + struct mtk_foe_entry *hwe; + + hwe = mtk_foe_get_entry(ppe, i + skip[k]); + hwe->ib1 |= MTK_FOE_IB1_STATIC; + } + } } -int mtk_ppe_start(struct mtk_ppe *ppe) +void mtk_ppe_start(struct mtk_ppe *ppe) { u32 val; + if (!ppe) + return; + mtk_ppe_init_foe_table(ppe); ppe_w32(ppe, MTK_PPE_TB_BASE, ppe->foe_phys); @@ -748,6 +805,8 @@ int mtk_ppe_start(struct mtk_ppe *ppe) MTK_PPE_SCAN_MODE_KEEPALIVE_AGE) | FIELD_PREP(MTK_PPE_TB_CFG_ENTRY_NUM, MTK_PPE_ENTRIES_SHIFT); + if (MTK_HAS_CAPS(ppe->eth->soc->caps, MTK_NETSYS_V2)) + val |= MTK_PPE_TB_CFG_INFO_SEL; ppe_w32(ppe, MTK_PPE_TB_CFG, val); ppe_w32(ppe, MTK_PPE_IP_PROTO_CHK, @@ -755,15 +814,21 @@ int mtk_ppe_start(struct mtk_ppe *ppe) mtk_ppe_cache_enable(ppe, true); - val = MTK_PPE_FLOW_CFG_IP4_TCP_FRAG | - MTK_PPE_FLOW_CFG_IP4_UDP_FRAG | - MTK_PPE_FLOW_CFG_IP6_3T_ROUTE | + val = MTK_PPE_FLOW_CFG_IP6_3T_ROUTE | MTK_PPE_FLOW_CFG_IP6_5T_ROUTE | MTK_PPE_FLOW_CFG_IP6_6RD | MTK_PPE_FLOW_CFG_IP4_NAT | MTK_PPE_FLOW_CFG_IP4_NAPT | MTK_PPE_FLOW_CFG_IP4_DSLITE | MTK_PPE_FLOW_CFG_IP4_NAT_FRAG; + if (MTK_HAS_CAPS(ppe->eth->soc->caps, MTK_NETSYS_V2)) + val |= MTK_PPE_MD_TOAP_BYP_CRSN0 | + MTK_PPE_MD_TOAP_BYP_CRSN1 | + MTK_PPE_MD_TOAP_BYP_CRSN2 | + MTK_PPE_FLOW_CFG_IP4_HASH_GRE_KEY; + else + val |= MTK_PPE_FLOW_CFG_IP4_TCP_FRAG | + MTK_PPE_FLOW_CFG_IP4_UDP_FRAG; ppe_w32(ppe, MTK_PPE_FLOW_CFG, val); val = FIELD_PREP(MTK_PPE_UNBIND_AGE_MIN_PACKETS, 1000) | @@ -798,7 +863,10 @@ int mtk_ppe_start(struct mtk_ppe *ppe) ppe_w32(ppe, MTK_PPE_DEFAULT_CPU_PORT, 0); - return 0; + if (MTK_HAS_CAPS(ppe->eth->soc->caps, MTK_NETSYS_V2)) { + ppe_w32(ppe, MTK_PPE_DEFAULT_CPU_PORT1, 0xcb777); + ppe_w32(ppe, MTK_PPE_SBW_CTRL, 0x7f); + } } int mtk_ppe_stop(struct mtk_ppe *ppe) @@ -806,9 +874,15 @@ int mtk_ppe_stop(struct mtk_ppe *ppe) u32 val; int i; - for (i = 0; i < MTK_PPE_ENTRIES; i++) - ppe->foe_table[i].ib1 = FIELD_PREP(MTK_FOE_IB1_STATE, - MTK_FOE_STATE_INVALID); + if (!ppe) + return 0; + + for (i = 0; i < MTK_PPE_ENTRIES; i++) { + struct mtk_foe_entry *hwe = mtk_foe_get_entry(ppe, i); + + hwe->ib1 = FIELD_PREP(MTK_FOE_IB1_STATE, + MTK_FOE_STATE_INVALID); + } mtk_ppe_cache_enable(ppe, false); diff --git a/drivers/net/ethernet/mediatek/mtk_ppe.h b/drivers/net/ethernet/mediatek/mtk_ppe.h index 69ffce04d630..0b7a67a958e4 100644 --- a/drivers/net/ethernet/mediatek/mtk_ppe.h +++ b/drivers/net/ethernet/mediatek/mtk_ppe.h @@ -8,8 +8,6 @@ #include <linux/bitfield.h> #include <linux/rhashtable.h> -#define MTK_ETH_PPE_BASE 0xc00 - #define MTK_PPE_ENTRIES_SHIFT 3 #define MTK_PPE_ENTRIES (1024 << MTK_PPE_ENTRIES_SHIFT) #define MTK_PPE_HASH_MASK (MTK_PPE_ENTRIES - 1) @@ -34,6 +32,15 @@ #define MTK_FOE_IB1_UDP BIT(30) #define MTK_FOE_IB1_STATIC BIT(31) +/* CONFIG_MEDIATEK_NETSYS_V2 */ +#define MTK_FOE_IB1_BIND_TIMESTAMP_V2 GENMASK(7, 0) +#define MTK_FOE_IB1_BIND_VLAN_LAYER_V2 GENMASK(16, 14) +#define MTK_FOE_IB1_BIND_PPPOE_V2 BIT(17) +#define MTK_FOE_IB1_BIND_VLAN_TAG_V2 BIT(18) +#define MTK_FOE_IB1_BIND_CACHE_V2 BIT(20) +#define MTK_FOE_IB1_BIND_TTL_V2 BIT(22) +#define MTK_FOE_IB1_PACKET_TYPE_V2 GENMASK(27, 23) + enum { MTK_PPE_PKT_TYPE_IPV4_HNAPT = 0, MTK_PPE_PKT_TYPE_IPV4_ROUTE = 1, @@ -55,14 +62,25 @@ enum { #define MTK_FOE_IB2_PORT_MG GENMASK(17, 12) +#define MTK_FOE_IB2_RX_IDX GENMASK(18, 17) #define MTK_FOE_IB2_PORT_AG GENMASK(23, 18) #define MTK_FOE_IB2_DSCP GENMASK(31, 24) +/* CONFIG_MEDIATEK_NETSYS_V2 */ +#define MTK_FOE_IB2_PORT_MG_V2 BIT(7) +#define MTK_FOE_IB2_DEST_PORT_V2 GENMASK(12, 9) +#define MTK_FOE_IB2_MULTICAST_V2 BIT(13) +#define MTK_FOE_IB2_WDMA_WINFO_V2 BIT(19) +#define MTK_FOE_IB2_PORT_AG_V2 GENMASK(23, 20) + #define MTK_FOE_VLAN2_WINFO_BSS GENMASK(5, 0) #define MTK_FOE_VLAN2_WINFO_WCID GENMASK(13, 6) #define MTK_FOE_VLAN2_WINFO_RING GENMASK(15, 14) +#define MTK_FOE_WINFO_BSS GENMASK(5, 0) +#define MTK_FOE_WINFO_WCID GENMASK(15, 6) + enum { MTK_FOE_STATE_INVALID, MTK_FOE_STATE_UNBIND, @@ -83,6 +101,9 @@ struct mtk_foe_mac_info { u16 pppoe_id; u16 src_mac_lo; + + u16 minfo; + u16 winfo; }; /* software-only entry type */ @@ -200,7 +221,7 @@ struct mtk_foe_entry { struct mtk_foe_ipv4_dslite dslite; struct mtk_foe_ipv6 ipv6; struct mtk_foe_ipv6_6rd ipv6_6rd; - u32 data[19]; + u32 data[23]; }; }; @@ -249,6 +270,7 @@ struct mtk_flow_entry { }; u8 type; s8 wed_index; + u8 ppe_index; u16 hash; union { struct mtk_foe_entry data; @@ -267,20 +289,22 @@ struct mtk_ppe { struct device *dev; void __iomem *base; int version; + char dirname[5]; - struct mtk_foe_entry *foe_table; + void *foe_table; dma_addr_t foe_phys; u16 foe_check_time[MTK_PPE_ENTRIES]; - struct hlist_head foe_flow[MTK_PPE_ENTRIES / 2]; + struct hlist_head *foe_flow; struct rhashtable l2_flows; void *acct_table; }; -struct mtk_ppe *mtk_ppe_init(struct mtk_eth *eth, void __iomem *base, int version); -int mtk_ppe_start(struct mtk_ppe *ppe); +struct mtk_ppe *mtk_ppe_init(struct mtk_eth *eth, void __iomem *base, + int version, int index); +void mtk_ppe_start(struct mtk_ppe *ppe); int mtk_ppe_stop(struct mtk_ppe *ppe); void __mtk_ppe_check_skb(struct mtk_ppe *ppe, struct sk_buff *skb, u16 hash); @@ -305,34 +329,30 @@ mtk_ppe_check_skb(struct mtk_ppe *ppe, struct sk_buff *skb, u16 hash) __mtk_ppe_check_skb(ppe, skb, hash); } -static inline int -mtk_foe_entry_timestamp(struct mtk_ppe *ppe, u16 hash) -{ - u32 ib1 = READ_ONCE(ppe->foe_table[hash].ib1); - - if (FIELD_GET(MTK_FOE_IB1_STATE, ib1) != MTK_FOE_STATE_BIND) - return -1; - - return FIELD_GET(MTK_FOE_IB1_BIND_TIMESTAMP, ib1); -} - -int mtk_foe_entry_prepare(struct mtk_foe_entry *entry, int type, int l4proto, - u8 pse_port, u8 *src_mac, u8 *dest_mac); -int mtk_foe_entry_set_pse_port(struct mtk_foe_entry *entry, u8 port); -int mtk_foe_entry_set_ipv4_tuple(struct mtk_foe_entry *entry, bool orig, +int mtk_foe_entry_prepare(struct mtk_eth *eth, struct mtk_foe_entry *entry, + int type, int l4proto, u8 pse_port, u8 *src_mac, + u8 *dest_mac); +int mtk_foe_entry_set_pse_port(struct mtk_eth *eth, + struct mtk_foe_entry *entry, u8 port); +int mtk_foe_entry_set_ipv4_tuple(struct mtk_eth *eth, + struct mtk_foe_entry *entry, bool orig, __be32 src_addr, __be16 src_port, __be32 dest_addr, __be16 dest_port); -int mtk_foe_entry_set_ipv6_tuple(struct mtk_foe_entry *entry, +int mtk_foe_entry_set_ipv6_tuple(struct mtk_eth *eth, + struct mtk_foe_entry *entry, __be32 *src_addr, __be16 src_port, __be32 *dest_addr, __be16 dest_port); -int mtk_foe_entry_set_dsa(struct mtk_foe_entry *entry, int port); -int mtk_foe_entry_set_vlan(struct mtk_foe_entry *entry, int vid); -int mtk_foe_entry_set_pppoe(struct mtk_foe_entry *entry, int sid); -int mtk_foe_entry_set_wdma(struct mtk_foe_entry *entry, int wdma_idx, int txq, - int bss, int wcid); +int mtk_foe_entry_set_dsa(struct mtk_eth *eth, struct mtk_foe_entry *entry, + int port); +int mtk_foe_entry_set_vlan(struct mtk_eth *eth, struct mtk_foe_entry *entry, + int vid); +int mtk_foe_entry_set_pppoe(struct mtk_eth *eth, struct mtk_foe_entry *entry, + int sid); +int mtk_foe_entry_set_wdma(struct mtk_eth *eth, struct mtk_foe_entry *entry, + int wdma_idx, int txq, int bss, int wcid); int mtk_foe_entry_commit(struct mtk_ppe *ppe, struct mtk_flow_entry *entry); void mtk_foe_entry_clear(struct mtk_ppe *ppe, struct mtk_flow_entry *entry); int mtk_foe_entry_idle_time(struct mtk_ppe *ppe, struct mtk_flow_entry *entry); -int mtk_ppe_debugfs_init(struct mtk_ppe *ppe); +int mtk_ppe_debugfs_init(struct mtk_ppe *ppe, int index); #endif diff --git a/drivers/net/ethernet/mediatek/mtk_ppe_debugfs.c b/drivers/net/ethernet/mediatek/mtk_ppe_debugfs.c index eb0b598f14e4..391b071bcff3 100644 --- a/drivers/net/ethernet/mediatek/mtk_ppe_debugfs.c +++ b/drivers/net/ethernet/mediatek/mtk_ppe_debugfs.c @@ -79,7 +79,7 @@ mtk_ppe_debugfs_foe_show(struct seq_file *m, void *private, bool bind) int i; for (i = 0; i < MTK_PPE_ENTRIES; i++) { - struct mtk_foe_entry *entry = &ppe->foe_table[i]; + struct mtk_foe_entry *entry = mtk_foe_get_entry(ppe, i); struct mtk_foe_mac_info *l2; struct mtk_flow_addr_info ai = {}; unsigned char h_source[ETH_ALEN]; @@ -162,52 +162,28 @@ mtk_ppe_debugfs_foe_show(struct seq_file *m, void *private, bool bind) } static int -mtk_ppe_debugfs_foe_show_all(struct seq_file *m, void *private) +mtk_ppe_debugfs_foe_all_show(struct seq_file *m, void *private) { return mtk_ppe_debugfs_foe_show(m, private, false); } +DEFINE_SHOW_ATTRIBUTE(mtk_ppe_debugfs_foe_all); static int -mtk_ppe_debugfs_foe_show_bind(struct seq_file *m, void *private) +mtk_ppe_debugfs_foe_bind_show(struct seq_file *m, void *private) { return mtk_ppe_debugfs_foe_show(m, private, true); } +DEFINE_SHOW_ATTRIBUTE(mtk_ppe_debugfs_foe_bind); -static int -mtk_ppe_debugfs_foe_open_all(struct inode *inode, struct file *file) +int mtk_ppe_debugfs_init(struct mtk_ppe *ppe, int index) { - return single_open(file, mtk_ppe_debugfs_foe_show_all, - inode->i_private); -} - -static int -mtk_ppe_debugfs_foe_open_bind(struct inode *inode, struct file *file) -{ - return single_open(file, mtk_ppe_debugfs_foe_show_bind, - inode->i_private); -} - -int mtk_ppe_debugfs_init(struct mtk_ppe *ppe) -{ - static const struct file_operations fops_all = { - .open = mtk_ppe_debugfs_foe_open_all, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, - }; - - static const struct file_operations fops_bind = { - .open = mtk_ppe_debugfs_foe_open_bind, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, - }; - struct dentry *root; - root = debugfs_create_dir("mtk_ppe", NULL); - debugfs_create_file("entries", S_IRUGO, root, ppe, &fops_all); - debugfs_create_file("bind", S_IRUGO, root, ppe, &fops_bind); + snprintf(ppe->dirname, sizeof(ppe->dirname), "ppe%d", index); + + root = debugfs_create_dir(ppe->dirname, NULL); + debugfs_create_file("entries", S_IRUGO, root, ppe, &mtk_ppe_debugfs_foe_all_fops); + debugfs_create_file("bind", S_IRUGO, root, ppe, &mtk_ppe_debugfs_foe_bind_fops); return 0; } diff --git a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c index 25dc3c3aa31d..28bbd1df3e30 100644 --- a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c +++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c @@ -52,18 +52,19 @@ static const struct rhashtable_params mtk_flow_ht_params = { }; static int -mtk_flow_set_ipv4_addr(struct mtk_foe_entry *foe, struct mtk_flow_data *data, - bool egress) +mtk_flow_set_ipv4_addr(struct mtk_eth *eth, struct mtk_foe_entry *foe, + struct mtk_flow_data *data, bool egress) { - return mtk_foe_entry_set_ipv4_tuple(foe, egress, + return mtk_foe_entry_set_ipv4_tuple(eth, foe, egress, data->v4.src_addr, data->src_port, data->v4.dst_addr, data->dst_port); } static int -mtk_flow_set_ipv6_addr(struct mtk_foe_entry *foe, struct mtk_flow_data *data) +mtk_flow_set_ipv6_addr(struct mtk_eth *eth, struct mtk_foe_entry *foe, + struct mtk_flow_data *data) { - return mtk_foe_entry_set_ipv6_tuple(foe, + return mtk_foe_entry_set_ipv6_tuple(eth, foe, data->v6.src_addr.s6_addr32, data->src_port, data->v6.dst_addr.s6_addr32, data->dst_port); } @@ -173,7 +174,7 @@ mtk_flow_get_dsa_port(struct net_device **dev) if (dp->cpu_dp->tag_ops->proto != DSA_TAG_PROTO_MTK) return -ENODEV; - *dev = dp->cpu_dp->master; + *dev = dsa_port_to_master(dp); return dp->index; #else @@ -190,16 +191,29 @@ mtk_flow_set_output_device(struct mtk_eth *eth, struct mtk_foe_entry *foe, int pse_port, dsa_port; if (mtk_flow_get_wdma_info(dev, dest_mac, &info) == 0) { - mtk_foe_entry_set_wdma(foe, info.wdma_idx, info.queue, info.bss, - info.wcid); - pse_port = 3; + mtk_foe_entry_set_wdma(eth, foe, info.wdma_idx, info.queue, + info.bss, info.wcid); + if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) { + switch (info.wdma_idx) { + case 0: + pse_port = 8; + break; + case 1: + pse_port = 9; + break; + default: + return -EINVAL; + } + } else { + pse_port = 3; + } *wed_index = info.wdma_idx; goto out; } dsa_port = mtk_flow_get_dsa_port(&dev); if (dsa_port >= 0) - mtk_foe_entry_set_dsa(foe, dsa_port); + mtk_foe_entry_set_dsa(eth, foe, dsa_port); if (dev == eth->netdev[0]) pse_port = 1; @@ -209,7 +223,7 @@ mtk_flow_set_output_device(struct mtk_eth *eth, struct mtk_foe_entry *foe, return -EOPNOTSUPP; out: - mtk_foe_entry_set_pse_port(foe, pse_port); + mtk_foe_entry_set_pse_port(eth, foe, pse_port); return 0; } @@ -333,9 +347,8 @@ mtk_flow_offload_replace(struct mtk_eth *eth, struct flow_cls_offload *f) !is_valid_ether_addr(data.eth.h_dest)) return -EINVAL; - err = mtk_foe_entry_prepare(&foe, offload_type, l4proto, 0, - data.eth.h_source, - data.eth.h_dest); + err = mtk_foe_entry_prepare(eth, &foe, offload_type, l4proto, 0, + data.eth.h_source, data.eth.h_dest); if (err) return err; @@ -360,7 +373,7 @@ mtk_flow_offload_replace(struct mtk_eth *eth, struct flow_cls_offload *f) data.v4.src_addr = addrs.key->src; data.v4.dst_addr = addrs.key->dst; - mtk_flow_set_ipv4_addr(&foe, &data, false); + mtk_flow_set_ipv4_addr(eth, &foe, &data, false); } if (addr_type == FLOW_DISSECTOR_KEY_IPV6_ADDRS) { @@ -371,7 +384,7 @@ mtk_flow_offload_replace(struct mtk_eth *eth, struct flow_cls_offload *f) data.v6.src_addr = addrs.key->src; data.v6.dst_addr = addrs.key->dst; - mtk_flow_set_ipv6_addr(&foe, &data); + mtk_flow_set_ipv6_addr(eth, &foe, &data); } flow_action_for_each(i, act, &rule->action) { @@ -401,7 +414,7 @@ mtk_flow_offload_replace(struct mtk_eth *eth, struct flow_cls_offload *f) } if (addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS) { - err = mtk_flow_set_ipv4_addr(&foe, &data, true); + err = mtk_flow_set_ipv4_addr(eth, &foe, &data, true); if (err) return err; } @@ -413,10 +426,10 @@ mtk_flow_offload_replace(struct mtk_eth *eth, struct flow_cls_offload *f) if (data.vlan.proto != htons(ETH_P_8021Q)) return -EOPNOTSUPP; - mtk_foe_entry_set_vlan(&foe, data.vlan.id); + mtk_foe_entry_set_vlan(eth, &foe, data.vlan.id); } if (data.pppoe.num == 1) - mtk_foe_entry_set_pppoe(&foe, data.pppoe.sid); + mtk_foe_entry_set_pppoe(eth, &foe, data.pppoe.sid); err = mtk_flow_set_output_device(eth, &foe, odev, data.eth.h_dest, &wed_index); @@ -434,7 +447,7 @@ mtk_flow_offload_replace(struct mtk_eth *eth, struct flow_cls_offload *f) memcpy(&entry->data, &foe, sizeof(entry->data)); entry->wed_index = wed_index; - err = mtk_foe_entry_commit(eth->ppe, entry); + err = mtk_foe_entry_commit(eth->ppe[entry->ppe_index], entry); if (err < 0) goto free; @@ -446,7 +459,7 @@ mtk_flow_offload_replace(struct mtk_eth *eth, struct flow_cls_offload *f) return 0; clear: - mtk_foe_entry_clear(eth->ppe, entry); + mtk_foe_entry_clear(eth->ppe[entry->ppe_index], entry); free: kfree(entry); if (wed_index >= 0) @@ -464,7 +477,7 @@ mtk_flow_offload_destroy(struct mtk_eth *eth, struct flow_cls_offload *f) if (!entry) return -ENOENT; - mtk_foe_entry_clear(eth->ppe, entry); + mtk_foe_entry_clear(eth->ppe[entry->ppe_index], entry); rhashtable_remove_fast(ð->flow_table, &entry->node, mtk_flow_ht_params); if (entry->wed_index >= 0) @@ -485,7 +498,7 @@ mtk_flow_offload_stats(struct mtk_eth *eth, struct flow_cls_offload *f) if (!entry) return -ENOENT; - idle = mtk_foe_entry_idle_time(eth->ppe, entry); + idle = mtk_foe_entry_idle_time(eth->ppe[entry->ppe_index], entry); f->stats.lastused = jiffies - idle * HZ; return 0; @@ -537,7 +550,7 @@ mtk_eth_setup_tc_block(struct net_device *dev, struct flow_block_offload *f) struct flow_block_cb *block_cb; flow_setup_cb_t *cb; - if (!eth->ppe || !eth->ppe->foe_table) + if (!eth->soc->offload_version) return -EOPNOTSUPP; if (f->binder_type != FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS) @@ -589,8 +602,5 @@ int mtk_eth_setup_tc(struct net_device *dev, enum tc_setup_type type, int mtk_eth_offload_init(struct mtk_eth *eth) { - if (!eth->ppe || !eth->ppe->foe_table) - return 0; - return rhashtable_init(ð->flow_table, &mtk_flow_ht_params); } diff --git a/drivers/net/ethernet/mediatek/mtk_ppe_regs.h b/drivers/net/ethernet/mediatek/mtk_ppe_regs.h index 0c45ea0900f1..59596d823d8b 100644 --- a/drivers/net/ethernet/mediatek/mtk_ppe_regs.h +++ b/drivers/net/ethernet/mediatek/mtk_ppe_regs.h @@ -21,6 +21,9 @@ #define MTK_PPE_GLO_CFG_BUSY BIT(31) #define MTK_PPE_FLOW_CFG 0x204 +#define MTK_PPE_MD_TOAP_BYP_CRSN0 BIT(1) +#define MTK_PPE_MD_TOAP_BYP_CRSN1 BIT(2) +#define MTK_PPE_MD_TOAP_BYP_CRSN2 BIT(3) #define MTK_PPE_FLOW_CFG_IP4_TCP_FRAG BIT(6) #define MTK_PPE_FLOW_CFG_IP4_UDP_FRAG BIT(7) #define MTK_PPE_FLOW_CFG_IP6_3T_ROUTE BIT(8) @@ -54,6 +57,7 @@ #define MTK_PPE_TB_CFG_HASH_MODE GENMASK(15, 14) #define MTK_PPE_TB_CFG_SCAN_MODE GENMASK(17, 16) #define MTK_PPE_TB_CFG_HASH_DEBUG GENMASK(19, 18) +#define MTK_PPE_TB_CFG_INFO_SEL BIT(20) enum { MTK_PPE_SCAN_MODE_DISABLED, @@ -112,6 +116,8 @@ enum { #define MTK_PPE_DEFAULT_CPU_PORT 0x248 #define MTK_PPE_DEFAULT_CPU_PORT_MASK(_n) (GENMASK(2, 0) << ((_n) * 4)) +#define MTK_PPE_DEFAULT_CPU_PORT1 0x24c + #define MTK_PPE_MTU_DROP 0x308 #define MTK_PPE_VLAN_MTU0 0x30c @@ -141,4 +147,6 @@ enum { #define MTK_PPE_MIB_CACHE_CTL_EN BIT(0) #define MTK_PPE_MIB_CACHE_CTL_FLUSH BIT(2) +#define MTK_PPE_SBW_CTRL 0x374 + #endif diff --git a/drivers/net/ethernet/mediatek/mtk_star_emac.c b/drivers/net/ethernet/mediatek/mtk_star_emac.c index 3f0e5e64de50..7e890f81148e 100644 --- a/drivers/net/ethernet/mediatek/mtk_star_emac.c +++ b/drivers/net/ethernet/mediatek/mtk_star_emac.c @@ -1255,7 +1255,7 @@ static const struct net_device_ops mtk_star_netdev_ops = { static void mtk_star_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { - strlcpy(info->driver, MTK_STAR_DRVNAME, sizeof(info->driver)); + strscpy(info->driver, MTK_STAR_DRVNAME, sizeof(info->driver)); } /* TODO Add ethtool stats. */ @@ -1651,8 +1651,7 @@ static int mtk_star_probe(struct platform_device *pdev) ndev->netdev_ops = &mtk_star_netdev_ops; ndev->ethtool_ops = &mtk_star_ethtool_ops; - netif_napi_add(ndev, &priv->rx_napi, mtk_star_rx_poll, - NAPI_POLL_WEIGHT); + netif_napi_add(ndev, &priv->rx_napi, mtk_star_rx_poll); netif_napi_add_tx(ndev, &priv->tx_napi, mtk_star_tx_poll); return devm_register_netdev(dev, ndev); diff --git a/drivers/net/ethernet/mediatek/mtk_wed.c b/drivers/net/ethernet/mediatek/mtk_wed.c index 29be2fcafea3..099b6e0df619 100644 --- a/drivers/net/ethernet/mediatek/mtk_wed.c +++ b/drivers/net/ethernet/mediatek/mtk_wed.c @@ -25,6 +25,11 @@ #define MTK_WED_TX_RING_SIZE 2048 #define MTK_WED_WDMA_RING_SIZE 1024 +#define MTK_WED_MAX_GROUP_SIZE 0x100 +#define MTK_WED_VLD_GROUP_SIZE 0x40 +#define MTK_WED_PER_GROUP_PKT 128 + +#define MTK_WED_FBUF_SIZE 128 static struct mtk_wed_hw *hw_list[2]; static DEFINE_MUTEX(hw_lock); @@ -80,11 +85,31 @@ static struct mtk_wed_hw * mtk_wed_assign(struct mtk_wed_device *dev) { struct mtk_wed_hw *hw; + int i; + + if (dev->wlan.bus_type == MTK_WED_BUS_PCIE) { + hw = hw_list[pci_domain_nr(dev->wlan.pci_dev->bus)]; + if (!hw) + return NULL; + + if (!hw->wed_dev) + goto out; + + if (hw->version == 1) + return NULL; + + /* MT7986 WED devices do not have any pcie slot restrictions */ + } + /* MT7986 PCIE or AXI */ + for (i = 0; i < ARRAY_SIZE(hw_list); i++) { + hw = hw_list[i]; + if (hw && !hw->wed_dev) + goto out; + } - hw = hw_list[pci_domain_nr(dev->wlan.pci_dev->bus)]; - if (!hw || hw->wed_dev) - return NULL; + return NULL; +out: hw->wed_dev = dev; return hw; } @@ -150,10 +175,17 @@ mtk_wed_buffer_alloc(struct mtk_wed_device *dev) desc->buf0 = cpu_to_le32(buf_phys); desc->buf1 = cpu_to_le32(buf_phys + txd_size); - ctrl = FIELD_PREP(MTK_WDMA_DESC_CTRL_LEN0, txd_size) | - FIELD_PREP(MTK_WDMA_DESC_CTRL_LEN1, - MTK_WED_BUF_SIZE - txd_size) | - MTK_WDMA_DESC_CTRL_LAST_SEG1; + + if (dev->hw->version == 1) + ctrl = FIELD_PREP(MTK_WDMA_DESC_CTRL_LEN0, txd_size) | + FIELD_PREP(MTK_WDMA_DESC_CTRL_LEN1, + MTK_WED_BUF_SIZE - txd_size) | + MTK_WDMA_DESC_CTRL_LAST_SEG1; + else + ctrl = FIELD_PREP(MTK_WDMA_DESC_CTRL_LEN0, txd_size) | + FIELD_PREP(MTK_WDMA_DESC_CTRL_LEN1_V2, + MTK_WED_BUF_SIZE - txd_size) | + MTK_WDMA_DESC_CTRL_LAST_SEG0; desc->ctrl = cpu_to_le32(ctrl); desc->info = 0; desc++; @@ -209,7 +241,7 @@ mtk_wed_free_ring(struct mtk_wed_device *dev, struct mtk_wed_ring *ring) if (!ring->desc) return; - dma_free_coherent(dev->hw->dev, ring->size * sizeof(*ring->desc), + dma_free_coherent(dev->hw->dev, ring->size * ring->desc_size, ring->desc, ring->desc_phys); } @@ -229,6 +261,14 @@ mtk_wed_set_ext_int(struct mtk_wed_device *dev, bool en) { u32 mask = MTK_WED_EXT_INT_STATUS_ERROR_MASK; + if (dev->hw->version == 1) + mask |= MTK_WED_EXT_INT_STATUS_TX_DRV_R_RESP_ERR; + else + mask |= MTK_WED_EXT_INT_STATUS_RX_FBUF_LO_TH | + MTK_WED_EXT_INT_STATUS_RX_FBUF_HI_TH | + MTK_WED_EXT_INT_STATUS_RX_DRV_COHERENT | + MTK_WED_EXT_INT_STATUS_TX_DMA_W_RESP_ERR; + if (!dev->hw->num_flows) mask &= ~MTK_WED_EXT_INT_STATUS_TKID_WO_PYLD; @@ -237,9 +277,54 @@ mtk_wed_set_ext_int(struct mtk_wed_device *dev, bool en) } static void +mtk_wed_set_512_support(struct mtk_wed_device *dev, bool enable) +{ + if (enable) { + wed_w32(dev, MTK_WED_TXDP_CTRL, MTK_WED_TXDP_DW9_OVERWR); + wed_w32(dev, MTK_WED_TXP_DW1, + FIELD_PREP(MTK_WED_WPDMA_WRITE_TXP, 0x0103)); + } else { + wed_w32(dev, MTK_WED_TXP_DW1, + FIELD_PREP(MTK_WED_WPDMA_WRITE_TXP, 0x0100)); + wed_clr(dev, MTK_WED_TXDP_CTRL, MTK_WED_TXDP_DW9_OVERWR); + } +} + +static void +mtk_wed_dma_disable(struct mtk_wed_device *dev) +{ + wed_clr(dev, MTK_WED_WPDMA_GLO_CFG, + MTK_WED_WPDMA_GLO_CFG_TX_DRV_EN | + MTK_WED_WPDMA_GLO_CFG_RX_DRV_EN); + + wed_clr(dev, MTK_WED_WDMA_GLO_CFG, MTK_WED_WDMA_GLO_CFG_RX_DRV_EN); + + wed_clr(dev, MTK_WED_GLO_CFG, + MTK_WED_GLO_CFG_TX_DMA_EN | + MTK_WED_GLO_CFG_RX_DMA_EN); + + wdma_m32(dev, MTK_WDMA_GLO_CFG, + MTK_WDMA_GLO_CFG_TX_DMA_EN | + MTK_WDMA_GLO_CFG_RX_INFO1_PRERES | + MTK_WDMA_GLO_CFG_RX_INFO2_PRERES, 0); + + if (dev->hw->version == 1) { + regmap_write(dev->hw->mirror, dev->hw->index * 4, 0); + wdma_m32(dev, MTK_WDMA_GLO_CFG, + MTK_WDMA_GLO_CFG_RX_INFO3_PRERES, 0); + } else { + wed_clr(dev, MTK_WED_WPDMA_GLO_CFG, + MTK_WED_WPDMA_GLO_CFG_RX_DRV_R0_PKT_PROC | + MTK_WED_WPDMA_GLO_CFG_RX_DRV_R0_CRX_SYNC); + + mtk_wed_set_512_support(dev, false); + } +} + +static void mtk_wed_stop(struct mtk_wed_device *dev) { - regmap_write(dev->hw->mirror, dev->hw->index * 4, 0); + mtk_wed_dma_disable(dev); mtk_wed_set_ext_int(dev, false); wed_clr(dev, MTK_WED_CTRL, @@ -252,21 +337,11 @@ mtk_wed_stop(struct mtk_wed_device *dev) wdma_w32(dev, MTK_WDMA_INT_MASK, 0); wdma_w32(dev, MTK_WDMA_INT_GRP2, 0); wed_w32(dev, MTK_WED_WPDMA_INT_MASK, 0); - - wed_clr(dev, MTK_WED_GLO_CFG, - MTK_WED_GLO_CFG_TX_DMA_EN | - MTK_WED_GLO_CFG_RX_DMA_EN); - wed_clr(dev, MTK_WED_WPDMA_GLO_CFG, - MTK_WED_WPDMA_GLO_CFG_TX_DRV_EN | - MTK_WED_WPDMA_GLO_CFG_RX_DRV_EN); - wed_clr(dev, MTK_WED_WDMA_GLO_CFG, - MTK_WED_WDMA_GLO_CFG_RX_DRV_EN); } static void mtk_wed_detach(struct mtk_wed_device *dev) { - struct device_node *wlan_node = dev->wlan.pci_dev->dev.of_node; struct mtk_wed_hw *hw = dev->hw; mutex_lock(&hw_lock); @@ -281,9 +356,14 @@ mtk_wed_detach(struct mtk_wed_device *dev) mtk_wed_free_buffer(dev); mtk_wed_free_tx_rings(dev); - if (of_dma_is_coherent(wlan_node)) - regmap_update_bits(hw->hifsys, HIFSYS_DMA_AG_MAP, - BIT(hw->index), BIT(hw->index)); + if (dev->wlan.bus_type == MTK_WED_BUS_PCIE) { + struct device_node *wlan_node; + + wlan_node = dev->wlan.pci_dev->dev.of_node; + if (of_dma_is_coherent(wlan_node) && hw->hifsys) + regmap_update_bits(hw->hifsys, HIFSYS_DMA_AG_MAP, + BIT(hw->index), BIT(hw->index)); + } if (!hw_list[!hw->index]->wed_dev && hw->eth->dma_dev != hw->eth->dev) @@ -296,14 +376,76 @@ mtk_wed_detach(struct mtk_wed_device *dev) mutex_unlock(&hw_lock); } +#define PCIE_BASE_ADDR0 0x11280000 +static void +mtk_wed_bus_init(struct mtk_wed_device *dev) +{ + switch (dev->wlan.bus_type) { + case MTK_WED_BUS_PCIE: { + struct device_node *np = dev->hw->eth->dev->of_node; + struct regmap *regs; + + regs = syscon_regmap_lookup_by_phandle(np, + "mediatek,wed-pcie"); + if (IS_ERR(regs)) + break; + + regmap_update_bits(regs, 0, BIT(0), BIT(0)); + + wed_w32(dev, MTK_WED_PCIE_INT_CTRL, + FIELD_PREP(MTK_WED_PCIE_INT_CTRL_POLL_EN, 2)); + + /* pcie interrupt control: pola/source selection */ + wed_set(dev, MTK_WED_PCIE_INT_CTRL, + MTK_WED_PCIE_INT_CTRL_MSK_EN_POLA | + FIELD_PREP(MTK_WED_PCIE_INT_CTRL_SRC_SEL, 1)); + wed_r32(dev, MTK_WED_PCIE_INT_CTRL); + + wed_w32(dev, MTK_WED_PCIE_CFG_INTM, PCIE_BASE_ADDR0 | 0x180); + wed_w32(dev, MTK_WED_PCIE_CFG_BASE, PCIE_BASE_ADDR0 | 0x184); + + /* pcie interrupt status trigger register */ + wed_w32(dev, MTK_WED_PCIE_INT_TRIGGER, BIT(24)); + wed_r32(dev, MTK_WED_PCIE_INT_TRIGGER); + + /* pola setting */ + wed_set(dev, MTK_WED_PCIE_INT_CTRL, + MTK_WED_PCIE_INT_CTRL_MSK_EN_POLA); + break; + } + case MTK_WED_BUS_AXI: + wed_set(dev, MTK_WED_WPDMA_INT_CTRL, + MTK_WED_WPDMA_INT_CTRL_SIG_SRC | + FIELD_PREP(MTK_WED_WPDMA_INT_CTRL_SRC_SEL, 0)); + break; + default: + break; + } +} + +static void +mtk_wed_set_wpdma(struct mtk_wed_device *dev) +{ + if (dev->hw->version == 1) { + wed_w32(dev, MTK_WED_WPDMA_CFG_BASE, dev->wlan.wpdma_phys); + } else { + mtk_wed_bus_init(dev); + + wed_w32(dev, MTK_WED_WPDMA_CFG_BASE, dev->wlan.wpdma_int); + wed_w32(dev, MTK_WED_WPDMA_CFG_INT_MASK, dev->wlan.wpdma_mask); + wed_w32(dev, MTK_WED_WPDMA_CFG_TX, dev->wlan.wpdma_tx); + wed_w32(dev, MTK_WED_WPDMA_CFG_TX_FREE, dev->wlan.wpdma_txfree); + } +} + static void mtk_wed_hw_init_early(struct mtk_wed_device *dev) { u32 mask, set; - u32 offset; mtk_wed_stop(dev); mtk_wed_reset(dev, MTK_WED_RESET_WED); + mtk_wed_set_wpdma(dev); mask = MTK_WED_WDMA_GLO_CFG_BT_SIZE | MTK_WED_WDMA_GLO_CFG_DYNAMIC_DMAD_RECYCLE | @@ -313,14 +455,33 @@ mtk_wed_hw_init_early(struct mtk_wed_device *dev) MTK_WED_WDMA_GLO_CFG_IDLE_DMAD_SUPPLY; wed_m32(dev, MTK_WED_WDMA_GLO_CFG, mask, set); - wdma_set(dev, MTK_WDMA_GLO_CFG, MTK_WDMA_GLO_CFG_RX_INFO_PRERES); + if (dev->hw->version == 1) { + u32 offset = dev->hw->index ? 0x04000400 : 0; - offset = dev->hw->index ? 0x04000400 : 0; - wed_w32(dev, MTK_WED_WDMA_OFFSET0, 0x2a042a20 + offset); - wed_w32(dev, MTK_WED_WDMA_OFFSET1, 0x29002800 + offset); + wdma_set(dev, MTK_WDMA_GLO_CFG, + MTK_WDMA_GLO_CFG_RX_INFO1_PRERES | + MTK_WDMA_GLO_CFG_RX_INFO2_PRERES | + MTK_WDMA_GLO_CFG_RX_INFO3_PRERES); - wed_w32(dev, MTK_WED_PCIE_CFG_BASE, MTK_PCIE_BASE(dev->hw->index)); - wed_w32(dev, MTK_WED_WPDMA_CFG_BASE, dev->wlan.wpdma_phys); + wed_w32(dev, MTK_WED_WDMA_OFFSET0, 0x2a042a20 + offset); + wed_w32(dev, MTK_WED_WDMA_OFFSET1, 0x29002800 + offset); + wed_w32(dev, MTK_WED_PCIE_CFG_BASE, + MTK_PCIE_BASE(dev->hw->index)); + } else { + wed_w32(dev, MTK_WED_WDMA_CFG_BASE, dev->hw->wdma_phy); + wed_set(dev, MTK_WED_CTRL, MTK_WED_CTRL_ETH_DMAD_FMT); + wed_w32(dev, MTK_WED_WDMA_OFFSET0, + FIELD_PREP(MTK_WED_WDMA_OFST0_GLO_INTS, + MTK_WDMA_INT_STATUS) | + FIELD_PREP(MTK_WED_WDMA_OFST0_GLO_CFG, + MTK_WDMA_GLO_CFG)); + + wed_w32(dev, MTK_WED_WDMA_OFFSET1, + FIELD_PREP(MTK_WED_WDMA_OFST1_TX_CTRL, + MTK_WDMA_RING_TX(0)) | + FIELD_PREP(MTK_WED_WDMA_OFST1_RX_CTRL, + MTK_WDMA_RING_RX(0))); + } } static void @@ -340,37 +501,65 @@ mtk_wed_hw_init(struct mtk_wed_device *dev) wed_w32(dev, MTK_WED_TX_BM_BASE, dev->buf_ring.desc_phys); - wed_w32(dev, MTK_WED_TX_BM_TKID, - FIELD_PREP(MTK_WED_TX_BM_TKID_START, - dev->wlan.token_start) | - FIELD_PREP(MTK_WED_TX_BM_TKID_END, - dev->wlan.token_start + dev->wlan.nbuf - 1)); - wed_w32(dev, MTK_WED_TX_BM_BUF_LEN, MTK_WED_PKT_SIZE); - wed_w32(dev, MTK_WED_TX_BM_DYN_THR, - FIELD_PREP(MTK_WED_TX_BM_DYN_THR_LO, 1) | - MTK_WED_TX_BM_DYN_THR_HI); + if (dev->hw->version == 1) { + wed_w32(dev, MTK_WED_TX_BM_TKID, + FIELD_PREP(MTK_WED_TX_BM_TKID_START, + dev->wlan.token_start) | + FIELD_PREP(MTK_WED_TX_BM_TKID_END, + dev->wlan.token_start + + dev->wlan.nbuf - 1)); + wed_w32(dev, MTK_WED_TX_BM_DYN_THR, + FIELD_PREP(MTK_WED_TX_BM_DYN_THR_LO, 1) | + MTK_WED_TX_BM_DYN_THR_HI); + } else { + wed_w32(dev, MTK_WED_TX_BM_TKID_V2, + FIELD_PREP(MTK_WED_TX_BM_TKID_START, + dev->wlan.token_start) | + FIELD_PREP(MTK_WED_TX_BM_TKID_END, + dev->wlan.token_start + + dev->wlan.nbuf - 1)); + wed_w32(dev, MTK_WED_TX_BM_DYN_THR, + FIELD_PREP(MTK_WED_TX_BM_DYN_THR_LO_V2, 0) | + MTK_WED_TX_BM_DYN_THR_HI_V2); + wed_w32(dev, MTK_WED_TX_TKID_CTRL, + MTK_WED_TX_TKID_CTRL_PAUSE | + FIELD_PREP(MTK_WED_TX_TKID_CTRL_VLD_GRP_NUM, + dev->buf_ring.size / 128) | + FIELD_PREP(MTK_WED_TX_TKID_CTRL_RSV_GRP_NUM, + dev->buf_ring.size / 128)); + wed_w32(dev, MTK_WED_TX_TKID_DYN_THR, + FIELD_PREP(MTK_WED_TX_TKID_DYN_THR_LO, 0) | + MTK_WED_TX_TKID_DYN_THR_HI); + } mtk_wed_reset(dev, MTK_WED_RESET_TX_BM); - wed_set(dev, MTK_WED_CTRL, - MTK_WED_CTRL_WED_TX_BM_EN | - MTK_WED_CTRL_WED_TX_FREE_AGENT_EN); + if (dev->hw->version == 1) + wed_set(dev, MTK_WED_CTRL, + MTK_WED_CTRL_WED_TX_BM_EN | + MTK_WED_CTRL_WED_TX_FREE_AGENT_EN); + else + wed_clr(dev, MTK_WED_TX_TKID_CTRL, MTK_WED_TX_TKID_CTRL_PAUSE); wed_clr(dev, MTK_WED_TX_BM_CTRL, MTK_WED_TX_BM_CTRL_PAUSE); } static void -mtk_wed_ring_reset(struct mtk_wdma_desc *desc, int size) +mtk_wed_ring_reset(struct mtk_wed_ring *ring, int size) { + void *head = (void *)ring->desc; int i; for (i = 0; i < size; i++) { - desc[i].buf0 = 0; - desc[i].ctrl = cpu_to_le32(MTK_WDMA_DESC_CTRL_DMA_DONE); - desc[i].buf1 = 0; - desc[i].info = 0; + struct mtk_wdma_desc *desc; + + desc = (struct mtk_wdma_desc *)(head + i * ring->desc_size); + desc->buf0 = 0; + desc->ctrl = cpu_to_le32(MTK_WDMA_DESC_CTRL_DMA_DONE); + desc->buf1 = 0; + desc->info = 0; } } @@ -421,12 +610,10 @@ mtk_wed_reset_dma(struct mtk_wed_device *dev) int i; for (i = 0; i < ARRAY_SIZE(dev->tx_ring); i++) { - struct mtk_wdma_desc *desc = dev->tx_ring[i].desc; - - if (!desc) + if (!dev->tx_ring[i].desc) continue; - mtk_wed_ring_reset(desc, MTK_WED_TX_RING_SIZE); + mtk_wed_ring_reset(&dev->tx_ring[i], MTK_WED_TX_RING_SIZE); } if (mtk_wed_poll_busy(dev)) @@ -483,16 +670,16 @@ mtk_wed_reset_dma(struct mtk_wed_device *dev) static int mtk_wed_ring_alloc(struct mtk_wed_device *dev, struct mtk_wed_ring *ring, - int size) + int size, u32 desc_size) { - ring->desc = dma_alloc_coherent(dev->hw->dev, - size * sizeof(*ring->desc), + ring->desc = dma_alloc_coherent(dev->hw->dev, size * desc_size, &ring->desc_phys, GFP_KERNEL); if (!ring->desc) return -ENOMEM; + ring->desc_size = desc_size; ring->size = size; - mtk_wed_ring_reset(ring->desc, size); + mtk_wed_ring_reset(ring, size); return 0; } @@ -500,9 +687,10 @@ mtk_wed_ring_alloc(struct mtk_wed_device *dev, struct mtk_wed_ring *ring, static int mtk_wed_wdma_ring_setup(struct mtk_wed_device *dev, int idx, int size) { + u32 desc_size = sizeof(struct mtk_wdma_desc) * dev->hw->version; struct mtk_wed_ring *wdma = &dev->tx_wdma[idx]; - if (mtk_wed_ring_alloc(dev, wdma, MTK_WED_WDMA_RING_SIZE)) + if (mtk_wed_ring_alloc(dev, wdma, MTK_WED_WDMA_RING_SIZE, desc_size)) return -ENOMEM; wdma_w32(dev, MTK_WDMA_RING_RX(idx) + MTK_WED_RING_OFS_BASE, @@ -520,43 +708,63 @@ mtk_wed_wdma_ring_setup(struct mtk_wed_device *dev, int idx, int size) } static void -mtk_wed_start(struct mtk_wed_device *dev, u32 irq_mask) +mtk_wed_configure_irq(struct mtk_wed_device *dev, u32 irq_mask) { - u32 wdma_mask; - u32 val; - int i; - - for (i = 0; i < ARRAY_SIZE(dev->tx_wdma); i++) - if (!dev->tx_wdma[i].desc) - mtk_wed_wdma_ring_setup(dev, i, 16); - - wdma_mask = FIELD_PREP(MTK_WDMA_INT_MASK_RX_DONE, GENMASK(1, 0)); - - mtk_wed_hw_init(dev); + u32 wdma_mask = FIELD_PREP(MTK_WDMA_INT_MASK_RX_DONE, GENMASK(1, 0)); + /* wed control cr set */ wed_set(dev, MTK_WED_CTRL, MTK_WED_CTRL_WDMA_INT_AGENT_EN | MTK_WED_CTRL_WPDMA_INT_AGENT_EN | MTK_WED_CTRL_WED_TX_BM_EN | MTK_WED_CTRL_WED_TX_FREE_AGENT_EN); - wed_w32(dev, MTK_WED_PCIE_INT_TRIGGER, MTK_WED_PCIE_INT_TRIGGER_STATUS); + if (dev->hw->version == 1) { + wed_w32(dev, MTK_WED_PCIE_INT_TRIGGER, + MTK_WED_PCIE_INT_TRIGGER_STATUS); - wed_w32(dev, MTK_WED_WPDMA_INT_TRIGGER, - MTK_WED_WPDMA_INT_TRIGGER_RX_DONE | - MTK_WED_WPDMA_INT_TRIGGER_TX_DONE); + wed_w32(dev, MTK_WED_WPDMA_INT_TRIGGER, + MTK_WED_WPDMA_INT_TRIGGER_RX_DONE | + MTK_WED_WPDMA_INT_TRIGGER_TX_DONE); - wed_set(dev, MTK_WED_WPDMA_INT_CTRL, - MTK_WED_WPDMA_INT_CTRL_SUBRT_ADV); + wed_clr(dev, MTK_WED_WDMA_INT_CTRL, wdma_mask); + } else { + /* initail tx interrupt trigger */ + wed_w32(dev, MTK_WED_WPDMA_INT_CTRL_TX, + MTK_WED_WPDMA_INT_CTRL_TX0_DONE_EN | + MTK_WED_WPDMA_INT_CTRL_TX0_DONE_CLR | + MTK_WED_WPDMA_INT_CTRL_TX1_DONE_EN | + MTK_WED_WPDMA_INT_CTRL_TX1_DONE_CLR | + FIELD_PREP(MTK_WED_WPDMA_INT_CTRL_TX0_DONE_TRIG, + dev->wlan.tx_tbit[0]) | + FIELD_PREP(MTK_WED_WPDMA_INT_CTRL_TX1_DONE_TRIG, + dev->wlan.tx_tbit[1])); + + /* initail txfree interrupt trigger */ + wed_w32(dev, MTK_WED_WPDMA_INT_CTRL_TX_FREE, + MTK_WED_WPDMA_INT_CTRL_TX_FREE_DONE_EN | + MTK_WED_WPDMA_INT_CTRL_TX_FREE_DONE_CLR | + FIELD_PREP(MTK_WED_WPDMA_INT_CTRL_TX_FREE_DONE_TRIG, + dev->wlan.txfree_tbit)); + + wed_w32(dev, MTK_WED_WDMA_INT_CLR, wdma_mask); + wed_set(dev, MTK_WED_WDMA_INT_CTRL, + FIELD_PREP(MTK_WED_WDMA_INT_CTRL_POLL_SRC_SEL, + dev->wdma_idx)); + } wed_w32(dev, MTK_WED_WDMA_INT_TRIGGER, wdma_mask); - wed_clr(dev, MTK_WED_WDMA_INT_CTRL, wdma_mask); wdma_w32(dev, MTK_WDMA_INT_MASK, wdma_mask); wdma_w32(dev, MTK_WDMA_INT_GRP2, wdma_mask); - wed_w32(dev, MTK_WED_WPDMA_INT_MASK, irq_mask); wed_w32(dev, MTK_WED_INT_MASK, irq_mask); +} + +static void +mtk_wed_dma_enable(struct mtk_wed_device *dev) +{ + wed_set(dev, MTK_WED_WPDMA_INT_CTRL, MTK_WED_WPDMA_INT_CTRL_SUBRT_ADV); wed_set(dev, MTK_WED_GLO_CFG, MTK_WED_GLO_CFG_TX_DMA_EN | @@ -567,16 +775,54 @@ mtk_wed_start(struct mtk_wed_device *dev, u32 irq_mask) wed_set(dev, MTK_WED_WDMA_GLO_CFG, MTK_WED_WDMA_GLO_CFG_RX_DRV_EN); + wdma_set(dev, MTK_WDMA_GLO_CFG, + MTK_WDMA_GLO_CFG_TX_DMA_EN | + MTK_WDMA_GLO_CFG_RX_INFO1_PRERES | + MTK_WDMA_GLO_CFG_RX_INFO2_PRERES); + + if (dev->hw->version == 1) { + wdma_set(dev, MTK_WDMA_GLO_CFG, + MTK_WDMA_GLO_CFG_RX_INFO3_PRERES); + } else { + wed_set(dev, MTK_WED_WPDMA_CTRL, + MTK_WED_WPDMA_CTRL_SDL1_FIXED); + + wed_set(dev, MTK_WED_WPDMA_GLO_CFG, + MTK_WED_WPDMA_GLO_CFG_RX_DRV_R0_PKT_PROC | + MTK_WED_WPDMA_GLO_CFG_RX_DRV_R0_CRX_SYNC); + + wed_clr(dev, MTK_WED_WPDMA_GLO_CFG, + MTK_WED_WPDMA_GLO_CFG_TX_TKID_KEEP | + MTK_WED_WPDMA_GLO_CFG_TX_DMAD_DW3_PREV); + } +} + +static void +mtk_wed_start(struct mtk_wed_device *dev, u32 irq_mask) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(dev->tx_wdma); i++) + if (!dev->tx_wdma[i].desc) + mtk_wed_wdma_ring_setup(dev, i, 16); + + mtk_wed_hw_init(dev); + mtk_wed_configure_irq(dev, irq_mask); + mtk_wed_set_ext_int(dev, true); - val = dev->wlan.wpdma_phys | - MTK_PCIE_MIRROR_MAP_EN | - FIELD_PREP(MTK_PCIE_MIRROR_MAP_WED_ID, dev->hw->index); - if (dev->hw->index) - val |= BIT(1); - val |= BIT(0); - regmap_write(dev->hw->mirror, dev->hw->index * 4, val); + if (dev->hw->version == 1) { + u32 val = dev->wlan.wpdma_phys | MTK_PCIE_MIRROR_MAP_EN | + FIELD_PREP(MTK_PCIE_MIRROR_MAP_WED_ID, + dev->hw->index); + + val |= BIT(0) | (BIT(1) * !!dev->hw->index); + regmap_write(dev->hw->mirror, dev->hw->index * 4, val); + } else { + mtk_wed_set_512_support(dev, true); + } + mtk_wed_dma_enable(dev); dev->running = true; } @@ -585,12 +831,14 @@ mtk_wed_attach(struct mtk_wed_device *dev) __releases(RCU) { struct mtk_wed_hw *hw; + struct device *device; int ret = 0; RCU_LOCKDEP_WARN(!rcu_read_lock_held(), "mtk_wed_attach without holding the RCU read lock"); - if (pci_domain_nr(dev->wlan.pci_dev->bus) > 1 || + if ((dev->wlan.bus_type == MTK_WED_BUS_PCIE && + pci_domain_nr(dev->wlan.pci_dev->bus) > 1) || !try_module_get(THIS_MODULE)) ret = -ENODEV; @@ -608,7 +856,11 @@ mtk_wed_attach(struct mtk_wed_device *dev) goto out; } - dev_info(&dev->wlan.pci_dev->dev, "attaching wed device %d\n", hw->index); + device = dev->wlan.bus_type == MTK_WED_BUS_PCIE + ? &dev->wlan.pci_dev->dev + : &dev->wlan.platform_dev->dev; + dev_info(device, "attaching wed device %d version %d\n", + hw->index, hw->version); dev->hw = hw; dev->dev = hw->dev; @@ -626,7 +878,9 @@ mtk_wed_attach(struct mtk_wed_device *dev) } mtk_wed_hw_init_early(dev); - regmap_update_bits(hw->hifsys, HIFSYS_DMA_AG_MAP, BIT(hw->index), 0); + if (hw->hifsys) + regmap_update_bits(hw->hifsys, HIFSYS_DMA_AG_MAP, + BIT(hw->index), 0); out: mutex_unlock(&hw_lock); @@ -653,7 +907,8 @@ mtk_wed_tx_ring_setup(struct mtk_wed_device *dev, int idx, void __iomem *regs) BUG_ON(idx >= ARRAY_SIZE(dev->tx_ring)); - if (mtk_wed_ring_alloc(dev, ring, MTK_WED_TX_RING_SIZE)) + if (mtk_wed_ring_alloc(dev, ring, MTK_WED_TX_RING_SIZE, + sizeof(*ring->desc))) return -ENOMEM; if (mtk_wed_wdma_ring_setup(dev, idx, MTK_WED_WDMA_RING_SIZE)) @@ -680,21 +935,21 @@ static int mtk_wed_txfree_ring_setup(struct mtk_wed_device *dev, void __iomem *regs) { struct mtk_wed_ring *ring = &dev->txfree_ring; - int i; + int i, index = dev->hw->version == 1; /* * For txfree event handling, the same DMA ring is shared between WED * and WLAN. The WLAN driver accesses the ring index registers through * WED */ - ring->reg_base = MTK_WED_RING_RX(1); + ring->reg_base = MTK_WED_RING_RX(index); ring->wpdma = regs; for (i = 0; i < 12; i += 4) { u32 val = readl(regs + i); - wed_w32(dev, MTK_WED_RING_RX(1) + i, val); - wed_w32(dev, MTK_WED_WPDMA_RING_RX(1) + i, val); + wed_w32(dev, MTK_WED_RING_RX(index) + i, val); + wed_w32(dev, MTK_WED_WPDMA_RING_RX(index) + i, val); } return 0; @@ -703,11 +958,19 @@ mtk_wed_txfree_ring_setup(struct mtk_wed_device *dev, void __iomem *regs) static u32 mtk_wed_irq_get(struct mtk_wed_device *dev, u32 mask) { - u32 val; + u32 val, ext_mask = MTK_WED_EXT_INT_STATUS_ERROR_MASK; + + if (dev->hw->version == 1) + ext_mask |= MTK_WED_EXT_INT_STATUS_TX_DRV_R_RESP_ERR; + else + ext_mask |= MTK_WED_EXT_INT_STATUS_RX_FBUF_LO_TH | + MTK_WED_EXT_INT_STATUS_RX_FBUF_HI_TH | + MTK_WED_EXT_INT_STATUS_RX_DRV_COHERENT | + MTK_WED_EXT_INT_STATUS_TX_DMA_W_RESP_ERR; val = wed_r32(dev, MTK_WED_EXT_INT_STATUS); wed_w32(dev, MTK_WED_EXT_INT_STATUS, val); - val &= MTK_WED_EXT_INT_STATUS_ERROR_MASK; + val &= ext_mask; if (!dev->hw->num_flows) val &= ~MTK_WED_EXT_INT_STATUS_TKID_WO_PYLD; if (val && net_ratelimit()) @@ -782,7 +1045,8 @@ out: } void mtk_wed_add_hw(struct device_node *np, struct mtk_eth *eth, - void __iomem *wdma, int index) + void __iomem *wdma, phys_addr_t wdma_phy, + int index) { static const struct mtk_wed_ops wed_ops = { .attach = mtk_wed_attach, @@ -829,26 +1093,33 @@ void mtk_wed_add_hw(struct device_node *np, struct mtk_eth *eth, hw = kzalloc(sizeof(*hw), GFP_KERNEL); if (!hw) goto unlock; + hw->node = np; hw->regs = regs; hw->eth = eth; hw->dev = &pdev->dev; + hw->wdma_phy = wdma_phy; hw->wdma = wdma; hw->index = index; hw->irq = irq; - hw->mirror = syscon_regmap_lookup_by_phandle(eth_np, - "mediatek,pcie-mirror"); - hw->hifsys = syscon_regmap_lookup_by_phandle(eth_np, - "mediatek,hifsys"); - if (IS_ERR(hw->mirror) || IS_ERR(hw->hifsys)) { - kfree(hw); - goto unlock; - } + hw->version = MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2) ? 2 : 1; + + if (hw->version == 1) { + hw->mirror = syscon_regmap_lookup_by_phandle(eth_np, + "mediatek,pcie-mirror"); + hw->hifsys = syscon_regmap_lookup_by_phandle(eth_np, + "mediatek,hifsys"); + if (IS_ERR(hw->mirror) || IS_ERR(hw->hifsys)) { + kfree(hw); + goto unlock; + } - if (!index) { - regmap_write(hw->mirror, 0, 0); - regmap_write(hw->mirror, 4, 0); + if (!index) { + regmap_write(hw->mirror, 0, 0); + regmap_write(hw->mirror, 4, 0); + } } + mtk_wed_hw_add_debugfs(hw); hw_list[index] = hw; diff --git a/drivers/net/ethernet/mediatek/mtk_wed.h b/drivers/net/ethernet/mediatek/mtk_wed.h index 981ec613f4b0..ae420ca01a48 100644 --- a/drivers/net/ethernet/mediatek/mtk_wed.h +++ b/drivers/net/ethernet/mediatek/mtk_wed.h @@ -18,11 +18,13 @@ struct mtk_wed_hw { struct regmap *hifsys; struct device *dev; void __iomem *wdma; + phys_addr_t wdma_phy; struct regmap *mirror; struct dentry *debugfs_dir; struct mtk_wed_device *wed_dev; u32 debugfs_reg; u32 num_flows; + u8 version; char dirname[5]; int irq; int index; @@ -101,14 +103,16 @@ wpdma_txfree_w32(struct mtk_wed_device *dev, u32 reg, u32 val) } void mtk_wed_add_hw(struct device_node *np, struct mtk_eth *eth, - void __iomem *wdma, int index); + void __iomem *wdma, phys_addr_t wdma_phy, + int index); void mtk_wed_exit(void); int mtk_wed_flow_add(int index); void mtk_wed_flow_remove(int index); #else static inline void mtk_wed_add_hw(struct device_node *np, struct mtk_eth *eth, - void __iomem *wdma, int index) + void __iomem *wdma, phys_addr_t wdma_phy, + int index) { } static inline void diff --git a/drivers/net/ethernet/mediatek/mtk_wed_debugfs.c b/drivers/net/ethernet/mediatek/mtk_wed_debugfs.c index a81d3fd1a439..f420f187e837 100644 --- a/drivers/net/ethernet/mediatek/mtk_wed_debugfs.c +++ b/drivers/net/ethernet/mediatek/mtk_wed_debugfs.c @@ -116,6 +116,9 @@ wed_txinfo_show(struct seq_file *s, void *data) DUMP_WDMA(WDMA_GLO_CFG), DUMP_WDMA_RING(WDMA_RING_RX(0)), DUMP_WDMA_RING(WDMA_RING_RX(1)), + + DUMP_STR("TX FREE"), + DUMP_WED(WED_RX_MIB(0)), }; struct mtk_wed_hw *hw = s->private; struct mtk_wed_device *dev = hw->wed_dev; diff --git a/drivers/net/ethernet/mediatek/mtk_wed_regs.h b/drivers/net/ethernet/mediatek/mtk_wed_regs.h index 0a0465ea58b4..e270fb336143 100644 --- a/drivers/net/ethernet/mediatek/mtk_wed_regs.h +++ b/drivers/net/ethernet/mediatek/mtk_wed_regs.h @@ -5,6 +5,7 @@ #define __MTK_WED_REGS_H #define MTK_WDMA_DESC_CTRL_LEN1 GENMASK(14, 0) +#define MTK_WDMA_DESC_CTRL_LEN1_V2 GENMASK(13, 0) #define MTK_WDMA_DESC_CTRL_LAST_SEG1 BIT(15) #define MTK_WDMA_DESC_CTRL_BURST BIT(16) #define MTK_WDMA_DESC_CTRL_LEN0 GENMASK(29, 16) @@ -41,6 +42,7 @@ struct mtk_wdma_desc { #define MTK_WED_CTRL_RESERVE_EN BIT(12) #define MTK_WED_CTRL_RESERVE_BUSY BIT(13) #define MTK_WED_CTRL_FINAL_DIDX_READ BIT(24) +#define MTK_WED_CTRL_ETH_DMAD_FMT BIT(25) #define MTK_WED_CTRL_MIB_READ_CLEAR BIT(28) #define MTK_WED_EXT_INT_STATUS 0x020 @@ -57,7 +59,8 @@ struct mtk_wdma_desc { #define MTK_WED_EXT_INT_STATUS_RX_DRV_INIT_WDMA_EN BIT(19) #define MTK_WED_EXT_INT_STATUS_RX_DRV_BM_DMAD_COHERENT BIT(20) #define MTK_WED_EXT_INT_STATUS_TX_DRV_R_RESP_ERR BIT(21) -#define MTK_WED_EXT_INT_STATUS_TX_DRV_W_RESP_ERR BIT(22) +#define MTK_WED_EXT_INT_STATUS_TX_DMA_R_RESP_ERR BIT(22) +#define MTK_WED_EXT_INT_STATUS_TX_DMA_W_RESP_ERR BIT(23) #define MTK_WED_EXT_INT_STATUS_RX_DRV_DMA_RECYCLE BIT(24) #define MTK_WED_EXT_INT_STATUS_ERROR_MASK (MTK_WED_EXT_INT_STATUS_TF_LEN_ERR | \ MTK_WED_EXT_INT_STATUS_TKID_WO_PYLD | \ @@ -65,8 +68,7 @@ struct mtk_wdma_desc { MTK_WED_EXT_INT_STATUS_RX_DRV_R_RESP_ERR | \ MTK_WED_EXT_INT_STATUS_RX_DRV_W_RESP_ERR | \ MTK_WED_EXT_INT_STATUS_RX_DRV_INIT_WDMA_EN | \ - MTK_WED_EXT_INT_STATUS_TX_DRV_R_RESP_ERR | \ - MTK_WED_EXT_INT_STATUS_TX_DRV_W_RESP_ERR) + MTK_WED_EXT_INT_STATUS_TX_DMA_R_RESP_ERR) #define MTK_WED_EXT_INT_MASK 0x028 @@ -81,6 +83,7 @@ struct mtk_wdma_desc { #define MTK_WED_TX_BM_BASE 0x084 #define MTK_WED_TX_BM_TKID 0x088 +#define MTK_WED_TX_BM_TKID_V2 0x0c8 #define MTK_WED_TX_BM_TKID_START GENMASK(15, 0) #define MTK_WED_TX_BM_TKID_END GENMASK(31, 16) @@ -94,7 +97,25 @@ struct mtk_wdma_desc { #define MTK_WED_TX_BM_DYN_THR 0x0a0 #define MTK_WED_TX_BM_DYN_THR_LO GENMASK(6, 0) +#define MTK_WED_TX_BM_DYN_THR_LO_V2 GENMASK(8, 0) #define MTK_WED_TX_BM_DYN_THR_HI GENMASK(22, 16) +#define MTK_WED_TX_BM_DYN_THR_HI_V2 GENMASK(24, 16) + +#define MTK_WED_TX_TKID_CTRL 0x0c0 +#define MTK_WED_TX_TKID_CTRL_VLD_GRP_NUM GENMASK(6, 0) +#define MTK_WED_TX_TKID_CTRL_RSV_GRP_NUM GENMASK(22, 16) +#define MTK_WED_TX_TKID_CTRL_PAUSE BIT(28) + +#define MTK_WED_TX_TKID_DYN_THR 0x0e0 +#define MTK_WED_TX_TKID_DYN_THR_LO GENMASK(6, 0) +#define MTK_WED_TX_TKID_DYN_THR_HI GENMASK(22, 16) + +#define MTK_WED_TXP_DW0 0x120 +#define MTK_WED_TXP_DW1 0x124 +#define MTK_WED_WPDMA_WRITE_TXP GENMASK(31, 16) +#define MTK_WED_TXDP_CTRL 0x130 +#define MTK_WED_TXDP_DW9_OVERWR BIT(9) +#define MTK_WED_RX_BM_TKID_MIB 0x1cc #define MTK_WED_INT_STATUS 0x200 #define MTK_WED_INT_MASK 0x204 @@ -125,6 +146,7 @@ struct mtk_wdma_desc { #define MTK_WED_RESET_IDX_RX GENMASK(17, 16) #define MTK_WED_TX_MIB(_n) (0x2a0 + (_n) * 4) +#define MTK_WED_RX_MIB(_n) (0x2e0 + (_n) * 4) #define MTK_WED_RING_TX(_n) (0x300 + (_n) * 0x10) @@ -155,21 +177,64 @@ struct mtk_wdma_desc { #define MTK_WED_WPDMA_GLO_CFG_BYTE_SWAP BIT(29) #define MTK_WED_WPDMA_GLO_CFG_RX_2B_OFFSET BIT(31) +/* CONFIG_MEDIATEK_NETSYS_V2 */ +#define MTK_WED_WPDMA_GLO_CFG_RX_DRV_R0_PKT_PROC BIT(4) +#define MTK_WED_WPDMA_GLO_CFG_RX_DRV_R1_PKT_PROC BIT(5) +#define MTK_WED_WPDMA_GLO_CFG_RX_DRV_R0_CRX_SYNC BIT(6) +#define MTK_WED_WPDMA_GLO_CFG_RX_DRV_R1_CRX_SYNC BIT(7) +#define MTK_WED_WPDMA_GLO_CFG_RX_DRV_EVENT_PKT_FMT_VER GENMASK(18, 16) +#define MTK_WED_WPDMA_GLO_CFG_RX_DRV_UNSUPPORT_FMT BIT(19) +#define MTK_WED_WPDMA_GLO_CFG_RX_DRV_UEVENT_PKT_FMT_CHK BIT(20) +#define MTK_WED_WPDMA_GLO_CFG_RX_DDONE2_WR BIT(21) +#define MTK_WED_WPDMA_GLO_CFG_TX_TKID_KEEP BIT(24) +#define MTK_WED_WPDMA_GLO_CFG_TX_DMAD_DW3_PREV BIT(28) + #define MTK_WED_WPDMA_RESET_IDX 0x50c #define MTK_WED_WPDMA_RESET_IDX_TX GENMASK(3, 0) #define MTK_WED_WPDMA_RESET_IDX_RX GENMASK(17, 16) +#define MTK_WED_WPDMA_CTRL 0x518 +#define MTK_WED_WPDMA_CTRL_SDL1_FIXED BIT(31) + #define MTK_WED_WPDMA_INT_CTRL 0x520 #define MTK_WED_WPDMA_INT_CTRL_SUBRT_ADV BIT(21) +#define MTK_WED_WPDMA_INT_CTRL_SIG_SRC BIT(22) +#define MTK_WED_WPDMA_INT_CTRL_SRC_SEL GENMASK(17, 16) #define MTK_WED_WPDMA_INT_MASK 0x524 +#define MTK_WED_WPDMA_INT_CTRL_TX 0x530 +#define MTK_WED_WPDMA_INT_CTRL_TX0_DONE_EN BIT(0) +#define MTK_WED_WPDMA_INT_CTRL_TX0_DONE_CLR BIT(1) +#define MTK_WED_WPDMA_INT_CTRL_TX0_DONE_TRIG GENMASK(6, 2) +#define MTK_WED_WPDMA_INT_CTRL_TX1_DONE_EN BIT(8) +#define MTK_WED_WPDMA_INT_CTRL_TX1_DONE_CLR BIT(9) +#define MTK_WED_WPDMA_INT_CTRL_TX1_DONE_TRIG GENMASK(14, 10) + +#define MTK_WED_WPDMA_INT_CTRL_RX 0x534 + +#define MTK_WED_WPDMA_INT_CTRL_TX_FREE 0x538 +#define MTK_WED_WPDMA_INT_CTRL_TX_FREE_DONE_EN BIT(0) +#define MTK_WED_WPDMA_INT_CTRL_TX_FREE_DONE_CLR BIT(1) +#define MTK_WED_WPDMA_INT_CTRL_TX_FREE_DONE_TRIG GENMASK(6, 2) + #define MTK_WED_PCIE_CFG_BASE 0x560 +#define MTK_WED_PCIE_CFG_BASE 0x560 +#define MTK_WED_PCIE_CFG_INTM 0x564 +#define MTK_WED_PCIE_CFG_MSIS 0x568 #define MTK_WED_PCIE_INT_TRIGGER 0x570 #define MTK_WED_PCIE_INT_TRIGGER_STATUS BIT(16) +#define MTK_WED_PCIE_INT_CTRL 0x57c +#define MTK_WED_PCIE_INT_CTRL_MSK_EN_POLA BIT(20) +#define MTK_WED_PCIE_INT_CTRL_SRC_SEL GENMASK(17, 16) +#define MTK_WED_PCIE_INT_CTRL_POLL_EN GENMASK(13, 12) + #define MTK_WED_WPDMA_CFG_BASE 0x580 +#define MTK_WED_WPDMA_CFG_INT_MASK 0x584 +#define MTK_WED_WPDMA_CFG_TX 0x588 +#define MTK_WED_WPDMA_CFG_TX_FREE 0x58c #define MTK_WED_WPDMA_TX_MIB(_n) (0x5a0 + (_n) * 4) #define MTK_WED_WPDMA_TX_COHERENT_MIB(_n) (0x5d0 + (_n) * 4) @@ -203,15 +268,24 @@ struct mtk_wdma_desc { #define MTK_WED_WDMA_RESET_IDX_RX GENMASK(17, 16) #define MTK_WED_WDMA_RESET_IDX_DRV GENMASK(25, 24) +#define MTK_WED_WDMA_INT_CLR 0xa24 +#define MTK_WED_WDMA_INT_CLR_RX_DONE GENMASK(17, 16) + #define MTK_WED_WDMA_INT_TRIGGER 0xa28 #define MTK_WED_WDMA_INT_TRIGGER_RX_DONE GENMASK(17, 16) #define MTK_WED_WDMA_INT_CTRL 0xa2c #define MTK_WED_WDMA_INT_CTRL_POLL_SRC_SEL GENMASK(17, 16) +#define MTK_WED_WDMA_CFG_BASE 0xaa0 #define MTK_WED_WDMA_OFFSET0 0xaa4 #define MTK_WED_WDMA_OFFSET1 0xaa8 +#define MTK_WED_WDMA_OFST0_GLO_INTS GENMASK(15, 0) +#define MTK_WED_WDMA_OFST0_GLO_CFG GENMASK(31, 16) +#define MTK_WED_WDMA_OFST1_TX_CTRL GENMASK(15, 0) +#define MTK_WED_WDMA_OFST1_RX_CTRL GENMASK(31, 16) + #define MTK_WED_WDMA_RX_MIB(_n) (0xae0 + (_n) * 4) #define MTK_WED_WDMA_RX_RECYCLE_MIB(_n) (0xae8 + (_n) * 4) #define MTK_WED_WDMA_RX_PROCESSED_MIB(_n) (0xaf0 + (_n) * 4) @@ -221,15 +295,22 @@ struct mtk_wdma_desc { #define MTK_WED_RING_OFS_CPU_IDX 0x08 #define MTK_WED_RING_OFS_DMA_IDX 0x0c +#define MTK_WDMA_RING_TX(_n) (0x000 + (_n) * 0x10) #define MTK_WDMA_RING_RX(_n) (0x100 + (_n) * 0x10) #define MTK_WDMA_GLO_CFG 0x204 -#define MTK_WDMA_GLO_CFG_RX_INFO_PRERES GENMASK(28, 26) +#define MTK_WDMA_GLO_CFG_TX_DMA_EN BIT(0) +#define MTK_WDMA_GLO_CFG_RX_DMA_EN BIT(2) +#define MTK_WDMA_GLO_CFG_RX_INFO3_PRERES BIT(26) +#define MTK_WDMA_GLO_CFG_RX_INFO2_PRERES BIT(27) +#define MTK_WDMA_GLO_CFG_RX_INFO1_PRERES BIT(28) #define MTK_WDMA_RESET_IDX 0x208 #define MTK_WDMA_RESET_IDX_TX GENMASK(3, 0) #define MTK_WDMA_RESET_IDX_RX GENMASK(17, 16) +#define MTK_WDMA_INT_STATUS 0x220 + #define MTK_WDMA_INT_MASK 0x228 #define MTK_WDMA_INT_MASK_TX_DONE GENMASK(3, 0) #define MTK_WDMA_INT_MASK_RX_DONE GENMASK(17, 16) diff --git a/drivers/net/ethernet/mellanox/mlx4/en_cq.c b/drivers/net/ethernet/mellanox/mlx4/en_cq.c index 6affbd241264..1184ac5751e1 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_cq.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_cq.c @@ -152,7 +152,7 @@ int mlx4_en_activate_cq(struct mlx4_en_priv *priv, struct mlx4_en_cq *cq, break; case RX: cq->mcq.comp = mlx4_en_rx_irq; - netif_napi_add(cq->dev, &cq->napi, mlx4_en_poll_rx_cq, 64); + netif_napi_add(cq->dev, &cq->napi, mlx4_en_poll_rx_cq); napi_enable(&cq->napi); break; case TX_XDP: diff --git a/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c b/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c index 6400a827173c..7d45f1d55f79 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c @@ -89,15 +89,15 @@ mlx4_en_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *drvinfo) struct mlx4_en_priv *priv = netdev_priv(dev); struct mlx4_en_dev *mdev = priv->mdev; - strlcpy(drvinfo->driver, DRV_NAME, sizeof(drvinfo->driver)); - strlcpy(drvinfo->version, DRV_VERSION, + strscpy(drvinfo->driver, DRV_NAME, sizeof(drvinfo->driver)); + strscpy(drvinfo->version, DRV_VERSION, sizeof(drvinfo->version)); snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version), "%d.%d.%d", (u16) (mdev->dev->caps.fw_ver >> 32), (u16) ((mdev->dev->caps.fw_ver >> 16) & 0xffff), (u16) (mdev->dev->caps.fw_ver & 0xffff)); - strlcpy(drvinfo->bus_info, pci_name(mdev->dev->persist->pdev), + strscpy(drvinfo->bus_info, pci_name(mdev->dev->persist->pdev), sizeof(drvinfo->bus_info)); } diff --git a/drivers/net/ethernet/mellanox/mlx4/fw.c b/drivers/net/ethernet/mellanox/mlx4/fw.c index dcb9eb1899ce..fe48d20d6118 100644 --- a/drivers/net/ethernet/mellanox/mlx4/fw.c +++ b/drivers/net/ethernet/mellanox/mlx4/fw.c @@ -1779,7 +1779,7 @@ static void get_board_id(void *vsd, char *board_id) if (be16_to_cpup(vsd + VSD_OFFSET_SIG1) == VSD_SIGNATURE_TOPSPIN && be16_to_cpup(vsd + VSD_OFFSET_SIG2) == VSD_SIGNATURE_TOPSPIN) { - strlcpy(board_id, vsd + VSD_OFFSET_TS_BOARD_ID, MLX4_BOARD_ID_LEN); + strscpy(board_id, vsd + VSD_OFFSET_TS_BOARD_ID, MLX4_BOARD_ID_LEN); } else { /* * The board ID is a string but the firmware byte diff --git a/drivers/net/ethernet/mellanox/mlx4/icm.c b/drivers/net/ethernet/mellanox/mlx4/icm.c index d89a3da89e5a..59b8b3c73582 100644 --- a/drivers/net/ethernet/mellanox/mlx4/icm.c +++ b/drivers/net/ethernet/mellanox/mlx4/icm.c @@ -208,7 +208,7 @@ struct mlx4_icm *mlx4_alloc_icm(struct mlx4_dev *dev, int npages, chunk->sg, chunk->npages, DMA_BIDIRECTIONAL); - if (chunk->nsg <= 0) + if (!chunk->nsg) goto fail; } @@ -222,7 +222,7 @@ struct mlx4_icm *mlx4_alloc_icm(struct mlx4_dev *dev, int npages, chunk->nsg = dma_map_sg(&dev->persist->pdev->dev, chunk->sg, chunk->npages, DMA_BIDIRECTIONAL); - if (chunk->nsg <= 0) + if (!chunk->nsg) goto fail; } diff --git a/drivers/net/ethernet/mellanox/mlx4/main.c b/drivers/net/ethernet/mellanox/mlx4/main.c index 78c5f40382c9..d3fc86cd3c1d 100644 --- a/drivers/net/ethernet/mellanox/mlx4/main.c +++ b/drivers/net/ethernet/mellanox/mlx4/main.c @@ -3071,6 +3071,7 @@ static int mlx4_init_port_info(struct mlx4_dev *dev, int port) err = device_create_file(&dev->persist->pdev->dev, &info->port_attr); if (err) { mlx4_err(dev, "Failed to create file for port %d\n", port); + devlink_port_type_clear(&info->devlink_port); devl_port_unregister(&info->devlink_port); info->port = -1; return err; @@ -3093,6 +3094,7 @@ static int mlx4_init_port_info(struct mlx4_dev *dev, int port) mlx4_err(dev, "Failed to create mtu file for port %d\n", port); device_remove_file(&info->dev->persist->pdev->dev, &info->port_attr); + devlink_port_type_clear(&info->devlink_port); devl_port_unregister(&info->devlink_port); info->port = -1; return err; @@ -3109,6 +3111,7 @@ static void mlx4_cleanup_port_info(struct mlx4_port_info *info) device_remove_file(&info->dev->persist->pdev->dev, &info->port_attr); device_remove_file(&info->dev->persist->pdev->dev, &info->port_mtu_attr); + devlink_port_type_clear(&info->devlink_port); devl_port_unregister(&info->devlink_port); #ifdef CONFIG_RFS_ACCEL diff --git a/drivers/net/ethernet/mellanox/mlx5/core/Kconfig b/drivers/net/ethernet/mellanox/mlx5/core/Kconfig index bfc0cd5ec423..26685fd0fdaa 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/Kconfig +++ b/drivers/net/ethernet/mellanox/mlx5/core/Kconfig @@ -139,6 +139,14 @@ config MLX5_CORE_IPOIB help MLX5 IPoIB offloads & acceleration support. +config MLX5_EN_MACSEC + bool "Connect-X support for MACSec offload" + depends on MLX5_CORE_EN + depends on MACSEC + default n + help + Build support for MACsec cryptography-offload acceleration in the NIC. + config MLX5_EN_IPSEC bool "Mellanox Technologies IPsec Connect-X support" depends on MLX5_CORE_EN diff --git a/drivers/net/ethernet/mellanox/mlx5/core/Makefile b/drivers/net/ethernet/mellanox/mlx5/core/Makefile index a3773a8177ed..a22c32aabf11 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/Makefile +++ b/drivers/net/ethernet/mellanox/mlx5/core/Makefile @@ -92,6 +92,9 @@ mlx5_core-$(CONFIG_MLX5_CORE_IPOIB) += ipoib/ipoib.o ipoib/ethtool.o ipoib/ipoib # mlx5_core-$(CONFIG_MLX5_FPGA) += fpga/cmd.o fpga/core.o fpga/conn.o fpga/sdk.o +mlx5_core-$(CONFIG_MLX5_EN_MACSEC) += en_accel/macsec.o en_accel/macsec_fs.o \ + en_accel/macsec_stats.o + mlx5_core-$(CONFIG_MLX5_EN_IPSEC) += en_accel/ipsec.o en_accel/ipsec_rxtx.o \ en_accel/ipsec_stats.o en_accel/ipsec_fs.o \ en_accel/ipsec_offload.o diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en.h b/drivers/net/ethernet/mellanox/mlx5/core/en.h index a560df446bac..26a23047f1f3 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en.h @@ -93,29 +93,26 @@ struct page_pool; #define MLX5_MPWRQ_DEF_LOG_STRIDE_SZ(mdev) \ MLX5_MPWRQ_LOG_STRIDE_SZ(mdev, order_base_2(MLX5E_RX_MAX_HEAD)) -#define MLX5_MPWRQ_LOG_WQE_SZ 18 -#define MLX5_MPWRQ_WQE_PAGE_ORDER (MLX5_MPWRQ_LOG_WQE_SZ - PAGE_SHIFT > 0 ? \ - MLX5_MPWRQ_LOG_WQE_SZ - PAGE_SHIFT : 0) -#define MLX5_MPWRQ_PAGES_PER_WQE BIT(MLX5_MPWRQ_WQE_PAGE_ORDER) - -#define MLX5_ALIGN_MTTS(mtts) (ALIGN(mtts, 8)) -#define MLX5_ALIGNED_MTTS_OCTW(mtts) ((mtts) / 2) -#define MLX5_MTT_OCTW(mtts) (MLX5_ALIGNED_MTTS_OCTW(MLX5_ALIGN_MTTS(mtts))) -/* Add another page to MLX5E_REQUIRED_WQE_MTTS as a buffer between - * WQEs, This page will absorb write overflow by the hardware, when - * receiving packets larger than MTU. These oversize packets are - * dropped by the driver at a later stage. +#define MLX5_MPWRQ_MAX_LOG_WQE_SZ 18 + +/* Keep in sync with mlx5e_mpwrq_log_wqe_sz. + * These are theoretical maximums, which can be further restricted by + * capabilities. These values are used for static resource allocations and + * sanity checks. + * MLX5_SEND_WQE_MAX_SIZE is a bit bigger than the maximum cacheline-aligned WQE + * size actually used at runtime, but it's not a problem when calculating static + * array sizes. */ -#define MLX5E_REQUIRED_WQE_MTTS (MLX5_ALIGN_MTTS(MLX5_MPWRQ_PAGES_PER_WQE + 1)) -#define MLX5E_REQUIRED_MTTS(wqes) (wqes * MLX5E_REQUIRED_WQE_MTTS) +#define MLX5_UMR_MAX_MTT_SPACE \ + (ALIGN_DOWN(MLX5_SEND_WQE_MAX_SIZE - sizeof(struct mlx5e_umr_wqe), \ + MLX5_UMR_MTT_ALIGNMENT)) +#define MLX5_MPWRQ_MAX_PAGES_PER_WQE \ + rounddown_pow_of_two(MLX5_UMR_MAX_MTT_SPACE / sizeof(struct mlx5_mtt)) + #define MLX5E_MAX_RQ_NUM_MTTS \ - (ALIGN_DOWN(U16_MAX, 4) * 2) /* So that MLX5_MTT_OCTW(num_mtts) fits into u16 */ + (ALIGN_DOWN(U16_MAX, 4) * 2) /* Fits into u16 and aligned by WQEBB. */ +#define MLX5E_MAX_RQ_NUM_KSMS (U16_MAX - 1) /* So that num_ksms fits into u16. */ #define MLX5E_ORDER2_MAX_PACKET_MTU (order_base_2(10 * 1024)) -#define MLX5E_PARAMS_MAXIMUM_LOG_RQ_SIZE_MPW \ - (ilog2(MLX5E_MAX_RQ_NUM_MTTS / MLX5E_REQUIRED_WQE_MTTS)) -#define MLX5E_LOG_MAX_RQ_NUM_PACKETS_MPW \ - (MLX5E_PARAMS_MAXIMUM_LOG_RQ_SIZE_MPW + \ - (MLX5_MPWRQ_LOG_WQE_SZ - MLX5E_ORDER2_MAX_PACKET_MTU)) #define MLX5E_MIN_SKB_FRAG_SZ (MLX5_SKB_FRAG_SZ(MLX5_RX_HEADROOM)) #define MLX5E_LOG_MAX_RX_WQE_BULK \ @@ -127,8 +124,7 @@ struct page_pool; #define MLX5E_PARAMS_MINIMUM_LOG_RQ_SIZE (1 + MLX5E_LOG_MAX_RX_WQE_BULK) #define MLX5E_PARAMS_DEFAULT_LOG_RQ_SIZE 0xa -#define MLX5E_PARAMS_MAXIMUM_LOG_RQ_SIZE min_t(u8, 0xd, \ - MLX5E_LOG_MAX_RQ_NUM_PACKETS_MPW) +#define MLX5E_PARAMS_MAXIMUM_LOG_RQ_SIZE 0xd #define MLX5E_PARAMS_MINIMUM_LOG_RQ_SIZE_MPW 0x2 @@ -150,13 +146,6 @@ struct page_pool; #define MLX5E_TX_XSK_POLL_BUDGET 64 #define MLX5E_SQ_RECOVER_MIN_INTERVAL 500 /* msecs */ -#define MLX5E_UMR_WQE_INLINE_SZ \ - (sizeof(struct mlx5e_umr_wqe) + \ - ALIGN(MLX5_MPWRQ_PAGES_PER_WQE * sizeof(struct mlx5_mtt), \ - MLX5_UMR_MTT_ALIGNMENT)) -#define MLX5E_UMR_WQEBBS \ - (DIV_ROUND_UP(MLX5E_UMR_WQE_INLINE_SZ, MLX5_SEND_WQE_BB)) - #define MLX5E_KLM_UMR_WQE_SZ(sgl_len)\ (sizeof(struct mlx5e_umr_wqe) +\ (sizeof(struct mlx5_klm) * (sgl_len))) @@ -174,8 +163,7 @@ struct page_pool; ALIGN_DOWN(MLX5E_KLM_MAX_ENTRIES_PER_WQE(wqe_size), MLX5_UMR_KLM_ALIGNMENT) #define MLX5E_MAX_KLM_PER_WQE(mdev) \ - MLX5E_KLM_ENTRIES_PER_WQE(MLX5_SEND_WQE_BB * \ - mlx5e_get_sw_max_sq_mpw_wqebbs(mlx5e_get_max_sq_wqebbs(mdev))) + MLX5E_KLM_ENTRIES_PER_WQE(MLX5_SEND_WQE_BB * mlx5e_get_max_sq_aligned_wqebbs(mdev)) #define MLX5E_MSG_LEVEL NETIF_MSG_LINK @@ -189,12 +177,6 @@ do { \ #define mlx5e_state_dereference(priv, p) \ rcu_dereference_protected((p), lockdep_is_held(&(priv)->state_lock)) -enum mlx5e_rq_group { - MLX5E_RQ_GROUP_REGULAR, - MLX5E_RQ_GROUP_XSK, -#define MLX5E_NUM_RQ_GROUPS(g) (1 + MLX5E_RQ_GROUP_##g) -}; - static inline u8 mlx5e_get_num_lag_ports(struct mlx5_core_dev *mdev) { if (mlx5_lag_is_lacp_owner(mdev)) @@ -227,13 +209,15 @@ static inline int mlx5e_get_max_num_channels(struct mlx5_core_dev *mdev) * bytes units. Driver hardens the limitation to 1KB (16 * WQEBBs), unless firmware capability is stricter. */ -static inline u16 mlx5e_get_max_sq_wqebbs(struct mlx5_core_dev *mdev) +static inline u8 mlx5e_get_max_sq_wqebbs(struct mlx5_core_dev *mdev) { - return min_t(u16, MLX5_SEND_WQE_MAX_WQEBBS, - MLX5_CAP_GEN(mdev, max_wqe_sz_sq) / MLX5_SEND_WQE_BB); + BUILD_BUG_ON(MLX5_SEND_WQE_MAX_WQEBBS > U8_MAX); + + return (u8)min_t(u16, MLX5_SEND_WQE_MAX_WQEBBS, + MLX5_CAP_GEN(mdev, max_wqe_sz_sq) / MLX5_SEND_WQE_BB); } -static inline u8 mlx5e_get_sw_max_sq_mpw_wqebbs(u8 max_sq_wqebbs) +static inline u8 mlx5e_get_max_sq_aligned_wqebbs(struct mlx5_core_dev *mdev) { /* The return value will be multiplied by MLX5_SEND_WQEBB_NUM_DS. * Since max_sq_wqebbs may be up to MLX5_SEND_WQE_MAX_WQEBBS == 16, @@ -242,8 +226,9 @@ static inline u8 mlx5e_get_sw_max_sq_mpw_wqebbs(u8 max_sq_wqebbs) * than MLX5_SEND_WQE_MAX_WQEBBS to let a full-session WQE be * cache-aligned. */ - u8 wqebbs = min_t(u8, max_sq_wqebbs, MLX5_SEND_WQE_MAX_WQEBBS - 1); + u8 wqebbs = mlx5e_get_max_sq_wqebbs(mdev); + wqebbs = min_t(u8, wqebbs, MLX5_SEND_WQE_MAX_WQEBBS - 1); #if L1_CACHE_BYTES >= 128 wqebbs = ALIGN_DOWN(wqebbs, 2); #endif @@ -272,6 +257,7 @@ struct mlx5e_umr_wqe { union { DECLARE_FLEX_ARRAY(struct mlx5_mtt, inline_mtts); DECLARE_FLEX_ARRAY(struct mlx5_klm, inline_klms); + DECLARE_FLEX_ARRAY(struct mlx5_ksm, inline_ksms); }; }; @@ -476,15 +462,11 @@ struct mlx5e_txqsq { struct work_struct recover_work; struct mlx5e_ptpsq *ptpsq; cqe_ts_to_ns ptp_cyc2time; - u16 max_sq_wqebbs; } ____cacheline_aligned_in_smp; -struct mlx5e_dma_info { - dma_addr_t addr; - union { - struct page *page; - struct xdp_buff *xsk; - }; +union mlx5e_alloc_unit { + struct page *page; + struct xdp_buff *xsk; }; /* XDP packets can be transmitted in different ways. On completion, we need to @@ -580,7 +562,6 @@ struct mlx5e_xdpsq { /* control path */ struct mlx5_wq_ctrl wq_ctrl; struct mlx5e_channel *channel; - u16 max_sq_wqebbs; } ____cacheline_aligned_in_smp; struct mlx5e_ktls_resync_resp; @@ -609,25 +590,20 @@ struct mlx5e_icosq { /* control path */ struct mlx5_wq_ctrl wq_ctrl; struct mlx5e_channel *channel; - u16 max_sq_wqebbs; struct work_struct recover_work; } ____cacheline_aligned_in_smp; struct mlx5e_wqe_frag_info { - struct mlx5e_dma_info *di; + union mlx5e_alloc_unit *au; u32 offset; bool last_in_page; }; -struct mlx5e_umr_dma_info { - struct mlx5e_dma_info dma_info[MLX5_MPWRQ_PAGES_PER_WQE]; -}; - struct mlx5e_mpw_info { - struct mlx5e_umr_dma_info umr; u16 consumed_strides; - DECLARE_BITMAP(xdp_xmit_bitmap, MLX5_MPWRQ_PAGES_PER_WQE); + DECLARE_BITMAP(xdp_xmit_bitmap, MLX5_MPWRQ_MAX_PAGES_PER_WQE); + union mlx5e_alloc_unit alloc_units[]; }; #define MLX5E_MAX_RX_FRAGS 4 @@ -635,13 +611,13 @@ struct mlx5e_mpw_info { /* a single cache unit is capable to serve one napi call (for non-striding rq) * or a MPWQE (for striding rq). */ -#define MLX5E_CACHE_UNIT (MLX5_MPWRQ_PAGES_PER_WQE > NAPI_POLL_WEIGHT ? \ - MLX5_MPWRQ_PAGES_PER_WQE : NAPI_POLL_WEIGHT) +#define MLX5E_CACHE_UNIT (MLX5_MPWRQ_MAX_PAGES_PER_WQE > NAPI_POLL_WEIGHT ? \ + MLX5_MPWRQ_MAX_PAGES_PER_WQE : NAPI_POLL_WEIGHT) #define MLX5E_CACHE_SIZE (4 * roundup_pow_of_two(MLX5E_CACHE_UNIT)) struct mlx5e_page_cache { u32 head; u32 tail; - struct mlx5e_dma_info page_cache[MLX5E_CACHE_SIZE]; + struct page *page_cache[MLX5E_CACHE_SIZE]; }; struct mlx5e_rq; @@ -674,6 +650,12 @@ struct mlx5e_rq_frags_info { u8 num_frags; u8 log_num_frags; u8 wqe_bulk; + u8 wqe_index_mask; +}; + +struct mlx5e_dma_info { + dma_addr_t addr; + struct page *page; }; struct mlx5e_shampo_hd { @@ -695,13 +677,20 @@ struct mlx5e_hw_gro_data { int second_ip_id; }; +enum mlx5e_mpwrq_umr_mode { + MLX5E_MPWRQ_UMR_MODE_ALIGNED, + MLX5E_MPWRQ_UMR_MODE_UNALIGNED, + MLX5E_MPWRQ_UMR_MODE_OVERSIZED, + MLX5E_MPWRQ_UMR_MODE_TRIPLE, +}; + struct mlx5e_rq { /* data path */ union { struct { struct mlx5_wq_cyc wq; struct mlx5e_wqe_frag_info *frags; - struct mlx5e_dma_info *di; + union mlx5e_alloc_unit *alloc_units; struct mlx5e_rq_frags_info info; mlx5e_fp_skb_from_cqe skb_from_cqe; } wqe; @@ -710,6 +699,7 @@ struct mlx5e_rq { struct mlx5e_umr_wqe umr_wqe; struct mlx5e_mpw_info *info; mlx5e_fp_skb_from_cqe_mpwrq skb_from_cqe_mpwrq; + __be32 umr_mkey_be; u16 num_strides; u16 actual_wq_head; u8 log_stride_sz; @@ -717,6 +707,11 @@ struct mlx5e_rq { u8 umr_last_bulk; u8 umr_completed; u8 min_wqe_bulk; + u8 page_shift; + u8 pages_per_wqe; + u8 umr_wqebbs; + u8 mtts_per_wqe; + u8 umr_mode; struct mlx5e_shampo_hd *shampo; } mpwqe; }; @@ -767,7 +762,6 @@ struct mlx5e_rq { u32 rqn; struct mlx5_core_dev *mdev; struct mlx5e_channel *channel; - u32 umr_mkey; struct mlx5e_dma_info wqe_overflow; /* XDP read-mostly */ @@ -856,11 +850,6 @@ enum { MLX5E_STATE_XDP_ACTIVE, }; -enum { - MLX5E_TC_PRIO = 0, - MLX5E_NIC_PRIO -}; - struct mlx5e_modify_sq_param { int curr_state; int next_state; @@ -959,6 +948,9 @@ struct mlx5e_priv { const struct mlx5e_profile *profile; void *ppriv; +#ifdef CONFIG_MLX5_EN_MACSEC + struct mlx5e_macsec *macsec; +#endif #ifdef CONFIG_MLX5_EN_IPSEC struct mlx5e_ipsec *ipsec; #endif @@ -1010,7 +1002,6 @@ struct mlx5e_profile { mlx5e_stats_grp_t *stats_grps; const struct mlx5e_rx_handlers *rx_handlers; int max_tc; - u8 rq_groups; u32 features; }; @@ -1019,7 +1010,8 @@ struct mlx5e_profile { void mlx5e_build_ptys2ethtool_map(void); -bool mlx5e_check_fragmented_striding_rq_cap(struct mlx5_core_dev *mdev); +bool mlx5e_check_fragmented_striding_rq_cap(struct mlx5_core_dev *mdev, u8 page_shift, + enum mlx5e_mpwrq_umr_mode umr_mode); void mlx5e_shampo_dealloc_hd(struct mlx5e_rq *rq, u16 len, u16 start, bool close); void mlx5e_get_stats(struct net_device *dev, struct rtnl_link_stats64 *stats); @@ -1047,6 +1039,7 @@ struct mlx5e_rq_param; int mlx5e_open_rq(struct mlx5e_params *params, struct mlx5e_rq_param *param, struct mlx5e_xsk_param *xsk, int node, struct mlx5e_rq *rq); +#define MLX5E_RQ_WQES_TIMEOUT 20000 /* msecs */ int mlx5e_wait_for_min_rx_wqes(struct mlx5e_rq *rq, int wait_time); void mlx5e_close_rq(struct mlx5e_rq *rq); int mlx5e_create_rq(struct mlx5e_rq *rq, struct mlx5e_rq_param *param); @@ -1101,7 +1094,7 @@ void mlx5e_activate_priv_channels(struct mlx5e_priv *priv); void mlx5e_deactivate_priv_channels(struct mlx5e_priv *priv); int mlx5e_ptp_rx_manage_fs_ctx(struct mlx5e_priv *priv, void *ctx); -int mlx5e_modify_rq_state(struct mlx5e_rq *rq, int curr_state, int next_state); +int mlx5e_flush_rq(struct mlx5e_rq *rq, int curr_state); void mlx5e_activate_rq(struct mlx5e_rq *rq); void mlx5e_deactivate_rq(struct mlx5e_rq *rq); void mlx5e_activate_icosq(struct mlx5e_icosq *icosq); @@ -1136,6 +1129,7 @@ static inline bool mlx5_tx_swp_supported(struct mlx5_core_dev *mdev) extern const struct ethtool_ops mlx5e_ethtool_ops; +int mlx5e_create_mkey(struct mlx5_core_dev *mdev, u32 pdn, u32 *mkey); int mlx5e_create_mdev_resources(struct mlx5_core_dev *mdev); void mlx5e_destroy_mdev_resources(struct mlx5_core_dev *mdev); int mlx5e_refresh_tirs(struct mlx5e_priv *priv, bool enable_uc_lb, @@ -1148,8 +1142,6 @@ void mlx5e_destroy_q_counters(struct mlx5e_priv *priv); int mlx5e_open_drop_rq(struct mlx5e_priv *priv, struct mlx5e_rq *drop_rq); void mlx5e_close_drop_rq(struct mlx5e_rq *drop_rq); -int mlx5e_init_di_list(struct mlx5e_rq *rq, int wq_sz, int node); -void mlx5e_free_di_list(struct mlx5e_rq *rq); int mlx5e_create_tis(struct mlx5_core_dev *mdev, void *in, u32 *tisn); void mlx5e_destroy_tis(struct mlx5_core_dev *mdev, u32 tisn); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/channels.c b/drivers/net/ethernet/mellanox/mlx5/core/en/channels.c index e7c14c0de0a7..48581ea3adcb 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/channels.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/channels.c @@ -10,28 +10,33 @@ unsigned int mlx5e_channels_get_num(struct mlx5e_channels *chs) return chs->num; } -void mlx5e_channels_get_regular_rqn(struct mlx5e_channels *chs, unsigned int ix, u32 *rqn) +static struct mlx5e_channel *mlx5e_channels_get(struct mlx5e_channels *chs, unsigned int ix) { - struct mlx5e_channel *c; + WARN_ON_ONCE(ix >= mlx5e_channels_get_num(chs)); + return chs->c[ix]; +} - WARN_ON(ix >= mlx5e_channels_get_num(chs)); - c = chs->c[ix]; +bool mlx5e_channels_is_xsk(struct mlx5e_channels *chs, unsigned int ix) +{ + struct mlx5e_channel *c = mlx5e_channels_get(chs, ix); - *rqn = c->rq.rqn; + return test_bit(MLX5E_CHANNEL_STATE_XSK, c->state); } -bool mlx5e_channels_get_xsk_rqn(struct mlx5e_channels *chs, unsigned int ix, u32 *rqn) +void mlx5e_channels_get_regular_rqn(struct mlx5e_channels *chs, unsigned int ix, u32 *rqn) { - struct mlx5e_channel *c; + struct mlx5e_channel *c = mlx5e_channels_get(chs, ix); - WARN_ON(ix >= mlx5e_channels_get_num(chs)); - c = chs->c[ix]; + *rqn = c->rq.rqn; +} - if (!test_bit(MLX5E_CHANNEL_STATE_XSK, c->state)) - return false; +void mlx5e_channels_get_xsk_rqn(struct mlx5e_channels *chs, unsigned int ix, u32 *rqn) +{ + struct mlx5e_channel *c = mlx5e_channels_get(chs, ix); + + WARN_ON_ONCE(!test_bit(MLX5E_CHANNEL_STATE_XSK, c->state)); *rqn = c->xskrq.rqn; - return true; } bool mlx5e_channels_get_ptp_rqn(struct mlx5e_channels *chs, u32 *rqn) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/channels.h b/drivers/net/ethernet/mellanox/mlx5/core/en/channels.h index ca00cbc827cb..637ca90daaa8 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/channels.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/channels.h @@ -9,8 +9,9 @@ struct mlx5e_channels; unsigned int mlx5e_channels_get_num(struct mlx5e_channels *chs); +bool mlx5e_channels_is_xsk(struct mlx5e_channels *chs, unsigned int ix); void mlx5e_channels_get_regular_rqn(struct mlx5e_channels *chs, unsigned int ix, u32 *rqn); -bool mlx5e_channels_get_xsk_rqn(struct mlx5e_channels *chs, unsigned int ix, u32 *rqn); +void mlx5e_channels_get_xsk_rqn(struct mlx5e_channels *chs, unsigned int ix, u32 *rqn); bool mlx5e_channels_get_ptp_rqn(struct mlx5e_channels *chs, u32 *rqn); #endif /* __MLX5_EN_CHANNELS_H__ */ diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/fs.h b/drivers/net/ethernet/mellanox/mlx5/core/en/fs.h index 9b8cdf2e68ad..bf2741eb7f9b 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/fs.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/fs.h @@ -8,6 +8,7 @@ #include "lib/fs_ttc.h" struct mlx5e_post_act; +struct mlx5e_tc_table; enum { MLX5E_TC_FT_LEVEL = 0, @@ -15,6 +16,11 @@ enum { MLX5E_TC_MISS_LEVEL, }; +enum { + MLX5E_TC_PRIO = 0, + MLX5E_NIC_PRIO +}; + struct mlx5e_flow_table { int num_groups; struct mlx5_flow_table *t; @@ -83,54 +89,28 @@ enum { #endif }; -struct mlx5e_priv; - -#ifdef CONFIG_MLX5_EN_RXNFC - -struct mlx5e_ethtool_table { - struct mlx5_flow_table *ft; - int num_rules; -}; - -#define ETHTOOL_NUM_L3_L4_FTS 7 -#define ETHTOOL_NUM_L2_FTS 4 - -struct mlx5e_ethtool_steering { - struct mlx5e_ethtool_table l3_l4_ft[ETHTOOL_NUM_L3_L4_FTS]; - struct mlx5e_ethtool_table l2_ft[ETHTOOL_NUM_L2_FTS]; - struct list_head rules; - int tot_num_rules; -}; - -void mlx5e_ethtool_init_steering(struct mlx5e_priv *priv); -void mlx5e_ethtool_cleanup_steering(struct mlx5e_priv *priv); -int mlx5e_ethtool_set_rxnfc(struct mlx5e_priv *priv, struct ethtool_rxnfc *cmd); -int mlx5e_ethtool_get_rxnfc(struct mlx5e_priv *priv, - struct ethtool_rxnfc *info, u32 *rule_locs); -#else -static inline void mlx5e_ethtool_init_steering(struct mlx5e_priv *priv) { } -static inline void mlx5e_ethtool_cleanup_steering(struct mlx5e_priv *priv) { } -static inline int mlx5e_ethtool_set_rxnfc(struct mlx5e_priv *priv, struct ethtool_rxnfc *cmd) -{ return -EOPNOTSUPP; } -static inline int mlx5e_ethtool_get_rxnfc(struct mlx5e_priv *priv, - struct ethtool_rxnfc *info, u32 *rule_locs) -{ return -EOPNOTSUPP; } -#endif /* CONFIG_MLX5_EN_RXNFC */ +struct mlx5e_flow_steering; +struct mlx5e_rx_res; #ifdef CONFIG_MLX5_EN_ARFS struct mlx5e_arfs_tables; -int mlx5e_arfs_create_tables(struct mlx5e_priv *priv); -void mlx5e_arfs_destroy_tables(struct mlx5e_priv *priv); -int mlx5e_arfs_enable(struct mlx5e_priv *priv); -int mlx5e_arfs_disable(struct mlx5e_priv *priv); +int mlx5e_arfs_create_tables(struct mlx5e_flow_steering *fs, + struct mlx5e_rx_res *rx_res, bool ntuple); +void mlx5e_arfs_destroy_tables(struct mlx5e_flow_steering *fs, bool ntuple); +int mlx5e_arfs_enable(struct mlx5e_flow_steering *fs); +int mlx5e_arfs_disable(struct mlx5e_flow_steering *fs); int mlx5e_rx_flow_steer(struct net_device *dev, const struct sk_buff *skb, u16 rxq_index, u32 flow_id); #else -static inline int mlx5e_arfs_create_tables(struct mlx5e_priv *priv) { return 0; } -static inline void mlx5e_arfs_destroy_tables(struct mlx5e_priv *priv) {} -static inline int mlx5e_arfs_enable(struct mlx5e_priv *priv) { return -EOPNOTSUPP; } -static inline int mlx5e_arfs_disable(struct mlx5e_priv *priv) { return -EOPNOTSUPP; } +static inline int mlx5e_arfs_create_tables(struct mlx5e_flow_steering *fs, + struct mlx5e_rx_res *rx_res, bool ntuple) +{ return 0; } +static inline void mlx5e_arfs_destroy_tables(struct mlx5e_flow_steering *fs, bool ntuple) {} +static inline int mlx5e_arfs_enable(struct mlx5e_flow_steering *fs) +{ return -EOPNOTSUPP; } +static inline int mlx5e_arfs_disable(struct mlx5e_flow_steering *fs) +{ return -EOPNOTSUPP; } #endif #ifdef CONFIG_MLX5_EN_TLS @@ -142,54 +122,63 @@ struct mlx5e_fs_udp; struct mlx5e_fs_any; struct mlx5e_ptp_fs; -struct mlx5e_flow_steering { - bool state_destroy; - bool vlan_strip_disable; - struct mlx5_core_dev *mdev; - struct mlx5_flow_namespace *ns; -#ifdef CONFIG_MLX5_EN_RXNFC - struct mlx5e_ethtool_steering ethtool; -#endif - struct mlx5e_tc_table *tc; - struct mlx5e_promisc_table promisc; - struct mlx5e_vlan_table *vlan; - struct mlx5e_l2_table l2; - struct mlx5_ttc_table *ttc; - struct mlx5_ttc_table *inner_ttc; -#ifdef CONFIG_MLX5_EN_ARFS - struct mlx5e_arfs_tables *arfs; -#endif -#ifdef CONFIG_MLX5_EN_TLS - struct mlx5e_accel_fs_tcp *accel_tcp; -#endif - struct mlx5e_fs_udp *udp; - struct mlx5e_fs_any *any; - struct mlx5e_ptp_fs *ptp_fs; -}; - -void mlx5e_set_ttc_params(struct mlx5e_priv *priv, +void mlx5e_set_ttc_params(struct mlx5e_flow_steering *fs, + struct mlx5e_rx_res *rx_res, struct ttc_params *ttc_params, bool tunnel); -void mlx5e_destroy_ttc_table(struct mlx5e_priv *priv); -int mlx5e_create_ttc_table(struct mlx5e_priv *priv); +void mlx5e_destroy_ttc_table(struct mlx5e_flow_steering *fs); +int mlx5e_create_ttc_table(struct mlx5e_flow_steering *fs, + struct mlx5e_rx_res *rx_res); void mlx5e_destroy_flow_table(struct mlx5e_flow_table *ft); -void mlx5e_enable_cvlan_filter(struct mlx5e_priv *priv); -void mlx5e_disable_cvlan_filter(struct mlx5e_priv *priv); +void mlx5e_enable_cvlan_filter(struct mlx5e_flow_steering *fs, bool promisc); +void mlx5e_disable_cvlan_filter(struct mlx5e_flow_steering *fs, bool promisc); -int mlx5e_create_flow_steering(struct mlx5e_priv *priv); -void mlx5e_destroy_flow_steering(struct mlx5e_priv *priv); +int mlx5e_create_flow_steering(struct mlx5e_flow_steering *fs, + struct mlx5e_rx_res *rx_res, + const struct mlx5e_profile *profile, + struct net_device *netdev); +void mlx5e_destroy_flow_steering(struct mlx5e_flow_steering *fs, bool ntuple, + const struct mlx5e_profile *profile); struct mlx5e_flow_steering *mlx5e_fs_init(const struct mlx5e_profile *profile, struct mlx5_core_dev *mdev, bool state_destroy); void mlx5e_fs_cleanup(struct mlx5e_flow_steering *fs); - -int mlx5e_add_vlan_trap(struct mlx5e_priv *priv, int trap_id, int tir_num); -void mlx5e_remove_vlan_trap(struct mlx5e_priv *priv); -int mlx5e_add_mac_trap(struct mlx5e_priv *priv, int trap_id, int tir_num); -void mlx5e_remove_mac_trap(struct mlx5e_priv *priv); +struct mlx5e_vlan_table *mlx5e_fs_get_vlan(struct mlx5e_flow_steering *fs); +void mlx5e_fs_set_tc(struct mlx5e_flow_steering *fs, struct mlx5e_tc_table *tc); +struct mlx5e_tc_table *mlx5e_fs_get_tc(struct mlx5e_flow_steering *fs); +struct mlx5e_l2_table *mlx5e_fs_get_l2(struct mlx5e_flow_steering *fs); +struct mlx5_flow_namespace *mlx5e_fs_get_ns(struct mlx5e_flow_steering *fs, bool egress); +void mlx5e_fs_set_ns(struct mlx5e_flow_steering *fs, struct mlx5_flow_namespace *ns, bool egress); +#ifdef CONFIG_MLX5_EN_RXNFC +struct mlx5e_ethtool_steering *mlx5e_fs_get_ethtool(struct mlx5e_flow_steering *fs); +#endif +struct mlx5_ttc_table *mlx5e_fs_get_ttc(struct mlx5e_flow_steering *fs, bool inner); +void mlx5e_fs_set_ttc(struct mlx5e_flow_steering *fs, struct mlx5_ttc_table *ttc, bool inner); +#ifdef CONFIG_MLX5_EN_ARFS +struct mlx5e_arfs_tables *mlx5e_fs_get_arfs(struct mlx5e_flow_steering *fs); +void mlx5e_fs_set_arfs(struct mlx5e_flow_steering *fs, struct mlx5e_arfs_tables *arfs); +#endif +struct mlx5e_ptp_fs *mlx5e_fs_get_ptp(struct mlx5e_flow_steering *fs); +void mlx5e_fs_set_ptp(struct mlx5e_flow_steering *fs, struct mlx5e_ptp_fs *ptp_fs); +struct mlx5e_fs_any *mlx5e_fs_get_any(struct mlx5e_flow_steering *fs); +void mlx5e_fs_set_any(struct mlx5e_flow_steering *fs, struct mlx5e_fs_any *any); +struct mlx5e_fs_udp *mlx5e_fs_get_udp(struct mlx5e_flow_steering *fs); +void mlx5e_fs_set_udp(struct mlx5e_flow_steering *fs, struct mlx5e_fs_udp *udp); +#ifdef CONFIG_MLX5_EN_TLS +struct mlx5e_accel_fs_tcp *mlx5e_fs_get_accel_tcp(struct mlx5e_flow_steering *fs); +void mlx5e_fs_set_accel_tcp(struct mlx5e_flow_steering *fs, struct mlx5e_accel_fs_tcp *accel_tcp); +#endif +void mlx5e_fs_set_state_destroy(struct mlx5e_flow_steering *fs, bool state_destroy); +void mlx5e_fs_set_vlan_strip_disable(struct mlx5e_flow_steering *fs, bool vlan_strip_disable); + +struct mlx5_core_dev *mlx5e_fs_get_mdev(struct mlx5e_flow_steering *fs); +int mlx5e_add_vlan_trap(struct mlx5e_flow_steering *fs, int trap_id, int tir_num); +void mlx5e_remove_vlan_trap(struct mlx5e_flow_steering *fs); +int mlx5e_add_mac_trap(struct mlx5e_flow_steering *fs, int trap_id, int tir_num); +void mlx5e_remove_mac_trap(struct mlx5e_flow_steering *fs); void mlx5e_fs_set_rx_mode_work(struct mlx5e_flow_steering *fs, struct net_device *netdev); int mlx5e_fs_vlan_rx_add_vid(struct mlx5e_flow_steering *fs, struct net_device *netdev, @@ -198,5 +187,18 @@ int mlx5e_fs_vlan_rx_kill_vid(struct mlx5e_flow_steering *fs, struct net_device *netdev, __be16 proto, u16 vid); void mlx5e_fs_init_l2_addr(struct mlx5e_flow_steering *fs, struct net_device *netdev); + +#define fs_err(fs, fmt, ...) \ + mlx5_core_err(mlx5e_fs_get_mdev(fs), fmt, ##__VA_ARGS__) + +#define fs_dbg(fs, fmt, ...) \ + mlx5_core_dbg(mlx5e_fs_get_mdev(fs), fmt, ##__VA_ARGS__) + +#define fs_warn(fs, fmt, ...) \ + mlx5_core_warn(mlx5e_fs_get_mdev(fs), fmt, ##__VA_ARGS__) + +#define fs_warn_once(fs, fmt, ...) \ + mlx5_core_warn_once(mlx5e_fs_get_mdev(fs), fmt, ##__VA_ARGS__) + #endif /* __MLX5E_FLOW_STEER_H__ */ diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/fs_ethtool.h b/drivers/net/ethernet/mellanox/mlx5/core/en/fs_ethtool.h new file mode 100644 index 000000000000..9e276fd3c0cf --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/fs_ethtool.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB */ +/* Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. */ + +#ifndef __MLX5E_FS_ETHTOOL_H__ +#define __MLX5E_FS_ETHTOOL_H__ + +struct mlx5e_priv; +struct mlx5e_ethtool_steering; +#ifdef CONFIG_MLX5_EN_RXNFC +int mlx5e_ethtool_alloc(struct mlx5e_ethtool_steering **ethtool); +void mlx5e_ethtool_free(struct mlx5e_ethtool_steering *ethtool); +void mlx5e_ethtool_init_steering(struct mlx5e_flow_steering *fs); +void mlx5e_ethtool_cleanup_steering(struct mlx5e_flow_steering *fs); +int mlx5e_ethtool_set_rxnfc(struct mlx5e_priv *priv, struct ethtool_rxnfc *cmd); +int mlx5e_ethtool_get_rxnfc(struct mlx5e_priv *priv, + struct ethtool_rxnfc *info, u32 *rule_locs); +#else +static inline int mlx5e_ethtool_alloc(struct mlx5e_ethtool_steering **ethtool) +{ return 0; } +static inline void mlx5e_ethtool_free(struct mlx5e_ethtool_steering *ethtool) { } +static inline void mlx5e_ethtool_init_steering(struct mlx5e_flow_steering *fs) { } +static inline void mlx5e_ethtool_cleanup_steering(struct mlx5e_flow_steering *fs) { } +static inline int mlx5e_ethtool_set_rxnfc(struct mlx5e_priv *priv, struct ethtool_rxnfc *cmd) +{ return -EOPNOTSUPP; } +static inline int mlx5e_ethtool_get_rxnfc(struct mlx5e_priv *priv, + struct ethtool_rxnfc *info, u32 *rule_locs) +{ return -EOPNOTSUPP; } +#endif +#endif diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/fs_tt_redirect.c b/drivers/net/ethernet/mellanox/mlx5/core/en/fs_tt_redirect.c index e153d6119e02..03cb79adf912 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/fs_tt_redirect.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/fs_tt_redirect.c @@ -1,9 +1,9 @@ // SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB /* Copyright (c) 2021, Mellanox Technologies inc. All rights reserved. */ -#include <linux/netdevice.h> #include "en/fs_tt_redirect.h" #include "fs_core.h" +#include "mlx5_core.h" enum fs_udp_type { FS_IPV4_UDP, @@ -74,17 +74,17 @@ static void fs_udp_set_dport_flow(struct mlx5_flow_spec *spec, enum fs_udp_type } struct mlx5_flow_handle * -mlx5e_fs_tt_redirect_udp_add_rule(struct mlx5e_priv *priv, +mlx5e_fs_tt_redirect_udp_add_rule(struct mlx5e_flow_steering *fs, enum mlx5_traffic_types ttc_type, u32 tir_num, u16 d_port) { + struct mlx5e_fs_udp *fs_udp = mlx5e_fs_get_udp(fs); enum fs_udp_type type = tt2fs_udp(ttc_type); struct mlx5_flow_destination dest = {}; struct mlx5_flow_table *ft = NULL; MLX5_DECLARE_FLOW_ACT(flow_act); struct mlx5_flow_handle *rule; struct mlx5_flow_spec *spec; - struct mlx5e_fs_udp *fs_udp; int err; if (type == FS_UDP_NUM_TYPES) @@ -94,7 +94,6 @@ mlx5e_fs_tt_redirect_udp_add_rule(struct mlx5e_priv *priv, if (!spec) return ERR_PTR(-ENOMEM); - fs_udp = priv->fs->udp; ft = fs_udp->tables[type].t; fs_udp_set_dport_flow(spec, type, d_port); @@ -106,31 +105,30 @@ mlx5e_fs_tt_redirect_udp_add_rule(struct mlx5e_priv *priv, if (IS_ERR(rule)) { err = PTR_ERR(rule); - netdev_err(priv->netdev, "%s: add %s rule failed, err %d\n", - __func__, fs_udp_type2str(type), err); + fs_err(fs, "%s: add %s rule failed, err %d\n", + __func__, fs_udp_type2str(type), err); } return rule; } -static int fs_udp_add_default_rule(struct mlx5e_priv *priv, enum fs_udp_type type) +static int fs_udp_add_default_rule(struct mlx5e_flow_steering *fs, enum fs_udp_type type) { + struct mlx5_ttc_table *ttc = mlx5e_fs_get_ttc(fs, false); + struct mlx5e_fs_udp *fs_udp = mlx5e_fs_get_udp(fs); struct mlx5e_flow_table *fs_udp_t; struct mlx5_flow_destination dest; MLX5_DECLARE_FLOW_ACT(flow_act); struct mlx5_flow_handle *rule; - struct mlx5e_fs_udp *fs_udp; int err; - fs_udp = priv->fs->udp; fs_udp_t = &fs_udp->tables[type]; - dest = mlx5_ttc_get_default_dest(priv->fs->ttc, fs_udp2tt(type)); + dest = mlx5_ttc_get_default_dest(ttc, fs_udp2tt(type)); rule = mlx5_add_flow_rules(fs_udp_t->t, NULL, &flow_act, &dest, 1); if (IS_ERR(rule)) { err = PTR_ERR(rule); - netdev_err(priv->netdev, - "%s: add default rule failed, fs type=%d, err %d\n", - __func__, type, err); + fs_err(fs, "%s: add default rule failed, fs type=%d, err %d\n", + __func__, type, err); return err; } @@ -206,33 +204,36 @@ out: return err; } -static int fs_udp_create_table(struct mlx5e_priv *priv, enum fs_udp_type type) +static int fs_udp_create_table(struct mlx5e_flow_steering *fs, enum fs_udp_type type) { - struct mlx5e_flow_table *ft = &priv->fs->udp->tables[type]; + struct mlx5_flow_namespace *ns = mlx5e_fs_get_ns(fs, false); + struct mlx5e_fs_udp *fs_udp = mlx5e_fs_get_udp(fs); struct mlx5_flow_table_attr ft_attr = {}; + struct mlx5e_flow_table *ft; int err; + ft = &fs_udp->tables[type]; ft->num_groups = 0; ft_attr.max_fte = MLX5E_FS_UDP_TABLE_SIZE; ft_attr.level = MLX5E_FS_TT_UDP_FT_LEVEL; ft_attr.prio = MLX5E_NIC_PRIO; - ft->t = mlx5_create_flow_table(priv->fs->ns, &ft_attr); + ft->t = mlx5_create_flow_table(ns, &ft_attr); if (IS_ERR(ft->t)) { err = PTR_ERR(ft->t); ft->t = NULL; return err; } - netdev_dbg(priv->netdev, "Created fs %s table id %u level %u\n", - fs_udp_type2str(type), ft->t->id, ft->t->level); + mlx5_core_dbg(mlx5e_fs_get_mdev(fs), "Created fs %s table id %u level %u\n", + fs_udp_type2str(type), ft->t->id, ft->t->level); err = fs_udp_create_groups(ft, type); if (err) goto err; - err = fs_udp_add_default_rule(priv, type); + err = fs_udp_add_default_rule(fs, type); if (err) goto err; @@ -253,17 +254,17 @@ static void fs_udp_destroy_table(struct mlx5e_fs_udp *fs_udp, int i) fs_udp->tables[i].t = NULL; } -static int fs_udp_disable(struct mlx5e_priv *priv) +static int fs_udp_disable(struct mlx5e_flow_steering *fs) { + struct mlx5_ttc_table *ttc = mlx5e_fs_get_ttc(fs, false); int err, i; for (i = 0; i < FS_UDP_NUM_TYPES; i++) { /* Modify ttc rules destination to point back to the indir TIRs */ - err = mlx5_ttc_fwd_default_dest(priv->fs->ttc, fs_udp2tt(i)); + err = mlx5_ttc_fwd_default_dest(ttc, fs_udp2tt(i)); if (err) { - netdev_err(priv->netdev, - "%s: modify ttc[%d] default destination failed, err(%d)\n", - __func__, fs_udp2tt(i), err); + fs_err(fs, "%s: modify ttc[%d] default destination failed, err(%d)\n", + __func__, fs_udp2tt(i), err); return err; } } @@ -271,30 +272,31 @@ static int fs_udp_disable(struct mlx5e_priv *priv) return 0; } -static int fs_udp_enable(struct mlx5e_priv *priv) +static int fs_udp_enable(struct mlx5e_flow_steering *fs) { + struct mlx5_ttc_table *ttc = mlx5e_fs_get_ttc(fs, false); + struct mlx5e_fs_udp *udp = mlx5e_fs_get_udp(fs); struct mlx5_flow_destination dest = {}; int err, i; dest.type = MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE; for (i = 0; i < FS_UDP_NUM_TYPES; i++) { - dest.ft = priv->fs->udp->tables[i].t; + dest.ft = udp->tables[i].t; /* Modify ttc rules destination to point on the accel_fs FTs */ - err = mlx5_ttc_fwd_dest(priv->fs->ttc, fs_udp2tt(i), &dest); + err = mlx5_ttc_fwd_dest(ttc, fs_udp2tt(i), &dest); if (err) { - netdev_err(priv->netdev, - "%s: modify ttc[%d] destination to accel failed, err(%d)\n", - __func__, fs_udp2tt(i), err); + fs_err(fs, "%s: modify ttc[%d] destination to accel failed, err(%d)\n", + __func__, fs_udp2tt(i), err); return err; } } return 0; } -void mlx5e_fs_tt_redirect_udp_destroy(struct mlx5e_priv *priv) +void mlx5e_fs_tt_redirect_udp_destroy(struct mlx5e_flow_steering *fs) { - struct mlx5e_fs_udp *fs_udp = priv->fs->udp; + struct mlx5e_fs_udp *fs_udp = mlx5e_fs_get_udp(fs); int i; if (!fs_udp) @@ -303,48 +305,50 @@ void mlx5e_fs_tt_redirect_udp_destroy(struct mlx5e_priv *priv) if (--fs_udp->ref_cnt) return; - fs_udp_disable(priv); + fs_udp_disable(fs); for (i = 0; i < FS_UDP_NUM_TYPES; i++) fs_udp_destroy_table(fs_udp, i); kfree(fs_udp); - priv->fs->udp = NULL; + mlx5e_fs_set_udp(fs, NULL); } -int mlx5e_fs_tt_redirect_udp_create(struct mlx5e_priv *priv) +int mlx5e_fs_tt_redirect_udp_create(struct mlx5e_flow_steering *fs) { + struct mlx5e_fs_udp *udp = mlx5e_fs_get_udp(fs); int i, err; - if (priv->fs->udp) { - priv->fs->udp->ref_cnt++; + if (udp) { + udp->ref_cnt++; return 0; } - priv->fs->udp = kzalloc(sizeof(*priv->fs->udp), GFP_KERNEL); - if (!priv->fs->udp) + udp = kzalloc(sizeof(*udp), GFP_KERNEL); + if (!udp) return -ENOMEM; + mlx5e_fs_set_udp(fs, udp); for (i = 0; i < FS_UDP_NUM_TYPES; i++) { - err = fs_udp_create_table(priv, i); + err = fs_udp_create_table(fs, i); if (err) goto err_destroy_tables; } - err = fs_udp_enable(priv); + err = fs_udp_enable(fs); if (err) goto err_destroy_tables; - priv->fs->udp->ref_cnt = 1; + udp->ref_cnt = 1; return 0; err_destroy_tables: while (--i >= 0) - fs_udp_destroy_table(priv->fs->udp, i); + fs_udp_destroy_table(udp, i); - kfree(priv->fs->udp); - priv->fs->udp = NULL; + kfree(udp); + mlx5e_fs_set_udp(fs, NULL); return err; } @@ -356,22 +360,21 @@ static void fs_any_set_ethertype_flow(struct mlx5_flow_spec *spec, u16 ether_typ } struct mlx5_flow_handle * -mlx5e_fs_tt_redirect_any_add_rule(struct mlx5e_priv *priv, +mlx5e_fs_tt_redirect_any_add_rule(struct mlx5e_flow_steering *fs, u32 tir_num, u16 ether_type) { + struct mlx5e_fs_any *fs_any = mlx5e_fs_get_any(fs); struct mlx5_flow_destination dest = {}; struct mlx5_flow_table *ft = NULL; MLX5_DECLARE_FLOW_ACT(flow_act); struct mlx5_flow_handle *rule; struct mlx5_flow_spec *spec; - struct mlx5e_fs_any *fs_any; int err; spec = kvzalloc(sizeof(*spec), GFP_KERNEL); if (!spec) return ERR_PTR(-ENOMEM); - fs_any = priv->fs->any; ft = fs_any->table.t; fs_any_set_ethertype_flow(spec, ether_type); @@ -383,31 +386,29 @@ mlx5e_fs_tt_redirect_any_add_rule(struct mlx5e_priv *priv, if (IS_ERR(rule)) { err = PTR_ERR(rule); - netdev_err(priv->netdev, "%s: add ANY rule failed, err %d\n", - __func__, err); + fs_err(fs, "%s: add ANY rule failed, err %d\n", + __func__, err); } return rule; } -static int fs_any_add_default_rule(struct mlx5e_priv *priv) +static int fs_any_add_default_rule(struct mlx5e_flow_steering *fs) { + struct mlx5_ttc_table *ttc = mlx5e_fs_get_ttc(fs, false); + struct mlx5e_fs_any *fs_any = mlx5e_fs_get_any(fs); struct mlx5e_flow_table *fs_any_t; struct mlx5_flow_destination dest; MLX5_DECLARE_FLOW_ACT(flow_act); struct mlx5_flow_handle *rule; - struct mlx5e_fs_any *fs_any; int err; - fs_any = priv->fs->any; fs_any_t = &fs_any->table; - - dest = mlx5_ttc_get_default_dest(priv->fs->ttc, MLX5_TT_ANY); + dest = mlx5_ttc_get_default_dest(ttc, MLX5_TT_ANY); rule = mlx5_add_flow_rules(fs_any_t->t, NULL, &flow_act, &dest, 1); if (IS_ERR(rule)) { err = PTR_ERR(rule); - netdev_err(priv->netdev, - "%s: add default rule failed, fs type=ANY, err %d\n", - __func__, err); + fs_err(fs, "%s: add default rule failed, fs type=ANY, err %d\n", + __func__, err); return err; } @@ -472,9 +473,11 @@ err: return err; } -static int fs_any_create_table(struct mlx5e_priv *priv) +static int fs_any_create_table(struct mlx5e_flow_steering *fs) { - struct mlx5e_flow_table *ft = &priv->fs->any->table; + struct mlx5_flow_namespace *ns = mlx5e_fs_get_ns(fs, false); + struct mlx5e_fs_any *fs_any = mlx5e_fs_get_any(fs); + struct mlx5e_flow_table *ft = &fs_any->table; struct mlx5_flow_table_attr ft_attr = {}; int err; @@ -484,21 +487,21 @@ static int fs_any_create_table(struct mlx5e_priv *priv) ft_attr.level = MLX5E_FS_TT_ANY_FT_LEVEL; ft_attr.prio = MLX5E_NIC_PRIO; - ft->t = mlx5_create_flow_table(priv->fs->ns, &ft_attr); + ft->t = mlx5_create_flow_table(ns, &ft_attr); if (IS_ERR(ft->t)) { err = PTR_ERR(ft->t); ft->t = NULL; return err; } - netdev_dbg(priv->netdev, "Created fs ANY table id %u level %u\n", - ft->t->id, ft->t->level); + mlx5_core_dbg(mlx5e_fs_get_mdev(fs), "Created fs ANY table id %u level %u\n", + ft->t->id, ft->t->level); err = fs_any_create_groups(ft); if (err) goto err; - err = fs_any_add_default_rule(priv); + err = fs_any_add_default_rule(fs); if (err) goto err; @@ -509,35 +512,38 @@ err: return err; } -static int fs_any_disable(struct mlx5e_priv *priv) +static int fs_any_disable(struct mlx5e_flow_steering *fs) { + struct mlx5_ttc_table *ttc = mlx5e_fs_get_ttc(fs, false); int err; /* Modify ttc rules destination to point back to the indir TIRs */ - err = mlx5_ttc_fwd_default_dest(priv->fs->ttc, MLX5_TT_ANY); + err = mlx5_ttc_fwd_default_dest(ttc, MLX5_TT_ANY); if (err) { - netdev_err(priv->netdev, - "%s: modify ttc[%d] default destination failed, err(%d)\n", - __func__, MLX5_TT_ANY, err); + fs_err(fs, + "%s: modify ttc[%d] default destination failed, err(%d)\n", + __func__, MLX5_TT_ANY, err); return err; } return 0; } -static int fs_any_enable(struct mlx5e_priv *priv) +static int fs_any_enable(struct mlx5e_flow_steering *fs) { + struct mlx5_ttc_table *ttc = mlx5e_fs_get_ttc(fs, false); + struct mlx5e_fs_any *any = mlx5e_fs_get_any(fs); struct mlx5_flow_destination dest = {}; int err; dest.type = MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE; - dest.ft = priv->fs->any->table.t; + dest.ft = any->table.t; /* Modify ttc rules destination to point on the accel_fs FTs */ - err = mlx5_ttc_fwd_dest(priv->fs->ttc, MLX5_TT_ANY, &dest); + err = mlx5_ttc_fwd_dest(ttc, MLX5_TT_ANY, &dest); if (err) { - netdev_err(priv->netdev, - "%s: modify ttc[%d] destination to accel failed, err(%d)\n", - __func__, MLX5_TT_ANY, err); + fs_err(fs, + "%s: modify ttc[%d] destination to accel failed, err(%d)\n", + __func__, MLX5_TT_ANY, err); return err; } return 0; @@ -553,9 +559,9 @@ static void fs_any_destroy_table(struct mlx5e_fs_any *fs_any) fs_any->table.t = NULL; } -void mlx5e_fs_tt_redirect_any_destroy(struct mlx5e_priv *priv) +void mlx5e_fs_tt_redirect_any_destroy(struct mlx5e_flow_steering *fs) { - struct mlx5e_fs_any *fs_any = priv->fs->any; + struct mlx5e_fs_any *fs_any = mlx5e_fs_get_any(fs); if (!fs_any) return; @@ -563,43 +569,45 @@ void mlx5e_fs_tt_redirect_any_destroy(struct mlx5e_priv *priv) if (--fs_any->ref_cnt) return; - fs_any_disable(priv); + fs_any_disable(fs); fs_any_destroy_table(fs_any); kfree(fs_any); - priv->fs->any = NULL; + mlx5e_fs_set_any(fs, NULL); } -int mlx5e_fs_tt_redirect_any_create(struct mlx5e_priv *priv) +int mlx5e_fs_tt_redirect_any_create(struct mlx5e_flow_steering *fs) { + struct mlx5e_fs_any *fs_any = mlx5e_fs_get_any(fs); int err; - if (priv->fs->any) { - priv->fs->any->ref_cnt++; + if (fs_any) { + fs_any->ref_cnt++; return 0; } - priv->fs->any = kzalloc(sizeof(*priv->fs->any), GFP_KERNEL); - if (!priv->fs->any) + fs_any = kzalloc(sizeof(*fs_any), GFP_KERNEL); + if (!fs_any) return -ENOMEM; + mlx5e_fs_set_any(fs, fs_any); - err = fs_any_create_table(priv); + err = fs_any_create_table(fs); if (err) return err; - err = fs_any_enable(priv); + err = fs_any_enable(fs); if (err) goto err_destroy_table; - priv->fs->any->ref_cnt = 1; + fs_any->ref_cnt = 1; return 0; err_destroy_table: - fs_any_destroy_table(priv->fs->any); + fs_any_destroy_table(fs_any); - kfree(priv->fs->any); - priv->fs->any = NULL; + kfree(fs_any); + mlx5e_fs_set_any(fs, NULL); return err; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/fs_tt_redirect.h b/drivers/net/ethernet/mellanox/mlx5/core/en/fs_tt_redirect.h index 7a70c4f38fda..5780fd7ad507 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/fs_tt_redirect.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/fs_tt_redirect.h @@ -4,23 +4,22 @@ #ifndef __MLX5E_FS_TT_REDIRECT_H__ #define __MLX5E_FS_TT_REDIRECT_H__ -#include "en.h" #include "en/fs.h" void mlx5e_fs_tt_redirect_del_rule(struct mlx5_flow_handle *rule); /* UDP traffic type redirect */ struct mlx5_flow_handle * -mlx5e_fs_tt_redirect_udp_add_rule(struct mlx5e_priv *priv, +mlx5e_fs_tt_redirect_udp_add_rule(struct mlx5e_flow_steering *fs, enum mlx5_traffic_types ttc_type, u32 tir_num, u16 d_port); -void mlx5e_fs_tt_redirect_udp_destroy(struct mlx5e_priv *priv); -int mlx5e_fs_tt_redirect_udp_create(struct mlx5e_priv *priv); +void mlx5e_fs_tt_redirect_udp_destroy(struct mlx5e_flow_steering *fs); +int mlx5e_fs_tt_redirect_udp_create(struct mlx5e_flow_steering *fs); /* ANY traffic type redirect*/ struct mlx5_flow_handle * -mlx5e_fs_tt_redirect_any_add_rule(struct mlx5e_priv *priv, +mlx5e_fs_tt_redirect_any_add_rule(struct mlx5e_flow_steering *fs, u32 tir_num, u16 ether_type); -void mlx5e_fs_tt_redirect_any_destroy(struct mlx5e_priv *priv); -int mlx5e_fs_tt_redirect_any_create(struct mlx5e_priv *priv); +void mlx5e_fs_tt_redirect_any_destroy(struct mlx5e_flow_steering *fs); +int mlx5e_fs_tt_redirect_any_create(struct mlx5e_flow_steering *fs); #endif diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/params.c b/drivers/net/ethernet/mellanox/mlx5/core/en/params.c index e025040350ba..29dd3a04c154 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/params.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/params.c @@ -6,11 +6,212 @@ #include "en/port.h" #include "en_accel/en_accel.h" #include "en_accel/ipsec.h" +#include <net/xdp_sock_drv.h> -static bool mlx5e_rx_is_xdp(struct mlx5e_params *params, - struct mlx5e_xsk_param *xsk) +static u8 mlx5e_mpwrq_min_page_shift(struct mlx5_core_dev *mdev) +{ + u8 min_page_shift = MLX5_CAP_GEN_2(mdev, log_min_mkey_entity_size); + + return min_page_shift ? : 12; +} + +u8 mlx5e_mpwrq_page_shift(struct mlx5_core_dev *mdev, struct mlx5e_xsk_param *xsk) +{ + u8 req_page_shift = xsk ? order_base_2(xsk->chunk_size) : PAGE_SHIFT; + u8 min_page_shift = mlx5e_mpwrq_min_page_shift(mdev); + + /* Regular RQ uses order-0 pages, the NIC must be able to map them. */ + if (WARN_ON_ONCE(!xsk && req_page_shift < min_page_shift)) + min_page_shift = req_page_shift; + + return max(req_page_shift, min_page_shift); +} + +enum mlx5e_mpwrq_umr_mode +mlx5e_mpwrq_umr_mode(struct mlx5_core_dev *mdev, struct mlx5e_xsk_param *xsk) +{ + /* Different memory management schemes use different mechanisms to map + * user-mode memory. The stricter guarantees we have, the faster + * mechanisms we use: + * 1. MTT - direct mapping in page granularity. + * 2. KSM - indirect mapping to another MKey to arbitrary addresses, but + * all mappings have the same size. + * 3. KLM - indirect mapping to another MKey to arbitrary addresses, and + * mappings can have different sizes. + */ + u8 page_shift = mlx5e_mpwrq_page_shift(mdev, xsk); + bool unaligned = xsk ? xsk->unaligned : false; + bool oversized = false; + + if (xsk) { + oversized = xsk->chunk_size < (1 << page_shift); + WARN_ON_ONCE(xsk->chunk_size > (1 << page_shift)); + } + + /* XSK frame size doesn't match the UMR page size, either because the + * frame size is not a power of two, or it's smaller than the minimal + * page size supported by the firmware. + * It's possible to receive packets bigger than MTU in certain setups. + * To avoid writing over the XSK frame boundary, the top region of each + * stride is mapped to a garbage page, resulting in two mappings of + * different sizes per frame. + */ + if (oversized) { + /* An optimization for frame sizes equal to 3 * power_of_two. + * 3 KSMs point to the frame, and one KSM points to the garbage + * page, which works faster than KLM. + */ + if (xsk->chunk_size % 3 == 0 && is_power_of_2(xsk->chunk_size / 3)) + return MLX5E_MPWRQ_UMR_MODE_TRIPLE; + + return MLX5E_MPWRQ_UMR_MODE_OVERSIZED; + } + + /* XSK frames can start at arbitrary unaligned locations, but they all + * have the same size which is a power of two. It allows to optimize to + * one KSM per frame. + */ + if (unaligned) + return MLX5E_MPWRQ_UMR_MODE_UNALIGNED; + + /* XSK: frames are naturally aligned, MTT can be used. + * Non-XSK: Allocations happen in units of CPU pages, therefore, the + * mappings are naturally aligned. + */ + return MLX5E_MPWRQ_UMR_MODE_ALIGNED; +} + +u8 mlx5e_mpwrq_umr_entry_size(enum mlx5e_mpwrq_umr_mode mode) +{ + switch (mode) { + case MLX5E_MPWRQ_UMR_MODE_ALIGNED: + return sizeof(struct mlx5_mtt); + case MLX5E_MPWRQ_UMR_MODE_UNALIGNED: + return sizeof(struct mlx5_ksm); + case MLX5E_MPWRQ_UMR_MODE_OVERSIZED: + return sizeof(struct mlx5_klm) * 2; + case MLX5E_MPWRQ_UMR_MODE_TRIPLE: + return sizeof(struct mlx5_ksm) * 4; + } + WARN_ONCE(1, "MPWRQ UMR mode %d is not known\n", mode); + return 0; +} + +u8 mlx5e_mpwrq_log_wqe_sz(struct mlx5_core_dev *mdev, u8 page_shift, + enum mlx5e_mpwrq_umr_mode umr_mode) +{ + u8 umr_entry_size = mlx5e_mpwrq_umr_entry_size(umr_mode); + u8 max_pages_per_wqe, max_log_mpwqe_size; + u16 max_wqe_size; + + /* Keep in sync with MLX5_MPWRQ_MAX_PAGES_PER_WQE. */ + max_wqe_size = mlx5e_get_max_sq_aligned_wqebbs(mdev) * MLX5_SEND_WQE_BB; + max_pages_per_wqe = ALIGN_DOWN(max_wqe_size - sizeof(struct mlx5e_umr_wqe), + MLX5_UMR_MTT_ALIGNMENT) / umr_entry_size; + max_log_mpwqe_size = ilog2(max_pages_per_wqe) + page_shift; + + WARN_ON_ONCE(max_log_mpwqe_size < MLX5E_ORDER2_MAX_PACKET_MTU); + + return min_t(u8, max_log_mpwqe_size, MLX5_MPWRQ_MAX_LOG_WQE_SZ); +} + +u8 mlx5e_mpwrq_pages_per_wqe(struct mlx5_core_dev *mdev, u8 page_shift, + enum mlx5e_mpwrq_umr_mode umr_mode) { - return params->xdp_prog || xsk; + u8 log_wqe_sz = mlx5e_mpwrq_log_wqe_sz(mdev, page_shift, umr_mode); + u8 pages_per_wqe; + + pages_per_wqe = log_wqe_sz > page_shift ? (1 << (log_wqe_sz - page_shift)) : 1; + + /* Two MTTs are needed to form an octword. The number of MTTs is encoded + * in octwords in a UMR WQE, so we need at least two to avoid mapping + * garbage addresses. + */ + if (WARN_ON_ONCE(pages_per_wqe < 2 && umr_mode == MLX5E_MPWRQ_UMR_MODE_ALIGNED)) + pages_per_wqe = 2; + + /* Sanity check for further calculations to succeed. */ + BUILD_BUG_ON(MLX5_MPWRQ_MAX_PAGES_PER_WQE > 64); + if (WARN_ON_ONCE(pages_per_wqe > MLX5_MPWRQ_MAX_PAGES_PER_WQE)) + return MLX5_MPWRQ_MAX_PAGES_PER_WQE; + + return pages_per_wqe; +} + +u16 mlx5e_mpwrq_umr_wqe_sz(struct mlx5_core_dev *mdev, u8 page_shift, + enum mlx5e_mpwrq_umr_mode umr_mode) +{ + u8 pages_per_wqe = mlx5e_mpwrq_pages_per_wqe(mdev, page_shift, umr_mode); + u8 umr_entry_size = mlx5e_mpwrq_umr_entry_size(umr_mode); + u16 umr_wqe_sz; + + umr_wqe_sz = sizeof(struct mlx5e_umr_wqe) + + ALIGN(pages_per_wqe * umr_entry_size, MLX5_UMR_MTT_ALIGNMENT); + + WARN_ON_ONCE(DIV_ROUND_UP(umr_wqe_sz, MLX5_SEND_WQE_DS) > MLX5_WQE_CTRL_DS_MASK); + + return umr_wqe_sz; +} + +u8 mlx5e_mpwrq_umr_wqebbs(struct mlx5_core_dev *mdev, u8 page_shift, + enum mlx5e_mpwrq_umr_mode umr_mode) +{ + return DIV_ROUND_UP(mlx5e_mpwrq_umr_wqe_sz(mdev, page_shift, umr_mode), + MLX5_SEND_WQE_BB); +} + +u8 mlx5e_mpwrq_mtts_per_wqe(struct mlx5_core_dev *mdev, u8 page_shift, + enum mlx5e_mpwrq_umr_mode umr_mode) +{ + u8 pages_per_wqe = mlx5e_mpwrq_pages_per_wqe(mdev, page_shift, umr_mode); + + /* Add another page as a buffer between WQEs. This page will absorb + * write overflow by the hardware, when receiving packets larger than + * MTU. These oversize packets are dropped by the driver at a later + * stage. + */ + return ALIGN(pages_per_wqe + 1, + MLX5_SEND_WQE_BB / mlx5e_mpwrq_umr_entry_size(umr_mode)); +} + +u32 mlx5e_mpwrq_max_num_entries(struct mlx5_core_dev *mdev, + enum mlx5e_mpwrq_umr_mode umr_mode) +{ + /* Same limits apply to KSMs and KLMs. */ + u32 klm_limit = min(MLX5E_MAX_RQ_NUM_KSMS, + 1 << MLX5_CAP_GEN(mdev, log_max_klm_list_size)); + + switch (umr_mode) { + case MLX5E_MPWRQ_UMR_MODE_ALIGNED: + return MLX5E_MAX_RQ_NUM_MTTS; + case MLX5E_MPWRQ_UMR_MODE_UNALIGNED: + return klm_limit; + case MLX5E_MPWRQ_UMR_MODE_OVERSIZED: + /* Each entry is two KLMs. */ + return klm_limit / 2; + case MLX5E_MPWRQ_UMR_MODE_TRIPLE: + /* Each entry is four KSMs. */ + return klm_limit / 4; + } + WARN_ONCE(1, "MPWRQ UMR mode %d is not known\n", umr_mode); + return 0; +} + +static u8 mlx5e_mpwrq_max_log_rq_size(struct mlx5_core_dev *mdev, u8 page_shift, + enum mlx5e_mpwrq_umr_mode umr_mode) +{ + u8 mtts_per_wqe = mlx5e_mpwrq_mtts_per_wqe(mdev, page_shift, umr_mode); + u32 max_entries = mlx5e_mpwrq_max_num_entries(mdev, umr_mode); + + return ilog2(max_entries / mtts_per_wqe); +} + +u8 mlx5e_mpwrq_max_log_rq_pkts(struct mlx5_core_dev *mdev, u8 page_shift, + enum mlx5e_mpwrq_umr_mode umr_mode) +{ + return mlx5e_mpwrq_max_log_rq_size(mdev, page_shift, umr_mode) + + mlx5e_mpwrq_log_wqe_sz(mdev, page_shift, umr_mode) - + MLX5E_ORDER2_MAX_PACKET_MTU; } u16 mlx5e_get_linear_rq_headroom(struct mlx5e_params *params, @@ -22,7 +223,7 @@ u16 mlx5e_get_linear_rq_headroom(struct mlx5e_params *params, return xsk->headroom; headroom = NET_IP_ALIGN; - if (mlx5e_rx_is_xdp(params, xsk)) + if (params->xdp_prog) headroom += XDP_PACKET_HEADROOM; else headroom += MLX5_RX_HEADROOM; @@ -30,70 +231,80 @@ u16 mlx5e_get_linear_rq_headroom(struct mlx5e_params *params, return headroom; } -u32 mlx5e_rx_get_min_frag_sz(struct mlx5e_params *params, - struct mlx5e_xsk_param *xsk) +static u32 mlx5e_rx_get_linear_sz_xsk(struct mlx5e_params *params, + struct mlx5e_xsk_param *xsk) { u32 hw_mtu = MLX5E_SW2HW_MTU(params, params->sw_mtu); - u16 linear_rq_headroom = mlx5e_get_linear_rq_headroom(params, xsk); - return linear_rq_headroom + hw_mtu; + return xsk->headroom + hw_mtu; } -static u32 mlx5e_rx_get_linear_frag_sz(struct mlx5e_params *params, - struct mlx5e_xsk_param *xsk) +static u32 mlx5e_rx_get_linear_sz_skb(struct mlx5e_params *params, bool xsk) { - u32 frag_sz = mlx5e_rx_get_min_frag_sz(params, xsk); - - /* AF_XDP doesn't build SKBs in place. */ - if (!xsk) - frag_sz = MLX5_SKB_FRAG_SZ(frag_sz); + /* SKBs built on XDP_PASS on XSK RQs don't have headroom. */ + u16 headroom = xsk ? 0 : mlx5e_get_linear_rq_headroom(params, NULL); + u32 hw_mtu = MLX5E_SW2HW_MTU(params, params->sw_mtu); - /* XDP in mlx5e doesn't support multiple packets per page. AF_XDP is a - * special case. It can run with frames smaller than a page, as it - * doesn't allocate pages dynamically. However, here we pretend that - * fragments are page-sized: it allows to treat XSK frames like pages - * by redirecting alloc and free operations to XSK rings and by using - * the fact there are no multiple packets per "page" (which is a frame). - * The latter is important, because frames may come in a random order, - * and we will have trouble assemblying a real page of multiple frames. - */ - if (mlx5e_rx_is_xdp(params, xsk)) - frag_sz = max_t(u32, frag_sz, PAGE_SIZE); + return MLX5_SKB_FRAG_SZ(headroom + hw_mtu); +} - /* Even if we can go with a smaller fragment size, we must not put - * multiple packets into a single frame. +static u32 mlx5e_rx_get_linear_stride_sz(struct mlx5_core_dev *mdev, + struct mlx5e_params *params, + struct mlx5e_xsk_param *xsk, + bool mpwqe) +{ + /* XSK frames are mapped as individual pages, because frames may come in + * an arbitrary order from random locations in the UMEM. */ if (xsk) - frag_sz = max_t(u32, frag_sz, xsk->chunk_size); + return mpwqe ? 1 << mlx5e_mpwrq_page_shift(mdev, xsk) : PAGE_SIZE; + + /* XDP in mlx5e doesn't support multiple packets per page. */ + if (params->xdp_prog) + return PAGE_SIZE; - return frag_sz; + return roundup_pow_of_two(mlx5e_rx_get_linear_sz_skb(params, false)); } -u8 mlx5e_mpwqe_log_pkts_per_wqe(struct mlx5e_params *params, - struct mlx5e_xsk_param *xsk) +static u8 mlx5e_mpwqe_log_pkts_per_wqe(struct mlx5_core_dev *mdev, + struct mlx5e_params *params, + struct mlx5e_xsk_param *xsk) { - u32 linear_frag_sz = mlx5e_rx_get_linear_frag_sz(params, xsk); + u32 linear_stride_sz = mlx5e_rx_get_linear_stride_sz(mdev, params, xsk, true); + enum mlx5e_mpwrq_umr_mode umr_mode = mlx5e_mpwrq_umr_mode(mdev, xsk); + u8 page_shift = mlx5e_mpwrq_page_shift(mdev, xsk); - return MLX5_MPWRQ_LOG_WQE_SZ - order_base_2(linear_frag_sz); + return mlx5e_mpwrq_log_wqe_sz(mdev, page_shift, umr_mode) - + order_base_2(linear_stride_sz); } -bool mlx5e_rx_is_linear_skb(struct mlx5e_params *params, +bool mlx5e_rx_is_linear_skb(struct mlx5_core_dev *mdev, + struct mlx5e_params *params, struct mlx5e_xsk_param *xsk) { - /* AF_XDP allocates SKBs on XDP_PASS - ensure they don't occupy more - * than one page. For this, check both with and without xsk. + if (params->packet_merge.type != MLX5E_PACKET_MERGE_NONE) + return false; + + /* Both XSK and non-XSK cases allocate an SKB on XDP_PASS. Packet data + * must fit into a CPU page. */ - u32 linear_frag_sz = max(mlx5e_rx_get_linear_frag_sz(params, xsk), - mlx5e_rx_get_linear_frag_sz(params, NULL)); + if (mlx5e_rx_get_linear_sz_skb(params, xsk) > PAGE_SIZE) + return false; + + /* XSK frames must be big enough to hold the packet data. */ + if (xsk && mlx5e_rx_get_linear_sz_xsk(params, xsk) > xsk->chunk_size) + return false; - return params->packet_merge.type == MLX5E_PACKET_MERGE_NONE && - linear_frag_sz <= PAGE_SIZE; + return true; } -bool mlx5e_verify_rx_mpwqe_strides(struct mlx5_core_dev *mdev, - u8 log_stride_sz, u8 log_num_strides) +static bool mlx5e_verify_rx_mpwqe_strides(struct mlx5_core_dev *mdev, + u8 log_stride_sz, u8 log_num_strides, + u8 page_shift, + enum mlx5e_mpwrq_umr_mode umr_mode) { - if (log_stride_sz + log_num_strides != MLX5_MPWRQ_LOG_WQE_SZ) + if (log_stride_sz + log_num_strides != + mlx5e_mpwrq_log_wqe_sz(mdev, page_shift, umr_mode)) return false; if (log_stride_sz < MLX5_MPWQE_LOG_STRIDE_SZ_BASE || @@ -113,28 +324,53 @@ bool mlx5e_rx_mpwqe_is_linear_skb(struct mlx5_core_dev *mdev, struct mlx5e_params *params, struct mlx5e_xsk_param *xsk) { - s8 log_num_strides; + enum mlx5e_mpwrq_umr_mode umr_mode = mlx5e_mpwrq_umr_mode(mdev, xsk); + u8 page_shift = mlx5e_mpwrq_page_shift(mdev, xsk); + u8 log_num_strides; u8 log_stride_sz; + u8 log_wqe_sz; - if (!mlx5e_rx_is_linear_skb(params, xsk)) + if (!mlx5e_rx_is_linear_skb(mdev, params, xsk)) return false; - log_stride_sz = order_base_2(mlx5e_rx_get_linear_frag_sz(params, xsk)); - log_num_strides = MLX5_MPWRQ_LOG_WQE_SZ - log_stride_sz; + log_stride_sz = order_base_2(mlx5e_rx_get_linear_stride_sz(mdev, params, xsk, true)); + log_wqe_sz = mlx5e_mpwrq_log_wqe_sz(mdev, page_shift, umr_mode); - return mlx5e_verify_rx_mpwqe_strides(mdev, log_stride_sz, log_num_strides); + if (log_wqe_sz < log_stride_sz) + return false; + + log_num_strides = log_wqe_sz - log_stride_sz; + + return mlx5e_verify_rx_mpwqe_strides(mdev, log_stride_sz, + log_num_strides, page_shift, + umr_mode); } -u8 mlx5e_mpwqe_get_log_rq_size(struct mlx5e_params *params, +u8 mlx5e_mpwqe_get_log_rq_size(struct mlx5_core_dev *mdev, + struct mlx5e_params *params, struct mlx5e_xsk_param *xsk) { - u8 log_pkts_per_wqe = mlx5e_mpwqe_log_pkts_per_wqe(params, xsk); + enum mlx5e_mpwrq_umr_mode umr_mode = mlx5e_mpwrq_umr_mode(mdev, xsk); + u8 log_pkts_per_wqe, page_shift, max_log_rq_size; + + log_pkts_per_wqe = mlx5e_mpwqe_log_pkts_per_wqe(mdev, params, xsk); + page_shift = mlx5e_mpwrq_page_shift(mdev, xsk); + max_log_rq_size = mlx5e_mpwrq_max_log_rq_size(mdev, page_shift, umr_mode); /* Numbers are unsigned, don't subtract to avoid underflow. */ if (params->log_rq_mtu_frames < log_pkts_per_wqe + MLX5E_PARAMS_MINIMUM_LOG_RQ_SIZE_MPW) return MLX5E_PARAMS_MINIMUM_LOG_RQ_SIZE_MPW; + /* Ethtool's rx_max_pending is calculated for regular RQ, that uses + * pages of PAGE_SIZE. Max length of an XSK RQ might differ if it uses a + * frame size not equal to PAGE_SIZE. + * A stricter condition is checked in mlx5e_mpwrq_validate_xsk, WARN on + * unexpected failure. + */ + if (WARN_ON_ONCE(params->log_rq_mtu_frames > log_pkts_per_wqe + max_log_rq_size)) + return max_log_rq_size; + return params->log_rq_mtu_frames - log_pkts_per_wqe; } @@ -164,7 +400,7 @@ u8 mlx5e_mpwqe_get_log_stride_size(struct mlx5_core_dev *mdev, struct mlx5e_xsk_param *xsk) { if (mlx5e_rx_mpwqe_is_linear_skb(mdev, params, xsk)) - return order_base_2(mlx5e_rx_get_linear_frag_sz(params, xsk)); + return order_base_2(mlx5e_rx_get_linear_stride_sz(mdev, params, xsk, true)); return MLX5_MPWRQ_DEF_LOG_STRIDE_SZ(mdev); } @@ -173,7 +409,10 @@ u8 mlx5e_mpwqe_get_log_num_strides(struct mlx5_core_dev *mdev, struct mlx5e_params *params, struct mlx5e_xsk_param *xsk) { - return MLX5_MPWRQ_LOG_WQE_SZ - + enum mlx5e_mpwrq_umr_mode umr_mode = mlx5e_mpwrq_umr_mode(mdev, xsk); + u8 page_shift = mlx5e_mpwrq_page_shift(mdev, xsk); + + return mlx5e_mpwrq_log_wqe_sz(mdev, page_shift, umr_mode) - mlx5e_mpwqe_get_log_stride_size(mdev, params, xsk); } @@ -209,11 +448,11 @@ u16 mlx5e_calc_sq_stop_room(struct mlx5_core_dev *mdev, struct mlx5e_params *par stop_room = mlx5e_ktls_get_stop_room(mdev, params); stop_room += mlx5e_stop_room_for_max_wqe(mdev); if (is_mpwqe) - /* A MPWQE can take up to the maximum-sized WQE + all the normal - * stop room can be taken if a new packet breaks the active - * MPWQE session and allocates its WQEs right away. + /* A MPWQE can take up to the maximum cacheline-aligned WQE + + * all the normal stop room can be taken if a new packet breaks + * the active MPWQE session and allocates its WQEs right away. */ - stop_room += mlx5e_stop_room_for_max_wqe(mdev); + stop_room += mlx5e_stop_room_for_mpwqe(mdev); return stop_room; } @@ -320,22 +559,46 @@ bool slow_pci_heuristic(struct mlx5_core_dev *mdev) link_speed > MLX5E_SLOW_PCI_RATIO * pci_bw; } -bool mlx5e_striding_rq_possible(struct mlx5_core_dev *mdev, - struct mlx5e_params *params) +int mlx5e_mpwrq_validate_regular(struct mlx5_core_dev *mdev, struct mlx5e_params *params) { - if (!mlx5e_check_fragmented_striding_rq_cap(mdev)) - return false; + enum mlx5e_mpwrq_umr_mode umr_mode = mlx5e_mpwrq_umr_mode(mdev, NULL); + u8 page_shift = mlx5e_mpwrq_page_shift(mdev, NULL); - if (params->xdp_prog) { - /* XSK params are not considered here. If striding RQ is in use, - * and an XSK is being opened, mlx5e_rx_mpwqe_is_linear_skb will - * be called with the known XSK params. - */ - if (!mlx5e_rx_mpwqe_is_linear_skb(mdev, params, NULL)) - return false; + if (!mlx5e_check_fragmented_striding_rq_cap(mdev, page_shift, umr_mode)) + return -EOPNOTSUPP; + + if (params->xdp_prog && !mlx5e_rx_mpwqe_is_linear_skb(mdev, params, NULL)) + return -EINVAL; + + return 0; +} + +int mlx5e_mpwrq_validate_xsk(struct mlx5_core_dev *mdev, struct mlx5e_params *params, + struct mlx5e_xsk_param *xsk) +{ + enum mlx5e_mpwrq_umr_mode umr_mode = mlx5e_mpwrq_umr_mode(mdev, xsk); + u8 page_shift = mlx5e_mpwrq_page_shift(mdev, xsk); + bool unaligned = xsk ? xsk->unaligned : false; + u16 max_mtu_pkts; + + if (!mlx5e_check_fragmented_striding_rq_cap(mdev, page_shift, umr_mode)) + return -EOPNOTSUPP; + + if (!mlx5e_rx_mpwqe_is_linear_skb(mdev, params, xsk)) + return -EINVAL; + + /* Current RQ length is too big for the given frame size, the + * needed number of WQEs exceeds the maximum. + */ + max_mtu_pkts = min_t(u8, MLX5E_PARAMS_MAXIMUM_LOG_RQ_SIZE, + mlx5e_mpwrq_max_log_rq_pkts(mdev, page_shift, unaligned)); + if (params->log_rq_mtu_frames > max_mtu_pkts) { + mlx5_core_err(mdev, "Current RQ length %d is too big for XSK with given frame size %u\n", + 1 << params->log_rq_mtu_frames, xsk->chunk_size); + return -EINVAL; } - return true; + return 0; } void mlx5e_init_rq_type_params(struct mlx5_core_dev *mdev, @@ -348,7 +611,7 @@ void mlx5e_init_rq_type_params(struct mlx5_core_dev *mdev, mlx5_core_info(mdev, "MLX5E: StrdRq(%d) RqSz(%ld) StrdSz(%ld) RxCqeCmprss(%d)\n", params->rq_wq_type == MLX5_WQ_TYPE_LINKED_LIST_STRIDING_RQ, params->rq_wq_type == MLX5_WQ_TYPE_LINKED_LIST_STRIDING_RQ ? - BIT(mlx5e_mpwqe_get_log_rq_size(params, NULL)) : + BIT(mlx5e_mpwqe_get_log_rq_size(mdev, params, NULL)) : BIT(params->log_rq_mtu_frames), BIT(mlx5e_mpwqe_get_log_stride_size(mdev, params, NULL)), MLX5E_GET_PFLAG(params, MLX5E_PFLAG_RX_CQE_COMPRESS)); @@ -356,8 +619,7 @@ void mlx5e_init_rq_type_params(struct mlx5_core_dev *mdev, void mlx5e_set_rq_type(struct mlx5_core_dev *mdev, struct mlx5e_params *params) { - params->rq_wq_type = mlx5e_striding_rq_possible(mdev, params) && - MLX5E_GET_PFLAG(params, MLX5E_PFLAG_RX_STRIDING_RQ) ? + params->rq_wq_type = MLX5E_GET_PFLAG(params, MLX5E_PFLAG_RX_STRIDING_RQ) ? MLX5_WQ_TYPE_LINKED_LIST_STRIDING_RQ : MLX5_WQ_TYPE_CYCLIC; } @@ -374,9 +636,9 @@ void mlx5e_build_rq_params(struct mlx5_core_dev *mdev, */ if ((!MLX5E_GET_PFLAG(params, MLX5E_PFLAG_RX_CQE_COMPRESS) || MLX5_CAP_GEN(mdev, mini_cqe_resp_stride_index)) && - mlx5e_striding_rq_possible(mdev, params) && + !mlx5e_mpwrq_validate_regular(mdev, params) && (mlx5e_rx_mpwqe_is_linear_skb(mdev, params, NULL) || - !mlx5e_rx_is_linear_skb(params, NULL))) + !mlx5e_rx_is_linear_skb(mdev, params, NULL))) MLX5E_SET_PFLAG(params, MLX5E_PFLAG_RX_STRIDING_RQ, true); mlx5e_set_rq_type(mdev, params); mlx5e_init_rq_type_params(mdev, params); @@ -419,16 +681,22 @@ static int mlx5e_build_rq_frags_info(struct mlx5_core_dev *mdev, int max_mtu; int i; - if (mlx5e_rx_is_linear_skb(params, xsk)) { + if (mlx5e_rx_is_linear_skb(mdev, params, xsk)) { int frag_stride; - frag_stride = mlx5e_rx_get_linear_frag_sz(params, xsk); - frag_stride = roundup_pow_of_two(frag_stride); + frag_stride = mlx5e_rx_get_linear_stride_sz(mdev, params, xsk, false); info->arr[0].frag_size = byte_count; info->arr[0].frag_stride = frag_stride; info->num_frags = 1; - info->wqe_bulk = PAGE_SIZE / frag_stride; + + /* N WQEs share the same page, N = PAGE_SIZE / frag_stride. The + * first WQE in the page is responsible for allocation of this + * page, this WQE's index is k*N. If WQEs [k*N+1; k*N+N-1] are + * still not completed, the allocation must stop before k*N. + */ + info->wqe_index_mask = (PAGE_SIZE / frag_stride) - 1; + goto out; } @@ -477,11 +745,40 @@ static int mlx5e_build_rq_frags_info(struct mlx5_core_dev *mdev, i++; } info->num_frags = i; - /* number of different wqes sharing a page */ - info->wqe_bulk = 1 + (info->num_frags % 2); + + /* The last fragment of WQE with index 2*N may share the page with the + * first fragment of WQE with index 2*N+1 in certain cases. If WQE 2*N+1 + * is not completed yet, WQE 2*N must not be allocated, as it's + * responsible for allocating a new page. + */ + if (frag_size_max == PAGE_SIZE) { + /* No WQE can start in the middle of a page. */ + info->wqe_index_mask = 0; + } else { + /* PAGE_SIZEs starting from 8192 don't use 2K-sized fragments, + * because there would be more than MLX5E_MAX_RX_FRAGS of them. + */ + WARN_ON(PAGE_SIZE != 2 * DEFAULT_FRAG_SIZE); + + /* Odd number of fragments allows to pack the last fragment of + * the previous WQE and the first fragment of the next WQE into + * the same page. + * As long as DEFAULT_FRAG_SIZE is 2048, and MLX5E_MAX_RX_FRAGS + * is 4, the last fragment can be bigger than the rest only if + * it's the fourth one, so WQEs consisting of 3 fragments will + * always share a page. + * When a page is shared, WQE bulk size is 2, otherwise just 1. + */ + info->wqe_index_mask = info->num_frags % 2; + } out: - info->wqe_bulk = max_t(u8, info->wqe_bulk, 8); + /* Bulking optimization to skip allocation until at least 8 WQEs can be + * allocated in a row. At the same time, never start allocation when + * the page is still used by older WQEs. + */ + info->wqe_bulk = max_t(u8, info->wqe_index_mask + 1, 8); + info->log_num_frags = order_base_2(info->num_frags); return 0; @@ -520,7 +817,7 @@ static u32 mlx5e_shampo_get_log_cq_size(struct mlx5_core_dev *mdev, u16 num_strides = BIT(mlx5e_mpwqe_get_log_num_strides(mdev, params, xsk)); int pkt_per_rsrv = BIT(mlx5e_shampo_get_log_pkt_per_rsrv(mdev, params)); u8 log_stride_sz = mlx5e_mpwqe_get_log_stride_size(mdev, params, xsk); - int wq_size = BIT(mlx5e_mpwqe_get_log_rq_size(params, xsk)); + int wq_size = BIT(mlx5e_mpwqe_get_log_rq_size(mdev, params, xsk)); int wqe_size = BIT(log_stride_sz) * num_strides; /* +1 is for the case that the pkt_per_rsrv dont consume the reservation @@ -544,7 +841,7 @@ static void mlx5e_build_rx_cq_param(struct mlx5_core_dev *mdev, if (params->packet_merge.type == MLX5E_PACKET_MERGE_SHAMPO) log_cq_size = mlx5e_shampo_get_log_cq_size(mdev, params, xsk); else - log_cq_size = mlx5e_mpwqe_get_log_rq_size(params, xsk) + + log_cq_size = mlx5e_mpwqe_get_log_rq_size(mdev, params, xsk) + mlx5e_mpwqe_get_log_num_strides(mdev, params, xsk); break; default: /* MLX5_WQ_TYPE_CYCLIC */ @@ -587,12 +884,16 @@ int mlx5e_build_rq_param(struct mlx5_core_dev *mdev, case MLX5_WQ_TYPE_LINKED_LIST_STRIDING_RQ: { u8 log_wqe_num_of_strides = mlx5e_mpwqe_get_log_num_strides(mdev, params, xsk); u8 log_wqe_stride_size = mlx5e_mpwqe_get_log_stride_size(mdev, params, xsk); + enum mlx5e_mpwrq_umr_mode umr_mode = mlx5e_mpwrq_umr_mode(mdev, xsk); + u8 page_shift = mlx5e_mpwrq_page_shift(mdev, xsk); if (!mlx5e_verify_rx_mpwqe_strides(mdev, log_wqe_stride_size, - log_wqe_num_of_strides)) { + log_wqe_num_of_strides, + page_shift, umr_mode)) { mlx5_core_err(mdev, - "Bad RX MPWQE params: log_stride_size %u, log_num_strides %u\n", - log_wqe_stride_size, log_wqe_num_of_strides); + "Bad RX MPWQE params: log_stride_size %u, log_num_strides %u, umr_mode %d\n", + log_wqe_stride_size, log_wqe_num_of_strides, + umr_mode); return -EINVAL; } @@ -600,7 +901,7 @@ int mlx5e_build_rq_param(struct mlx5_core_dev *mdev, log_wqe_num_of_strides - MLX5_MPWQE_LOG_NUM_STRIDES_BASE); MLX5_SET(wq, wq, log_wqe_stride_size, log_wqe_stride_size - MLX5_MPWQE_LOG_STRIDE_SZ_BASE); - MLX5_SET(wq, wq, log_wq_sz, mlx5e_mpwqe_get_log_rq_size(params, xsk)); + MLX5_SET(wq, wq, log_wq_sz, mlx5e_mpwqe_get_log_rq_size(mdev, params, xsk)); if (params->packet_merge.type == MLX5E_PACKET_MERGE_SHAMPO) { MLX5_SET(wq, wq, shampo_enable, true); MLX5_SET(wq, wq, log_reservation_size, @@ -712,13 +1013,6 @@ static void mlx5e_build_ico_cq_param(struct mlx5_core_dev *mdev, param->cq_period_mode = DIM_CQ_PERIOD_MODE_START_FROM_EQE; } -static u8 mlx5e_get_rq_log_wq_sz(void *rqc) -{ - void *wq = MLX5_ADDR_OF(rqc, rqc, wq); - - return MLX5_GET(wq, wq, log_wq_sz); -} - /* This function calculates the maximum number of headers entries that are needed * per WQE, the formula is based on the size of the reservations and the * restriction we have about max packets for reservation that is equal to max @@ -779,31 +1073,92 @@ static u32 mlx5e_shampo_icosq_sz(struct mlx5_core_dev *mdev, return wqebbs; } +static u32 mlx5e_mpwrq_total_umr_wqebbs(struct mlx5_core_dev *mdev, + struct mlx5e_params *params, + struct mlx5e_xsk_param *xsk) +{ + enum mlx5e_mpwrq_umr_mode umr_mode = mlx5e_mpwrq_umr_mode(mdev, xsk); + u8 page_shift = mlx5e_mpwrq_page_shift(mdev, xsk); + u8 umr_wqebbs; + + umr_wqebbs = mlx5e_mpwrq_umr_wqebbs(mdev, page_shift, umr_mode); + + return umr_wqebbs * (1 << mlx5e_mpwqe_get_log_rq_size(mdev, params, xsk)); +} + static u8 mlx5e_build_icosq_log_wq_sz(struct mlx5_core_dev *mdev, struct mlx5e_params *params, struct mlx5e_rq_param *rqp) { - u32 wqebbs; + u32 wqebbs, total_pages, useful_space; /* MLX5_WQ_TYPE_CYCLIC */ if (params->rq_wq_type != MLX5_WQ_TYPE_LINKED_LIST_STRIDING_RQ) return MLX5E_PARAMS_MINIMUM_LOG_SQ_SIZE; - wqebbs = MLX5E_UMR_WQEBBS * BIT(mlx5e_get_rq_log_wq_sz(rqp->rqc)); + /* UMR WQEs for the regular RQ. */ + wqebbs = mlx5e_mpwrq_total_umr_wqebbs(mdev, params, NULL); /* If XDP program is attached, XSK may be turned on at any time without * restarting the channel. ICOSQ must be big enough to fit UMR WQEs of * both regular RQ and XSK RQ. - * Although mlx5e_mpwqe_get_log_rq_size accepts mlx5e_xsk_param, it - * doesn't affect its return value, as long as params->xdp_prog != NULL, - * so we can just multiply by 2. + * + * XSK uses different values of page_shift, and the total number of UMR + * WQEBBs depends on it. This dependency is complex and not monotonic, + * especially taking into consideration that some of the parameters come + * from capabilities. Hence, we have to try all valid values of XSK + * frame size (and page_shift) to find the maximum. */ - if (params->xdp_prog) - wqebbs *= 2; + if (params->xdp_prog) { + u32 max_xsk_wqebbs = 0; + u8 frame_shift; + + for (frame_shift = XDP_UMEM_MIN_CHUNK_SHIFT; + frame_shift <= PAGE_SHIFT; frame_shift++) { + /* The headroom doesn't affect the calculation. */ + struct mlx5e_xsk_param xsk = { + .chunk_size = 1 << frame_shift, + .unaligned = false, + }; + + /* XSK aligned mode. */ + max_xsk_wqebbs = max(max_xsk_wqebbs, + mlx5e_mpwrq_total_umr_wqebbs(mdev, params, &xsk)); + + /* XSK unaligned mode, frame size is a power of two. */ + xsk.unaligned = true; + max_xsk_wqebbs = max(max_xsk_wqebbs, + mlx5e_mpwrq_total_umr_wqebbs(mdev, params, &xsk)); + + /* XSK unaligned mode, frame size is not equal to stride size. */ + xsk.chunk_size -= 1; + max_xsk_wqebbs = max(max_xsk_wqebbs, + mlx5e_mpwrq_total_umr_wqebbs(mdev, params, &xsk)); + + /* XSK unaligned mode, frame size is a triple power of two. */ + xsk.chunk_size = (1 << frame_shift) / 4 * 3; + max_xsk_wqebbs = max(max_xsk_wqebbs, + mlx5e_mpwrq_total_umr_wqebbs(mdev, params, &xsk)); + } + + wqebbs += max_xsk_wqebbs; + } if (params->packet_merge.type == MLX5E_PACKET_MERGE_SHAMPO) wqebbs += mlx5e_shampo_icosq_sz(mdev, params, rqp); + /* UMR WQEs don't cross the page boundary, they are padded with NOPs. + * This padding is always smaller than the max WQE size. That gives us + * at least (PAGE_SIZE - (max WQE size - MLX5_SEND_WQE_BB)) useful bytes + * per page. The number of pages is estimated as the total size of WQEs + * divided by the useful space in page, rounding up. If some WQEs don't + * fully fit into the useful space, they can occupy part of the padding, + * which proves this estimation to be correct (reserve enough space). + */ + useful_space = PAGE_SIZE - mlx5e_get_max_sq_wqebbs(mdev) + MLX5_SEND_WQE_BB; + total_pages = DIV_ROUND_UP(wqebbs * MLX5_SEND_WQE_BB, useful_space); + wqebbs = total_pages * (PAGE_SIZE / MLX5_SEND_WQE_BB); + return max_t(u8, MLX5E_PARAMS_MINIMUM_LOG_SQ_SIZE, order_base_2(wqebbs)); } @@ -857,7 +1212,7 @@ void mlx5e_build_xdpsq_param(struct mlx5_core_dev *mdev, mlx5e_build_sq_param_common(mdev, param); MLX5_SET(wq, wq, log_wq_sz, params->log_sq_size); param->is_mpw = MLX5E_GET_PFLAG(params, MLX5E_PFLAG_XDP_TX_MPWQE); - param->is_xdp_mb = !mlx5e_rx_is_linear_skb(params, xsk); + param->is_xdp_mb = !mlx5e_rx_is_linear_skb(mdev, params, xsk); mlx5e_build_tx_cq_param(mdev, params, ¶m->cqp); } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/params.h b/drivers/net/ethernet/mellanox/mlx5/core/en/params.h index f5c46e78eebc..034debd140bc 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/params.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/params.h @@ -9,6 +9,7 @@ struct mlx5e_xsk_param { u16 headroom; u16 chunk_size; + bool unaligned; }; struct mlx5e_cq_param { @@ -52,37 +53,26 @@ struct mlx5e_create_sq_param { u8 min_inline_mode; }; -static inline bool mlx5e_qid_get_ch_if_in_group(struct mlx5e_params *params, - u16 qid, - enum mlx5e_rq_group group, - u16 *ix) -{ - int nch = params->num_channels; - int ch = qid - nch * group; - - if (ch < 0 || ch >= nch) - return false; - - *ix = ch; - return true; -} - -static inline void mlx5e_qid_get_ch_and_group(struct mlx5e_params *params, - u16 qid, - u16 *ix, - enum mlx5e_rq_group *group) -{ - u16 nch = params->num_channels; - - *ix = qid % nch; - *group = qid / nch; -} - -static inline bool mlx5e_qid_validate(const struct mlx5e_profile *profile, - struct mlx5e_params *params, u64 qid) -{ - return qid < params->num_channels * profile->rq_groups; -} +/* Striding RQ dynamic parameters */ + +u8 mlx5e_mpwrq_page_shift(struct mlx5_core_dev *mdev, struct mlx5e_xsk_param *xsk); +enum mlx5e_mpwrq_umr_mode +mlx5e_mpwrq_umr_mode(struct mlx5_core_dev *mdev, struct mlx5e_xsk_param *xsk); +u8 mlx5e_mpwrq_umr_entry_size(enum mlx5e_mpwrq_umr_mode mode); +u8 mlx5e_mpwrq_log_wqe_sz(struct mlx5_core_dev *mdev, u8 page_shift, + enum mlx5e_mpwrq_umr_mode umr_mode); +u8 mlx5e_mpwrq_pages_per_wqe(struct mlx5_core_dev *mdev, u8 page_shift, + enum mlx5e_mpwrq_umr_mode umr_mode); +u16 mlx5e_mpwrq_umr_wqe_sz(struct mlx5_core_dev *mdev, u8 page_shift, + enum mlx5e_mpwrq_umr_mode umr_mode); +u8 mlx5e_mpwrq_umr_wqebbs(struct mlx5_core_dev *mdev, u8 page_shift, + enum mlx5e_mpwrq_umr_mode umr_mode); +u8 mlx5e_mpwrq_mtts_per_wqe(struct mlx5_core_dev *mdev, u8 page_shift, + enum mlx5e_mpwrq_umr_mode umr_mode); +u32 mlx5e_mpwrq_max_num_entries(struct mlx5_core_dev *mdev, + enum mlx5e_mpwrq_umr_mode umr_mode); +u8 mlx5e_mpwrq_max_log_rq_pkts(struct mlx5_core_dev *mdev, u8 page_shift, + enum mlx5e_mpwrq_umr_mode umr_mode); /* Parameter calculations */ @@ -92,25 +82,23 @@ void mlx5e_set_tx_cq_mode_params(struct mlx5e_params *params, u8 cq_period_mode) void mlx5e_set_rx_cq_mode_params(struct mlx5e_params *params, u8 cq_period_mode); bool slow_pci_heuristic(struct mlx5_core_dev *mdev); -bool mlx5e_striding_rq_possible(struct mlx5_core_dev *mdev, struct mlx5e_params *params); +int mlx5e_mpwrq_validate_regular(struct mlx5_core_dev *mdev, struct mlx5e_params *params); +int mlx5e_mpwrq_validate_xsk(struct mlx5_core_dev *mdev, struct mlx5e_params *params, + struct mlx5e_xsk_param *xsk); void mlx5e_build_rq_params(struct mlx5_core_dev *mdev, struct mlx5e_params *params); void mlx5e_set_rq_type(struct mlx5_core_dev *mdev, struct mlx5e_params *params); void mlx5e_init_rq_type_params(struct mlx5_core_dev *mdev, struct mlx5e_params *params); -bool mlx5e_verify_rx_mpwqe_strides(struct mlx5_core_dev *mdev, - u8 log_stride_sz, u8 log_num_strides); u16 mlx5e_get_linear_rq_headroom(struct mlx5e_params *params, struct mlx5e_xsk_param *xsk); -u32 mlx5e_rx_get_min_frag_sz(struct mlx5e_params *params, - struct mlx5e_xsk_param *xsk); -u8 mlx5e_mpwqe_log_pkts_per_wqe(struct mlx5e_params *params, - struct mlx5e_xsk_param *xsk); -bool mlx5e_rx_is_linear_skb(struct mlx5e_params *params, +bool mlx5e_rx_is_linear_skb(struct mlx5_core_dev *mdev, + struct mlx5e_params *params, struct mlx5e_xsk_param *xsk); bool mlx5e_rx_mpwqe_is_linear_skb(struct mlx5_core_dev *mdev, struct mlx5e_params *params, struct mlx5e_xsk_param *xsk); -u8 mlx5e_mpwqe_get_log_rq_size(struct mlx5e_params *params, +u8 mlx5e_mpwqe_get_log_rq_size(struct mlx5_core_dev *mdev, + struct mlx5e_params *params, struct mlx5e_xsk_param *xsk); u8 mlx5e_shampo_get_log_hd_entry_size(struct mlx5_core_dev *mdev, struct mlx5e_params *params); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/ptp.c b/drivers/net/ethernet/mellanox/mlx5/core/en/ptp.c index 903de88bab53..8469e9c38670 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/ptp.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/ptp.c @@ -622,37 +622,39 @@ static int mlx5e_ptp_set_state(struct mlx5e_ptp *c, struct mlx5e_params *params) return bitmap_empty(c->state, MLX5E_PTP_STATE_NUM_STATES) ? -EINVAL : 0; } -static void mlx5e_ptp_rx_unset_fs(struct mlx5e_priv *priv) +static void mlx5e_ptp_rx_unset_fs(struct mlx5e_flow_steering *fs) { - struct mlx5e_ptp_fs *ptp_fs = priv->fs->ptp_fs; + struct mlx5e_ptp_fs *ptp_fs = mlx5e_fs_get_ptp(fs); if (!ptp_fs->valid) return; mlx5e_fs_tt_redirect_del_rule(ptp_fs->l2_rule); - mlx5e_fs_tt_redirect_any_destroy(priv); + mlx5e_fs_tt_redirect_any_destroy(fs); mlx5e_fs_tt_redirect_del_rule(ptp_fs->udp_v6_rule); mlx5e_fs_tt_redirect_del_rule(ptp_fs->udp_v4_rule); - mlx5e_fs_tt_redirect_udp_destroy(priv); + mlx5e_fs_tt_redirect_udp_destroy(fs); ptp_fs->valid = false; } static int mlx5e_ptp_rx_set_fs(struct mlx5e_priv *priv) { u32 tirn = mlx5e_rx_res_get_tirn_ptp(priv->rx_res); - struct mlx5e_ptp_fs *ptp_fs = priv->fs->ptp_fs; + struct mlx5e_flow_steering *fs = priv->fs; struct mlx5_flow_handle *rule; + struct mlx5e_ptp_fs *ptp_fs; int err; + ptp_fs = mlx5e_fs_get_ptp(fs); if (ptp_fs->valid) return 0; - err = mlx5e_fs_tt_redirect_udp_create(priv); + err = mlx5e_fs_tt_redirect_udp_create(fs); if (err) goto out_free; - rule = mlx5e_fs_tt_redirect_udp_add_rule(priv, MLX5_TT_IPV4_UDP, + rule = mlx5e_fs_tt_redirect_udp_add_rule(fs, MLX5_TT_IPV4_UDP, tirn, PTP_EV_PORT); if (IS_ERR(rule)) { err = PTR_ERR(rule); @@ -660,7 +662,7 @@ static int mlx5e_ptp_rx_set_fs(struct mlx5e_priv *priv) } ptp_fs->udp_v4_rule = rule; - rule = mlx5e_fs_tt_redirect_udp_add_rule(priv, MLX5_TT_IPV6_UDP, + rule = mlx5e_fs_tt_redirect_udp_add_rule(fs, MLX5_TT_IPV6_UDP, tirn, PTP_EV_PORT); if (IS_ERR(rule)) { err = PTR_ERR(rule); @@ -668,11 +670,11 @@ static int mlx5e_ptp_rx_set_fs(struct mlx5e_priv *priv) } ptp_fs->udp_v6_rule = rule; - err = mlx5e_fs_tt_redirect_any_create(priv); + err = mlx5e_fs_tt_redirect_any_create(fs); if (err) goto out_destroy_udp_v6_rule; - rule = mlx5e_fs_tt_redirect_any_add_rule(priv, tirn, ETH_P_1588); + rule = mlx5e_fs_tt_redirect_any_add_rule(fs, tirn, ETH_P_1588); if (IS_ERR(rule)) { err = PTR_ERR(rule); goto out_destroy_fs_any; @@ -683,13 +685,13 @@ static int mlx5e_ptp_rx_set_fs(struct mlx5e_priv *priv) return 0; out_destroy_fs_any: - mlx5e_fs_tt_redirect_any_destroy(priv); + mlx5e_fs_tt_redirect_any_destroy(fs); out_destroy_udp_v6_rule: mlx5e_fs_tt_redirect_del_rule(ptp_fs->udp_v6_rule); out_destroy_udp_v4_rule: mlx5e_fs_tt_redirect_del_rule(ptp_fs->udp_v4_rule); out_destroy_fs_udp: - mlx5e_fs_tt_redirect_udp_destroy(priv); + mlx5e_fs_tt_redirect_udp_destroy(fs); out_free: return err; } @@ -723,7 +725,7 @@ int mlx5e_ptp_open(struct mlx5e_priv *priv, struct mlx5e_params *params, if (err) goto err_free; - netif_napi_add(netdev, &c->napi, mlx5e_ptp_napi_poll, 64); + netif_napi_add(netdev, &c->napi, mlx5e_ptp_napi_poll); mlx5e_ptp_build_params(c, cparams, params); @@ -797,29 +799,31 @@ int mlx5e_ptp_get_rqn(struct mlx5e_ptp *c, u32 *rqn) return 0; } -int mlx5e_ptp_alloc_rx_fs(struct mlx5e_priv *priv) +int mlx5e_ptp_alloc_rx_fs(struct mlx5e_flow_steering *fs, + const struct mlx5e_profile *profile) { struct mlx5e_ptp_fs *ptp_fs; - if (!mlx5e_profile_feature_cap(priv->profile, PTP_RX)) + if (!mlx5e_profile_feature_cap(profile, PTP_RX)) return 0; ptp_fs = kzalloc(sizeof(*ptp_fs), GFP_KERNEL); if (!ptp_fs) return -ENOMEM; + mlx5e_fs_set_ptp(fs, ptp_fs); - priv->fs->ptp_fs = ptp_fs; return 0; } -void mlx5e_ptp_free_rx_fs(struct mlx5e_priv *priv) +void mlx5e_ptp_free_rx_fs(struct mlx5e_flow_steering *fs, + const struct mlx5e_profile *profile) { - struct mlx5e_ptp_fs *ptp_fs = priv->fs->ptp_fs; + struct mlx5e_ptp_fs *ptp_fs = mlx5e_fs_get_ptp(fs); - if (!mlx5e_profile_feature_cap(priv->profile, PTP_RX)) + if (!mlx5e_profile_feature_cap(profile, PTP_RX)) return; - mlx5e_ptp_rx_unset_fs(priv); + mlx5e_ptp_rx_unset_fs(fs); kfree(ptp_fs); } @@ -845,6 +849,6 @@ int mlx5e_ptp_rx_manage_fs(struct mlx5e_priv *priv, bool set) netdev_WARN_ONCE(priv->netdev, "Don't try to remove PTP RX-FS rules"); return -EINVAL; } - mlx5e_ptp_rx_unset_fs(priv); + mlx5e_ptp_rx_unset_fs(priv->fs); return 0; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/ptp.h b/drivers/net/ethernet/mellanox/mlx5/core/en/ptp.h index 92dbbec472ec..5bce554e131a 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/ptp.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/ptp.h @@ -74,8 +74,10 @@ void mlx5e_ptp_close(struct mlx5e_ptp *c); void mlx5e_ptp_activate_channel(struct mlx5e_ptp *c); void mlx5e_ptp_deactivate_channel(struct mlx5e_ptp *c); int mlx5e_ptp_get_rqn(struct mlx5e_ptp *c, u32 *rqn); -int mlx5e_ptp_alloc_rx_fs(struct mlx5e_priv *priv); -void mlx5e_ptp_free_rx_fs(struct mlx5e_priv *priv); +int mlx5e_ptp_alloc_rx_fs(struct mlx5e_flow_steering *fs, + const struct mlx5e_profile *profile); +void mlx5e_ptp_free_rx_fs(struct mlx5e_flow_steering *fs, + const struct mlx5e_profile *profile); int mlx5e_ptp_rx_manage_fs(struct mlx5e_priv *priv, bool set); enum { diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_rx.c b/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_rx.c index fc366e66d0b0..5f6f95ad6888 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_rx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_rx.c @@ -134,38 +134,17 @@ out: return err; } -static int mlx5e_rq_to_ready(struct mlx5e_rq *rq, int curr_state) -{ - struct net_device *dev = rq->netdev; - int err; - - err = mlx5e_modify_rq_state(rq, curr_state, MLX5_RQC_STATE_RST); - if (err) { - netdev_err(dev, "Failed to move rq 0x%x to reset\n", rq->rqn); - return err; - } - err = mlx5e_modify_rq_state(rq, MLX5_RQC_STATE_RST, MLX5_RQC_STATE_RDY); - if (err) { - netdev_err(dev, "Failed to move rq 0x%x to ready\n", rq->rqn); - return err; - } - - return 0; -} - static int mlx5e_rx_reporter_err_rq_cqe_recover(void *ctx) { struct mlx5e_rq *rq = ctx; int err; mlx5e_deactivate_rq(rq); - mlx5e_free_rx_descs(rq); - - err = mlx5e_rq_to_ready(rq, MLX5_RQC_STATE_ERR); + err = mlx5e_flush_rq(rq, MLX5_RQC_STATE_ERR); + clear_bit(MLX5E_RQ_STATE_RECOVERING, &rq->state); if (err) - goto out; + return err; - clear_bit(MLX5E_RQ_STATE_RECOVERING, &rq->state); mlx5e_activate_rq(rq); rq->stats->recover++; if (rq->channel) @@ -173,9 +152,6 @@ static int mlx5e_rx_reporter_err_rq_cqe_recover(void *ctx) else mlx5e_trigger_napi_sched(rq->cq.napi); return 0; -out: - clear_bit(MLX5E_RQ_STATE_RECOVERING, &rq->state); - return err; } static int mlx5e_rx_reporter_timeout_recover(void *ctx) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/rx_res.c b/drivers/net/ethernet/mellanox/mlx5/core/en/rx_res.c index 24c32f73040a..e1095bc36543 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/rx_res.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/rx_res.c @@ -24,8 +24,6 @@ struct mlx5e_rx_res { struct { struct mlx5e_rqt direct_rqt; struct mlx5e_tir direct_tir; - struct mlx5e_rqt xsk_rqt; - struct mlx5e_tir xsk_tir; } *channels; struct { @@ -320,48 +318,8 @@ static int mlx5e_rx_res_channels_init(struct mlx5e_rx_res *res) mlx5e_tir_builder_clear(builder); } - if (!(res->features & MLX5E_RX_RES_FEATURE_XSK)) - goto out; - - for (ix = 0; ix < res->max_nch; ix++) { - err = mlx5e_rqt_init_direct(&res->channels[ix].xsk_rqt, - res->mdev, false, res->drop_rqn); - if (err) { - mlx5_core_warn(res->mdev, "Failed to create an XSK RQT: err = %d, ix = %u\n", - err, ix); - goto err_destroy_xsk_rqts; - } - } - - for (ix = 0; ix < res->max_nch; ix++) { - mlx5e_tir_builder_build_rqt(builder, res->mdev->mlx5e_res.hw_objs.td.tdn, - mlx5e_rqt_get_rqtn(&res->channels[ix].xsk_rqt), - inner_ft_support); - mlx5e_tir_builder_build_packet_merge(builder, &res->pkt_merge_param); - mlx5e_tir_builder_build_direct(builder); - - err = mlx5e_tir_init(&res->channels[ix].xsk_tir, builder, res->mdev, true); - if (err) { - mlx5_core_warn(res->mdev, "Failed to create an XSK TIR: err = %d, ix = %u\n", - err, ix); - goto err_destroy_xsk_tirs; - } - - mlx5e_tir_builder_clear(builder); - } - goto out; -err_destroy_xsk_tirs: - while (--ix >= 0) - mlx5e_tir_destroy(&res->channels[ix].xsk_tir); - - ix = res->max_nch; -err_destroy_xsk_rqts: - while (--ix >= 0) - mlx5e_rqt_destroy(&res->channels[ix].xsk_rqt); - - ix = res->max_nch; err_destroy_direct_tirs: while (--ix >= 0) mlx5e_tir_destroy(&res->channels[ix].direct_tir); @@ -420,12 +378,6 @@ static void mlx5e_rx_res_channels_destroy(struct mlx5e_rx_res *res) for (ix = 0; ix < res->max_nch; ix++) { mlx5e_tir_destroy(&res->channels[ix].direct_tir); mlx5e_rqt_destroy(&res->channels[ix].direct_rqt); - - if (!(res->features & MLX5E_RX_RES_FEATURE_XSK)) - continue; - - mlx5e_tir_destroy(&res->channels[ix].xsk_tir); - mlx5e_rqt_destroy(&res->channels[ix].xsk_rqt); } kvfree(res->channels); @@ -491,13 +443,6 @@ u32 mlx5e_rx_res_get_tirn_direct(struct mlx5e_rx_res *res, unsigned int ix) return mlx5e_tir_get_tirn(&res->channels[ix].direct_tir); } -u32 mlx5e_rx_res_get_tirn_xsk(struct mlx5e_rx_res *res, unsigned int ix) -{ - WARN_ON(!(res->features & MLX5E_RX_RES_FEATURE_XSK)); - - return mlx5e_tir_get_tirn(&res->channels[ix].xsk_tir); -} - u32 mlx5e_rx_res_get_tirn_rss(struct mlx5e_rx_res *res, enum mlx5_traffic_types tt) { struct mlx5e_rss *rss = res->rss[0]; @@ -523,56 +468,53 @@ static u32 mlx5e_rx_res_get_rqtn_direct(struct mlx5e_rx_res *res, unsigned int i return mlx5e_rqt_get_rqtn(&res->channels[ix].direct_rqt); } -void mlx5e_rx_res_channels_activate(struct mlx5e_rx_res *res, struct mlx5e_channels *chs) +static void mlx5e_rx_res_channel_activate_direct(struct mlx5e_rx_res *res, + struct mlx5e_channels *chs, + unsigned int ix) { - unsigned int nch, ix; + u32 rqn = res->rss_rqns[ix]; int err; - nch = mlx5e_channels_get_num(chs); - - for (ix = 0; ix < chs->num; ix++) - mlx5e_channels_get_regular_rqn(chs, ix, &res->rss_rqns[ix]); - res->rss_nch = chs->num; + err = mlx5e_rqt_redirect_direct(&res->channels[ix].direct_rqt, rqn); + if (err) + mlx5_core_warn(res->mdev, "Failed to redirect direct RQT %#x to RQ %#x (channel %u): err = %d\n", + mlx5e_rqt_get_rqtn(&res->channels[ix].direct_rqt), + rqn, ix, err); +} - mlx5e_rx_res_rss_enable(res); +static void mlx5e_rx_res_channel_deactivate_direct(struct mlx5e_rx_res *res, + unsigned int ix) +{ + int err; - for (ix = 0; ix < nch; ix++) { - u32 rqn; + err = mlx5e_rqt_redirect_direct(&res->channels[ix].direct_rqt, res->drop_rqn); + if (err) + mlx5_core_warn(res->mdev, "Failed to redirect direct RQT %#x to drop RQ %#x (channel %u): err = %d\n", + mlx5e_rqt_get_rqtn(&res->channels[ix].direct_rqt), + res->drop_rqn, ix, err); +} - mlx5e_channels_get_regular_rqn(chs, ix, &rqn); - err = mlx5e_rqt_redirect_direct(&res->channels[ix].direct_rqt, rqn); - if (err) - mlx5_core_warn(res->mdev, "Failed to redirect direct RQT %#x to RQ %#x (channel %u): err = %d\n", - mlx5e_rqt_get_rqtn(&res->channels[ix].direct_rqt), - rqn, ix, err); +void mlx5e_rx_res_channels_activate(struct mlx5e_rx_res *res, struct mlx5e_channels *chs) +{ + unsigned int nch, ix; + int err; - if (!(res->features & MLX5E_RX_RES_FEATURE_XSK)) - continue; + nch = mlx5e_channels_get_num(chs); - if (!mlx5e_channels_get_xsk_rqn(chs, ix, &rqn)) - rqn = res->drop_rqn; - err = mlx5e_rqt_redirect_direct(&res->channels[ix].xsk_rqt, rqn); - if (err) - mlx5_core_warn(res->mdev, "Failed to redirect XSK RQT %#x to RQ %#x (channel %u): err = %d\n", - mlx5e_rqt_get_rqtn(&res->channels[ix].xsk_rqt), - rqn, ix, err); + for (ix = 0; ix < chs->num; ix++) { + if (mlx5e_channels_is_xsk(chs, ix)) + mlx5e_channels_get_xsk_rqn(chs, ix, &res->rss_rqns[ix]); + else + mlx5e_channels_get_regular_rqn(chs, ix, &res->rss_rqns[ix]); } - for (ix = nch; ix < res->max_nch; ix++) { - err = mlx5e_rqt_redirect_direct(&res->channels[ix].direct_rqt, res->drop_rqn); - if (err) - mlx5_core_warn(res->mdev, "Failed to redirect direct RQT %#x to drop RQ %#x (channel %u): err = %d\n", - mlx5e_rqt_get_rqtn(&res->channels[ix].direct_rqt), - res->drop_rqn, ix, err); + res->rss_nch = chs->num; - if (!(res->features & MLX5E_RX_RES_FEATURE_XSK)) - continue; + mlx5e_rx_res_rss_enable(res); - err = mlx5e_rqt_redirect_direct(&res->channels[ix].xsk_rqt, res->drop_rqn); - if (err) - mlx5_core_warn(res->mdev, "Failed to redirect XSK RQT %#x to drop RQ %#x (channel %u): err = %d\n", - mlx5e_rqt_get_rqtn(&res->channels[ix].xsk_rqt), - res->drop_rqn, ix, err); - } + for (ix = 0; ix < nch; ix++) + mlx5e_rx_res_channel_activate_direct(res, chs, ix); + for (ix = nch; ix < res->max_nch; ix++) + mlx5e_rx_res_channel_deactivate_direct(res, ix); if (res->features & MLX5E_RX_RES_FEATURE_PTP) { u32 rqn; @@ -595,22 +537,8 @@ void mlx5e_rx_res_channels_deactivate(struct mlx5e_rx_res *res) mlx5e_rx_res_rss_disable(res); - for (ix = 0; ix < res->max_nch; ix++) { - err = mlx5e_rqt_redirect_direct(&res->channels[ix].direct_rqt, res->drop_rqn); - if (err) - mlx5_core_warn(res->mdev, "Failed to redirect direct RQT %#x to drop RQ %#x (channel %u): err = %d\n", - mlx5e_rqt_get_rqtn(&res->channels[ix].direct_rqt), - res->drop_rqn, ix, err); - - if (!(res->features & MLX5E_RX_RES_FEATURE_XSK)) - continue; - - err = mlx5e_rqt_redirect_direct(&res->channels[ix].xsk_rqt, res->drop_rqn); - if (err) - mlx5_core_warn(res->mdev, "Failed to redirect XSK RQT %#x to drop RQ %#x (channel %u): err = %d\n", - mlx5e_rqt_get_rqtn(&res->channels[ix].xsk_rqt), - res->drop_rqn, ix, err); - } + for (ix = 0; ix < res->max_nch; ix++) + mlx5e_rx_res_channel_deactivate_direct(res, ix); if (res->features & MLX5E_RX_RES_FEATURE_PTP) { err = mlx5e_rqt_redirect_direct(&res->ptp.rqt, res->drop_rqn); @@ -621,33 +549,17 @@ void mlx5e_rx_res_channels_deactivate(struct mlx5e_rx_res *res) } } -int mlx5e_rx_res_xsk_activate(struct mlx5e_rx_res *res, struct mlx5e_channels *chs, - unsigned int ix) +void mlx5e_rx_res_xsk_update(struct mlx5e_rx_res *res, struct mlx5e_channels *chs, + unsigned int ix, bool xsk) { - u32 rqn; - int err; - - if (!mlx5e_channels_get_xsk_rqn(chs, ix, &rqn)) - return -EINVAL; - - err = mlx5e_rqt_redirect_direct(&res->channels[ix].xsk_rqt, rqn); - if (err) - mlx5_core_warn(res->mdev, "Failed to redirect XSK RQT %#x to XSK RQ %#x (channel %u): err = %d\n", - mlx5e_rqt_get_rqtn(&res->channels[ix].xsk_rqt), - rqn, ix, err); - return err; -} + if (xsk) + mlx5e_channels_get_xsk_rqn(chs, ix, &res->rss_rqns[ix]); + else + mlx5e_channels_get_regular_rqn(chs, ix, &res->rss_rqns[ix]); -int mlx5e_rx_res_xsk_deactivate(struct mlx5e_rx_res *res, unsigned int ix) -{ - int err; + mlx5e_rx_res_rss_enable(res); - err = mlx5e_rqt_redirect_direct(&res->channels[ix].xsk_rqt, res->drop_rqn); - if (err) - mlx5_core_warn(res->mdev, "Failed to redirect XSK RQT %#x to drop RQ %#x (channel %u): err = %d\n", - mlx5e_rqt_get_rqtn(&res->channels[ix].xsk_rqt), - res->drop_rqn, ix, err); - return err; + mlx5e_rx_res_channel_activate_direct(res, chs, ix); } int mlx5e_rx_res_packet_merge_set_param(struct mlx5e_rx_res *res, diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/rx_res.h b/drivers/net/ethernet/mellanox/mlx5/core/en/rx_res.h index b39b20a720e0..5d5f64fab60f 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/rx_res.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/rx_res.h @@ -17,8 +17,7 @@ struct mlx5e_rss_params_hash; enum mlx5e_rx_res_features { MLX5E_RX_RES_FEATURE_INNER_FT = BIT(0), - MLX5E_RX_RES_FEATURE_XSK = BIT(1), - MLX5E_RX_RES_FEATURE_PTP = BIT(2), + MLX5E_RX_RES_FEATURE_PTP = BIT(1), }; /* Setup */ @@ -32,7 +31,6 @@ void mlx5e_rx_res_free(struct mlx5e_rx_res *res); /* TIRN getters for flow steering */ u32 mlx5e_rx_res_get_tirn_direct(struct mlx5e_rx_res *res, unsigned int ix); -u32 mlx5e_rx_res_get_tirn_xsk(struct mlx5e_rx_res *res, unsigned int ix); u32 mlx5e_rx_res_get_tirn_rss(struct mlx5e_rx_res *res, enum mlx5_traffic_types tt); u32 mlx5e_rx_res_get_tirn_rss_inner(struct mlx5e_rx_res *res, enum mlx5_traffic_types tt); u32 mlx5e_rx_res_get_tirn_ptp(struct mlx5e_rx_res *res); @@ -40,9 +38,8 @@ u32 mlx5e_rx_res_get_tirn_ptp(struct mlx5e_rx_res *res); /* Activate/deactivate API */ void mlx5e_rx_res_channels_activate(struct mlx5e_rx_res *res, struct mlx5e_channels *chs); void mlx5e_rx_res_channels_deactivate(struct mlx5e_rx_res *res); -int mlx5e_rx_res_xsk_activate(struct mlx5e_rx_res *res, struct mlx5e_channels *chs, - unsigned int ix); -int mlx5e_rx_res_xsk_deactivate(struct mlx5e_rx_res *res, unsigned int ix); +void mlx5e_rx_res_xsk_update(struct mlx5e_rx_res *res, struct mlx5e_channels *chs, + unsigned int ix, bool xsk); /* Configuration API */ void mlx5e_rx_res_rss_set_indir_uniform(struct mlx5e_rx_res *res, unsigned int nch); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/goto.c b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/goto.c index 69949ab830b6..25174f68613e 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/goto.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/goto.c @@ -12,6 +12,7 @@ validate_goto_chain(struct mlx5e_priv *priv, const struct flow_action_entry *act, struct netlink_ext_ack *extack) { + struct mlx5e_tc_table *tc = mlx5e_fs_get_tc(priv->fs); bool is_esw = mlx5e_is_eswitch_flow(flow); bool ft_flow = mlx5e_is_ft_flow(flow); u32 dest_chain = act->chain_index; @@ -21,7 +22,7 @@ validate_goto_chain(struct mlx5e_priv *priv, u32 max_chain; esw = priv->mdev->priv.eswitch; - chains = is_esw ? esw_chains(esw) : mlx5e_nic_chains(priv->fs->tc); + chains = is_esw ? esw_chains(esw) : mlx5e_nic_chains(tc); max_chain = mlx5_chains_get_chain_range(chains); reformat_and_fwd = is_esw ? MLX5_CAP_ESW_FLOWTABLE_FDB(priv->mdev, reformat_and_fwd_to_table) : diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/trap.c b/drivers/net/ethernet/mellanox/mlx5/core/en/trap.c index 11f2a7fb72a9..201ac7dd338f 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/trap.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/trap.c @@ -147,7 +147,7 @@ static struct mlx5e_trap *mlx5e_open_trap(struct mlx5e_priv *priv) t->mkey_be = cpu_to_be32(priv->mdev->mlx5e_res.hw_objs.mkey); t->stats = &priv->trap_stats.ch; - netif_napi_add(netdev, &t->napi, mlx5e_trap_napi_poll, 64); + netif_napi_add(netdev, &t->napi, mlx5e_trap_napi_poll); err = mlx5e_open_trap_rq(priv, t); if (unlikely(err)) @@ -230,12 +230,12 @@ static int mlx5e_handle_action_trap(struct mlx5e_priv *priv, int trap_id) switch (trap_id) { case DEVLINK_TRAP_GENERIC_ID_INGRESS_VLAN_FILTER: - err = mlx5e_add_vlan_trap(priv, trap_id, mlx5e_trap_get_tirn(priv->en_trap)); + err = mlx5e_add_vlan_trap(priv->fs, trap_id, mlx5e_trap_get_tirn(priv->en_trap)); if (err) goto err_out; break; case DEVLINK_TRAP_GENERIC_ID_DMAC_FILTER: - err = mlx5e_add_mac_trap(priv, trap_id, mlx5e_trap_get_tirn(priv->en_trap)); + err = mlx5e_add_mac_trap(priv->fs, trap_id, mlx5e_trap_get_tirn(priv->en_trap)); if (err) goto err_out; break; @@ -256,10 +256,10 @@ static int mlx5e_handle_action_drop(struct mlx5e_priv *priv, int trap_id) { switch (trap_id) { case DEVLINK_TRAP_GENERIC_ID_INGRESS_VLAN_FILTER: - mlx5e_remove_vlan_trap(priv); + mlx5e_remove_vlan_trap(priv->fs); break; case DEVLINK_TRAP_GENERIC_ID_DMAC_FILTER: - mlx5e_remove_mac_trap(priv); + mlx5e_remove_mac_trap(priv->fs); break; default: netdev_warn(priv->netdev, "%s: Unknown trap id %d\n", __func__, trap_id); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/txrx.h b/drivers/net/ethernet/mellanox/mlx5/core/en/txrx.h index c208ea307bff..4456ad5cedf1 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/txrx.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/txrx.h @@ -439,16 +439,24 @@ static inline u16 mlx5e_stop_room_for_max_wqe(struct mlx5_core_dev *mdev) return MLX5E_STOP_ROOM(mlx5e_get_max_sq_wqebbs(mdev)); } -static inline bool mlx5e_icosq_can_post_wqe(struct mlx5e_icosq *sq, u16 wqe_size) +static inline u16 mlx5e_stop_room_for_mpwqe(struct mlx5_core_dev *mdev) { - u16 room = sq->reserved_room; + u8 mpwqe_wqebbs = mlx5e_get_max_sq_aligned_wqebbs(mdev); - WARN_ONCE(wqe_size > sq->max_sq_wqebbs, - "wqe_size %u is greater than max SQ WQEBBs %u", - wqe_size, sq->max_sq_wqebbs); + return mlx5e_stop_room_for_wqe(mdev, mpwqe_wqebbs); +} - room += MLX5E_STOP_ROOM(wqe_size); +static inline bool mlx5e_icosq_can_post_wqe(struct mlx5e_icosq *sq, u16 wqe_size) +{ + u16 room = sq->reserved_room + MLX5E_STOP_ROOM(wqe_size); return mlx5e_wqc_has_room_for(&sq->wq, sq->cc, sq->pc, room); } + +static inline struct mlx5e_mpw_info *mlx5e_get_mpw_info(struct mlx5e_rq *rq, int i) +{ + size_t isz = struct_size(rq->mpwqe.info, alloc_units, rq->mpwqe.pages_per_wqe); + + return (struct mlx5e_mpw_info *)((char *)rq->mpwqe.info + array_size(i, isz)); +} #endif diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/xdp.c b/drivers/net/ethernet/mellanox/mlx5/core/en/xdp.c index 8f321a6c0809..4685c652c97e 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/xdp.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/xdp.c @@ -333,7 +333,7 @@ mlx5e_xmit_xdp_frame_mpwqe(struct mlx5e_xdpsq *sq, struct mlx5e_xmit_data *xdptx mlx5e_xdp_mpwqe_add_dseg(sq, xdptxd, stats); - if (unlikely(mlx5e_xdp_mpqwe_is_full(session, sq->max_sq_mpw_wqebbs))) + if (unlikely(mlx5e_xdp_mpwqe_is_full(session, sq->max_sq_mpw_wqebbs))) mlx5e_xdp_mpwqe_complete(sq); stats->xmit++; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/xdp.h b/drivers/net/ethernet/mellanox/mlx5/core/en/xdp.h index 287e17911251..bc2d9034af5b 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/xdp.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/xdp.h @@ -122,7 +122,7 @@ static inline bool mlx5e_xdp_get_inline_state(struct mlx5e_xdpsq *sq, bool cur) return cur; } -static inline bool mlx5e_xdp_mpqwe_is_full(struct mlx5e_tx_mpwqe *session, u8 max_sq_mpw_wqebbs) +static inline bool mlx5e_xdp_mpwqe_is_full(struct mlx5e_tx_mpwqe *session, u8 max_sq_mpw_wqebbs) { if (session->inline_on) return session->ds_count + MLX5E_XDP_INLINE_WQE_MAX_DS_CNT > diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/xsk/pool.c b/drivers/net/ethernet/mellanox/mlx5/core/en/xsk/pool.c index 2c520394aa1d..ebada0c5af3c 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/xsk/pool.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/xsk/pool.c @@ -72,6 +72,7 @@ void mlx5e_build_xsk_param(struct xsk_buff_pool *pool, struct mlx5e_xsk_param *x { xsk->headroom = xsk_pool_get_headroom(pool); xsk->chunk_size = xsk_pool_get_chunk_size(pool); + xsk->unaligned = pool->unaligned; } static int mlx5e_xsk_enable_locked(struct mlx5e_priv *priv, @@ -98,6 +99,15 @@ static int mlx5e_xsk_enable_locked(struct mlx5e_priv *priv, mlx5e_build_xsk_param(pool, &xsk); + if (priv->channels.params.rq_wq_type == MLX5_WQ_TYPE_LINKED_LIST_STRIDING_RQ && + mlx5e_mpwrq_umr_mode(priv->mdev, &xsk) == MLX5E_MPWRQ_UMR_MODE_OVERSIZED) { + const char *recommendation = is_power_of_2(xsk.chunk_size) ? + "Upgrade firmware" : "Disable striding RQ"; + + mlx5_core_warn(priv->mdev, "Expected slowdown with XSK frame size %u. %s for better performance.\n", + xsk.chunk_size, recommendation); + } + if (!test_bit(MLX5E_STATE_OPENED, &priv->state)) { /* XSK objects will be created on open. */ goto validate_closed; @@ -123,15 +133,12 @@ static int mlx5e_xsk_enable_locked(struct mlx5e_priv *priv, * any Fill Ring entries at the setup stage. */ - err = mlx5e_rx_res_xsk_activate(priv->rx_res, &priv->channels, ix); - if (unlikely(err)) - goto err_deactivate; + mlx5e_rx_res_xsk_update(priv->rx_res, &priv->channels, ix, true); - return 0; + mlx5e_deactivate_rq(&c->rq); + mlx5e_flush_rq(&c->rq, MLX5_RQC_STATE_RDY); -err_deactivate: - mlx5e_deactivate_xsk(c); - mlx5e_close_xsk(c); + return 0; err_remove_pool: mlx5e_xsk_remove_pool(&priv->xsk, ix); @@ -170,7 +177,13 @@ static int mlx5e_xsk_disable_locked(struct mlx5e_priv *priv, u16 ix) goto remove_pool; c = priv->channels.c[ix]; - mlx5e_rx_res_xsk_deactivate(priv->rx_res, ix); + + mlx5e_activate_rq(&c->rq); + mlx5e_trigger_napi_icosq(c); + mlx5e_wait_for_min_rx_wqes(&c->rq, MLX5E_RQ_WQES_TIMEOUT); + + mlx5e_rx_res_xsk_update(priv->rx_res, &priv->channels, ix, false); + mlx5e_deactivate_xsk(c); mlx5e_close_xsk(c); @@ -208,11 +221,10 @@ int mlx5e_xsk_setup_pool(struct net_device *dev, struct xsk_buff_pool *pool, u16 { struct mlx5e_priv *priv = netdev_priv(dev); struct mlx5e_params *params = &priv->channels.params; - u16 ix; - if (unlikely(!mlx5e_qid_get_ch_if_in_group(params, qid, MLX5E_RQ_GROUP_XSK, &ix))) + if (unlikely(qid >= params->num_channels)) return -EINVAL; - return pool ? mlx5e_xsk_enable_pool(priv, pool, ix) : - mlx5e_xsk_disable_pool(priv, ix); + return pool ? mlx5e_xsk_enable_pool(priv, pool, qid) : + mlx5e_xsk_disable_pool(priv, qid); } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/xsk/rx.c b/drivers/net/ethernet/mellanox/mlx5/core/en/xsk/rx.c index 9a1553598a7c..c91b54d9ff27 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/xsk/rx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/xsk/rx.c @@ -8,18 +8,221 @@ /* RX data path */ -static struct sk_buff *mlx5e_xsk_construct_skb(struct mlx5e_rq *rq, void *data, - u32 cqe_bcnt) +int mlx5e_xsk_alloc_rx_mpwqe(struct mlx5e_rq *rq, u16 ix) { + struct mlx5e_mpw_info *wi = mlx5e_get_mpw_info(rq, ix); + struct mlx5e_icosq *icosq = rq->icosq; + struct mlx5_wq_cyc *wq = &icosq->wq; + struct mlx5e_umr_wqe *umr_wqe; + int batch, i; + u32 offset; /* 17-bit value with MTT. */ + u16 pi; + + if (unlikely(!xsk_buff_can_alloc(rq->xsk_pool, rq->mpwqe.pages_per_wqe))) + goto err; + + BUILD_BUG_ON(sizeof(wi->alloc_units[0]) != sizeof(wi->alloc_units[0].xsk)); + batch = xsk_buff_alloc_batch(rq->xsk_pool, (struct xdp_buff **)wi->alloc_units, + rq->mpwqe.pages_per_wqe); + + /* If batch < pages_per_wqe, either: + * 1. Some (or all) descriptors were invalid. + * 2. dma_need_sync is true, and it fell back to allocating one frame. + * In either case, try to continue allocating frames one by one, until + * the first error, which will mean there are no more valid descriptors. + */ + for (; batch < rq->mpwqe.pages_per_wqe; batch++) { + wi->alloc_units[batch].xsk = xsk_buff_alloc(rq->xsk_pool); + if (unlikely(!wi->alloc_units[batch].xsk)) + goto err_reuse_batch; + } + + pi = mlx5e_icosq_get_next_pi(icosq, rq->mpwqe.umr_wqebbs); + umr_wqe = mlx5_wq_cyc_get_wqe(wq, pi); + memcpy(umr_wqe, &rq->mpwqe.umr_wqe, sizeof(struct mlx5e_umr_wqe)); + + if (likely(rq->mpwqe.umr_mode == MLX5E_MPWRQ_UMR_MODE_ALIGNED)) { + for (i = 0; i < batch; i++) { + dma_addr_t addr = xsk_buff_xdp_get_frame_dma(wi->alloc_units[i].xsk); + + umr_wqe->inline_mtts[i] = (struct mlx5_mtt) { + .ptag = cpu_to_be64(addr | MLX5_EN_WR), + }; + } + } else if (unlikely(rq->mpwqe.umr_mode == MLX5E_MPWRQ_UMR_MODE_UNALIGNED)) { + for (i = 0; i < batch; i++) { + dma_addr_t addr = xsk_buff_xdp_get_frame_dma(wi->alloc_units[i].xsk); + + umr_wqe->inline_ksms[i] = (struct mlx5_ksm) { + .key = rq->mkey_be, + .va = cpu_to_be64(addr), + }; + } + } else if (likely(rq->mpwqe.umr_mode == MLX5E_MPWRQ_UMR_MODE_TRIPLE)) { + u32 mapping_size = 1 << (rq->mpwqe.page_shift - 2); + + for (i = 0; i < batch; i++) { + dma_addr_t addr = xsk_buff_xdp_get_frame_dma(wi->alloc_units[i].xsk); + + umr_wqe->inline_ksms[i << 2] = (struct mlx5_ksm) { + .key = rq->mkey_be, + .va = cpu_to_be64(addr), + }; + umr_wqe->inline_ksms[(i << 2) + 1] = (struct mlx5_ksm) { + .key = rq->mkey_be, + .va = cpu_to_be64(addr + mapping_size), + }; + umr_wqe->inline_ksms[(i << 2) + 2] = (struct mlx5_ksm) { + .key = rq->mkey_be, + .va = cpu_to_be64(addr + mapping_size * 2), + }; + umr_wqe->inline_ksms[(i << 2) + 3] = (struct mlx5_ksm) { + .key = rq->mkey_be, + .va = cpu_to_be64(rq->wqe_overflow.addr), + }; + } + } else { + __be32 pad_size = cpu_to_be32((1 << rq->mpwqe.page_shift) - + rq->xsk_pool->chunk_size); + __be32 frame_size = cpu_to_be32(rq->xsk_pool->chunk_size); + + for (i = 0; i < batch; i++) { + dma_addr_t addr = xsk_buff_xdp_get_frame_dma(wi->alloc_units[i].xsk); + + umr_wqe->inline_klms[i << 1] = (struct mlx5_klm) { + .key = rq->mkey_be, + .va = cpu_to_be64(addr), + .bcount = frame_size, + }; + umr_wqe->inline_klms[(i << 1) + 1] = (struct mlx5_klm) { + .key = rq->mkey_be, + .va = cpu_to_be64(rq->wqe_overflow.addr), + .bcount = pad_size, + }; + } + } + + bitmap_zero(wi->xdp_xmit_bitmap, rq->mpwqe.pages_per_wqe); + wi->consumed_strides = 0; + + umr_wqe->ctrl.opmod_idx_opcode = + cpu_to_be32((icosq->pc << MLX5_WQE_CTRL_WQE_INDEX_SHIFT) | MLX5_OPCODE_UMR); + + /* Optimized for speed: keep in sync with mlx5e_mpwrq_umr_entry_size. */ + offset = ix * rq->mpwqe.mtts_per_wqe; + if (likely(rq->mpwqe.umr_mode == MLX5E_MPWRQ_UMR_MODE_ALIGNED)) + offset = offset * sizeof(struct mlx5_mtt) / MLX5_OCTWORD; + else if (unlikely(rq->mpwqe.umr_mode == MLX5E_MPWRQ_UMR_MODE_OVERSIZED)) + offset = offset * sizeof(struct mlx5_klm) * 2 / MLX5_OCTWORD; + else if (unlikely(rq->mpwqe.umr_mode == MLX5E_MPWRQ_UMR_MODE_TRIPLE)) + offset = offset * sizeof(struct mlx5_ksm) * 4 / MLX5_OCTWORD; + umr_wqe->uctrl.xlt_offset = cpu_to_be16(offset); + + icosq->db.wqe_info[pi] = (struct mlx5e_icosq_wqe_info) { + .wqe_type = MLX5E_ICOSQ_WQE_UMR_RX, + .num_wqebbs = rq->mpwqe.umr_wqebbs, + .umr.rq = rq, + }; + + icosq->pc += rq->mpwqe.umr_wqebbs; + + icosq->doorbell_cseg = &umr_wqe->ctrl; + + return 0; + +err_reuse_batch: + while (--batch >= 0) + xsk_buff_free(wi->alloc_units[batch].xsk); + +err: + rq->stats->buff_alloc_err++; + return -ENOMEM; +} + +int mlx5e_xsk_alloc_rx_wqes_batched(struct mlx5e_rq *rq, u16 ix, int wqe_bulk) +{ + struct mlx5_wq_cyc *wq = &rq->wqe.wq; + struct xdp_buff **buffs; + u32 contig, alloc; + int i; + + /* mlx5e_init_frags_partition creates a 1:1 mapping between + * rq->wqe.frags and rq->wqe.alloc_units, which allows us to + * allocate XDP buffers straight into alloc_units. + */ + BUILD_BUG_ON(sizeof(rq->wqe.alloc_units[0]) != + sizeof(rq->wqe.alloc_units[0].xsk)); + buffs = (struct xdp_buff **)rq->wqe.alloc_units; + contig = mlx5_wq_cyc_get_size(wq) - ix; + if (wqe_bulk <= contig) { + alloc = xsk_buff_alloc_batch(rq->xsk_pool, buffs + ix, wqe_bulk); + } else { + alloc = xsk_buff_alloc_batch(rq->xsk_pool, buffs + ix, contig); + if (likely(alloc == contig)) + alloc += xsk_buff_alloc_batch(rq->xsk_pool, buffs, wqe_bulk - contig); + } + + for (i = 0; i < alloc; i++) { + int j = mlx5_wq_cyc_ctr2ix(wq, ix + i); + struct mlx5e_wqe_frag_info *frag; + struct mlx5e_rx_wqe_cyc *wqe; + dma_addr_t addr; + + wqe = mlx5_wq_cyc_get_wqe(wq, j); + /* Assumes log_num_frags == 0. */ + frag = &rq->wqe.frags[j]; + + addr = xsk_buff_xdp_get_frame_dma(frag->au->xsk); + wqe->data[0].addr = cpu_to_be64(addr + rq->buff.headroom); + } + + return alloc; +} + +int mlx5e_xsk_alloc_rx_wqes(struct mlx5e_rq *rq, u16 ix, int wqe_bulk) +{ + struct mlx5_wq_cyc *wq = &rq->wqe.wq; + int i; + + for (i = 0; i < wqe_bulk; i++) { + int j = mlx5_wq_cyc_ctr2ix(wq, ix + i); + struct mlx5e_wqe_frag_info *frag; + struct mlx5e_rx_wqe_cyc *wqe; + dma_addr_t addr; + + wqe = mlx5_wq_cyc_get_wqe(wq, j); + /* Assumes log_num_frags == 0. */ + frag = &rq->wqe.frags[j]; + + frag->au->xsk = xsk_buff_alloc(rq->xsk_pool); + if (unlikely(!frag->au->xsk)) + return i; + + addr = xsk_buff_xdp_get_frame_dma(frag->au->xsk); + wqe->data[0].addr = cpu_to_be64(addr + rq->buff.headroom); + } + + return wqe_bulk; +} + +static struct sk_buff *mlx5e_xsk_construct_skb(struct mlx5e_rq *rq, struct xdp_buff *xdp) +{ + u32 totallen = xdp->data_end - xdp->data_meta; + u32 metalen = xdp->data - xdp->data_meta; struct sk_buff *skb; - skb = napi_alloc_skb(rq->cq.napi, cqe_bcnt); + skb = napi_alloc_skb(rq->cq.napi, totallen); if (unlikely(!skb)) { rq->stats->buff_alloc_err++; return NULL; } - skb_put_data(skb, data, cqe_bcnt); + skb_put_data(skb, xdp->data_meta, totallen); + + if (metalen) { + skb_metadata_set(skb, metalen); + __skb_pull(skb, metalen); + } return skb; } @@ -30,7 +233,7 @@ struct sk_buff *mlx5e_xsk_skb_from_cqe_mpwrq_linear(struct mlx5e_rq *rq, u32 head_offset, u32 page_idx) { - struct xdp_buff *xdp = wi->umr.dma_info[page_idx].xsk; + struct xdp_buff *xdp = wi->alloc_units[page_idx].xsk; struct bpf_prog *prog; /* Check packet size. Note LRO doesn't use linear SKB */ @@ -46,8 +249,7 @@ struct sk_buff *mlx5e_xsk_skb_from_cqe_mpwrq_linear(struct mlx5e_rq *rq, */ WARN_ON_ONCE(head_offset); - xdp->data_end = xdp->data + cqe_bcnt; - xdp_set_data_meta_invalid(xdp); + xsk_buff_set_size(xdp, cqe_bcnt); xsk_buff_dma_sync_for_cpu(xdp, rq->xsk_pool); net_prefetch(xdp->data); @@ -76,14 +278,14 @@ struct sk_buff *mlx5e_xsk_skb_from_cqe_mpwrq_linear(struct mlx5e_rq *rq, /* XDP_PASS: copy the data from the UMEM to a new SKB and reuse the * frame. On SKB allocation failure, NULL is returned. */ - return mlx5e_xsk_construct_skb(rq, xdp->data, xdp->data_end - xdp->data); + return mlx5e_xsk_construct_skb(rq, xdp); } struct sk_buff *mlx5e_xsk_skb_from_cqe_linear(struct mlx5e_rq *rq, struct mlx5e_wqe_frag_info *wi, u32 cqe_bcnt) { - struct xdp_buff *xdp = wi->di->xsk; + struct xdp_buff *xdp = wi->au->xsk; struct bpf_prog *prog; /* wi->offset is not used in this function, because xdp->data and the @@ -93,8 +295,7 @@ struct sk_buff *mlx5e_xsk_skb_from_cqe_linear(struct mlx5e_rq *rq, */ WARN_ON_ONCE(wi->offset); - xdp->data_end = xdp->data + cqe_bcnt; - xdp_set_data_meta_invalid(xdp); + xsk_buff_set_size(xdp, cqe_bcnt); xsk_buff_dma_sync_for_cpu(xdp, rq->xsk_pool); net_prefetch(xdp->data); @@ -103,8 +304,8 @@ struct sk_buff *mlx5e_xsk_skb_from_cqe_linear(struct mlx5e_rq *rq, return NULL; /* page/packet was consumed by XDP */ /* XDP_PASS: copy the data from the UMEM to a new SKB. The frame reuse - * will be handled by mlx5e_put_rx_frag. + * will be handled by mlx5e_free_rx_wqe. * On SKB allocation failure, NULL is returned. */ - return mlx5e_xsk_construct_skb(rq, xdp->data, xdp->data_end - xdp->data); + return mlx5e_xsk_construct_skb(rq, xdp); } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/xsk/rx.h b/drivers/net/ethernet/mellanox/mlx5/core/en/xsk/rx.h index cc18d97d8ee0..087c943bd8e9 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/xsk/rx.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/xsk/rx.h @@ -5,12 +5,12 @@ #define __MLX5_EN_XSK_RX_H__ #include "en.h" -#include <net/xdp_sock_drv.h> - -#define MLX5E_MTT_PTAG_MASK 0xfffffffffffffff8ULL /* RX data path */ +int mlx5e_xsk_alloc_rx_mpwqe(struct mlx5e_rq *rq, u16 ix); +int mlx5e_xsk_alloc_rx_wqes_batched(struct mlx5e_rq *rq, u16 ix, int wqe_bulk); +int mlx5e_xsk_alloc_rx_wqes(struct mlx5e_rq *rq, u16 ix, int wqe_bulk); struct sk_buff *mlx5e_xsk_skb_from_cqe_mpwrq_linear(struct mlx5e_rq *rq, struct mlx5e_mpw_info *wi, u16 cqe_bcnt, @@ -20,46 +20,4 @@ struct sk_buff *mlx5e_xsk_skb_from_cqe_linear(struct mlx5e_rq *rq, struct mlx5e_wqe_frag_info *wi, u32 cqe_bcnt); -static inline int mlx5e_xsk_page_alloc_pool(struct mlx5e_rq *rq, - struct mlx5e_dma_info *dma_info) -{ -retry: - dma_info->xsk = xsk_buff_alloc(rq->xsk_pool); - if (!dma_info->xsk) - return -ENOMEM; - - /* Store the DMA address without headroom. In striding RQ case, we just - * provide pages for UMR, and headroom is counted at the setup stage - * when creating a WQE. In non-striding RQ case, headroom is accounted - * in mlx5e_alloc_rx_wqe. - */ - dma_info->addr = xsk_buff_xdp_get_frame_dma(dma_info->xsk); - - /* MTT page mapping has alignment requirements. If they are not - * satisfied, leak the descriptor so that it won't come again, and try - * to allocate a new one. - */ - if (rq->wq_type == MLX5_WQ_TYPE_LINKED_LIST_STRIDING_RQ) { - if (unlikely(dma_info->addr & ~MLX5E_MTT_PTAG_MASK)) { - xsk_buff_discard(dma_info->xsk); - goto retry; - } - } - - return 0; -} - -static inline bool mlx5e_xsk_update_rx_wakeup(struct mlx5e_rq *rq, bool alloc_err) -{ - if (!xsk_uses_need_wakeup(rq->xsk_pool)) - return alloc_err; - - if (unlikely(alloc_err)) - xsk_set_rx_need_wakeup(rq->xsk_pool); - else - xsk_clear_rx_need_wakeup(rq->xsk_pool); - - return false; -} - #endif /* __MLX5_EN_XSK_RX_H__ */ diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/xsk/setup.c b/drivers/net/ethernet/mellanox/mlx5/core/en/xsk/setup.c index 98ed9ef3a6bd..ff03c43833bb 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/xsk/setup.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/xsk/setup.c @@ -5,24 +5,19 @@ #include "en/params.h" #include "en/txrx.h" #include "en/health.h" +#include <net/xdp_sock_drv.h> -/* It matches XDP_UMEM_MIN_CHUNK_SIZE, but as this constant is private and may - * change unexpectedly, and mlx5e has a minimum valid stride size for striding - * RQ, keep this check in the driver. +/* The limitation of 2048 can be altered, but shouldn't go beyond the minimal + * stride size of striding RQ. */ -#define MLX5E_MIN_XSK_CHUNK_SIZE 2048 +#define MLX5E_MIN_XSK_CHUNK_SIZE max(2048, XDP_UMEM_MIN_CHUNK_SIZE) bool mlx5e_validate_xsk_param(struct mlx5e_params *params, struct mlx5e_xsk_param *xsk, struct mlx5_core_dev *mdev) { /* AF_XDP doesn't support frames larger than PAGE_SIZE. */ - if (xsk->chunk_size > PAGE_SIZE || - xsk->chunk_size < MLX5E_MIN_XSK_CHUNK_SIZE) - return false; - - /* Current MTU and XSK headroom don't allow packets to fit the frames. */ - if (mlx5e_rx_get_min_frag_sz(params, xsk) > xsk->chunk_size) + if (xsk->chunk_size > PAGE_SIZE || xsk->chunk_size < MLX5E_MIN_XSK_CHUNK_SIZE) return false; /* frag_sz is different for regular and XSK RQs, so ensure that linear @@ -30,9 +25,9 @@ bool mlx5e_validate_xsk_param(struct mlx5e_params *params, */ switch (params->rq_wq_type) { case MLX5_WQ_TYPE_LINKED_LIST_STRIDING_RQ: - return mlx5e_rx_mpwqe_is_linear_skb(mdev, params, xsk); + return !mlx5e_mpwrq_validate_xsk(mdev, params, xsk); default: /* MLX5_WQ_TYPE_CYCLIC */ - return mlx5e_rx_is_linear_skb(params, xsk); + return mlx5e_rx_is_linear_skb(mdev, params, xsk); } } @@ -71,7 +66,7 @@ static int mlx5e_init_xsk_rq(struct mlx5e_channel *c, rq->xsk_pool = pool; rq->stats = &c->priv->channel_stats[c->ix]->xskrq; rq->ptp_cyc2time = mlx5_rq_ts_translator(mdev); - rq_xdp_ix = c->ix + params->num_channels * MLX5E_RQ_GROUP_XSK; + rq_xdp_ix = c->ix; err = mlx5e_rq_set_handlers(rq, params, xsk); if (err) return err; @@ -159,7 +154,7 @@ err_free_cparam: void mlx5e_close_xsk(struct mlx5e_channel *c) { clear_bit(MLX5E_CHANNEL_STATE_XSK, c->state); - synchronize_net(); /* Sync with the XSK wakeup and with NAPI. */ + synchronize_net(); /* Sync with NAPI. */ mlx5e_close_rq(&c->xskrq); mlx5e_close_cq(&c->xskrq.cq); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/xsk/tx.c b/drivers/net/ethernet/mellanox/mlx5/core/en/xsk/tx.c index 4902ef74fedf..367a9505ca4f 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/xsk/tx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/xsk/tx.c @@ -12,18 +12,14 @@ int mlx5e_xsk_wakeup(struct net_device *dev, u32 qid, u32 flags) struct mlx5e_priv *priv = netdev_priv(dev); struct mlx5e_params *params = &priv->channels.params; struct mlx5e_channel *c; - u16 ix; if (unlikely(!mlx5e_xdp_is_active(priv))) return -ENETDOWN; - if (unlikely(!mlx5e_qid_get_ch_if_in_group(params, qid, MLX5E_RQ_GROUP_XSK, &ix))) + if (unlikely(qid >= params->num_channels)) return -EINVAL; - c = priv->channels.c[ix]; - - if (unlikely(!test_bit(MLX5E_CHANNEL_STATE_XSK, c->state))) - return -EINVAL; + c = priv->channels.c[qid]; if (!napi_if_scheduled_mark_missed(&c->napi)) { /* To avoid WQE overrun, don't post a NOP if async_icosq is not @@ -36,9 +32,7 @@ int mlx5e_xsk_wakeup(struct net_device *dev, u32 qid, u32 flags) if (test_and_set_bit(MLX5E_SQ_STATE_PENDING_XSK_TX, &c->async_icosq.state)) return 0; - spin_lock_bh(&c->async_icosq_lock); - mlx5e_trigger_irq(&c->async_icosq); - spin_unlock_bh(&c->async_icosq_lock); + mlx5e_trigger_napi_icosq(c); } return 0; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/xsk/tx.h b/drivers/net/ethernet/mellanox/mlx5/core/en/xsk/tx.h index a05085035f23..9c505158b975 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/xsk/tx.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/xsk/tx.h @@ -5,7 +5,6 @@ #define __MLX5_EN_XSK_TX_H__ #include "en.h" -#include <net/xdp_sock_drv.h> /* TX data path */ @@ -13,15 +12,4 @@ int mlx5e_xsk_wakeup(struct net_device *dev, u32 qid, u32 flags); bool mlx5e_xsk_tx(struct mlx5e_xdpsq *sq, unsigned int budget); -static inline void mlx5e_xsk_update_tx_wakeup(struct mlx5e_xdpsq *sq) -{ - if (!xsk_uses_need_wakeup(sq->xsk_pool)) - return; - - if (sq->pc != sq->cc) - xsk_clear_tx_need_wakeup(sq->xsk_pool); - else - xsk_set_tx_need_wakeup(sq->xsk_pool); -} - #endif /* __MLX5_EN_XSK_TX_H__ */ diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/en_accel.h b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/en_accel.h index 1839f1ab1ddd..07187028f0d3 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/en_accel.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/en_accel.h @@ -39,6 +39,7 @@ #include "en_accel/ipsec_rxtx.h" #include "en_accel/ktls.h" #include "en_accel/ktls_txrx.h" +#include <en_accel/macsec.h> #include "en.h" #include "en/txrx.h" @@ -137,6 +138,15 @@ static inline bool mlx5e_accel_tx_begin(struct net_device *dev, } #endif +#ifdef CONFIG_MLX5_EN_MACSEC + if (unlikely(mlx5e_macsec_skb_is_offload(skb))) { + struct mlx5e_priv *priv = netdev_priv(dev); + + if (unlikely(!mlx5e_macsec_handle_tx_skb(priv->macsec, skb))) + return false; + } +#endif + return true; } @@ -163,6 +173,11 @@ static inline void mlx5e_accel_tx_eseg(struct mlx5e_priv *priv, mlx5e_ipsec_tx_build_eseg(priv, skb, eseg); #endif +#ifdef CONFIG_MLX5_EN_MACSEC + if (unlikely(mlx5e_macsec_skb_is_offload(skb))) + mlx5e_macsec_tx_build_eseg(priv->macsec, skb, eseg); +#endif + #if IS_ENABLED(CONFIG_GENEVE) if (skb->encapsulation && skb->ip_summed == CHECKSUM_PARTIAL) mlx5e_tx_tunnel_accel(skb, eseg, ihs); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/fs_tcp.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/fs_tcp.c index 20a4f1e585af..285d32d2fd08 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/fs_tcp.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/fs_tcp.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB /* Copyright (c) 2020, Mellanox Technologies inc. All rights reserved. */ -#include <linux/netdevice.h> +#include <mlx5_core.h> #include "en_accel/fs_tcp.h" #include "fs_core.h" @@ -71,13 +71,13 @@ void mlx5e_accel_fs_del_sk(struct mlx5_flow_handle *rule) mlx5_del_flow_rules(rule); } -struct mlx5_flow_handle *mlx5e_accel_fs_add_sk(struct mlx5e_priv *priv, +struct mlx5_flow_handle *mlx5e_accel_fs_add_sk(struct mlx5e_flow_steering *fs, struct sock *sk, u32 tirn, uint32_t flow_tag) { + struct mlx5e_accel_fs_tcp *fs_tcp = mlx5e_fs_get_accel_tcp(fs); struct mlx5_flow_destination dest = {}; struct mlx5e_flow_table *ft = NULL; - struct mlx5e_accel_fs_tcp *fs_tcp; MLX5_DECLARE_FLOW_ACT(flow_act); struct mlx5_flow_handle *flow; struct mlx5_flow_spec *spec; @@ -86,19 +86,17 @@ struct mlx5_flow_handle *mlx5e_accel_fs_add_sk(struct mlx5e_priv *priv, if (!spec) return ERR_PTR(-ENOMEM); - fs_tcp = priv->fs->accel_tcp; - spec->match_criteria_enable = MLX5_MATCH_OUTER_HEADERS; switch (sk->sk_family) { case AF_INET: accel_fs_tcp_set_ipv4_flow(spec, sk); ft = &fs_tcp->tables[ACCEL_FS_IPV4_TCP]; - mlx5e_dbg(HW, priv, "%s flow is %pI4:%d -> %pI4:%d\n", __func__, - &inet_sk(sk)->inet_rcv_saddr, - inet_sk(sk)->inet_sport, - &inet_sk(sk)->inet_daddr, - inet_sk(sk)->inet_dport); + fs_dbg(fs, "%s flow is %pI4:%d -> %pI4:%d\n", __func__, + &inet_sk(sk)->inet_rcv_saddr, + inet_sk(sk)->inet_sport, + &inet_sk(sk)->inet_daddr, + inet_sk(sk)->inet_dport); break; #if IS_ENABLED(CONFIG_IPV6) case AF_INET6: @@ -140,34 +138,32 @@ struct mlx5_flow_handle *mlx5e_accel_fs_add_sk(struct mlx5e_priv *priv, flow = mlx5_add_flow_rules(ft->t, spec, &flow_act, &dest, 1); if (IS_ERR(flow)) - netdev_err(priv->netdev, "mlx5_add_flow_rules() failed, flow is %ld\n", - PTR_ERR(flow)); + fs_err(fs, "mlx5_add_flow_rules() failed, flow is %ld\n", PTR_ERR(flow)); out: kvfree(spec); return flow; } -static int accel_fs_tcp_add_default_rule(struct mlx5e_priv *priv, +static int accel_fs_tcp_add_default_rule(struct mlx5e_flow_steering *fs, enum accel_fs_tcp_type type) { + struct mlx5e_accel_fs_tcp *fs_tcp = mlx5e_fs_get_accel_tcp(fs); + struct mlx5_ttc_table *ttc = mlx5e_fs_get_ttc(fs, false); struct mlx5e_flow_table *accel_fs_t; struct mlx5_flow_destination dest; - struct mlx5e_accel_fs_tcp *fs_tcp; MLX5_DECLARE_FLOW_ACT(flow_act); struct mlx5_flow_handle *rule; int err = 0; - fs_tcp = priv->fs->accel_tcp; accel_fs_t = &fs_tcp->tables[type]; - dest = mlx5_ttc_get_default_dest(priv->fs->ttc, fs_accel2tt(type)); + dest = mlx5_ttc_get_default_dest(ttc, fs_accel2tt(type)); rule = mlx5_add_flow_rules(accel_fs_t->t, NULL, &flow_act, &dest, 1); if (IS_ERR(rule)) { err = PTR_ERR(rule); - netdev_err(priv->netdev, - "%s: add default rule failed, accel_fs type=%d, err %d\n", - __func__, type, err); + fs_err(fs, "%s: add default rule failed, accel_fs type=%d, err %d\n", + __func__, type, err); return err; } @@ -265,9 +261,11 @@ out: return err; } -static int accel_fs_tcp_create_table(struct mlx5e_priv *priv, enum accel_fs_tcp_type type) +static int accel_fs_tcp_create_table(struct mlx5e_flow_steering *fs, enum accel_fs_tcp_type type) { - struct mlx5e_flow_table *ft = &priv->fs->accel_tcp->tables[type]; + struct mlx5e_accel_fs_tcp *accel_tcp = mlx5e_fs_get_accel_tcp(fs); + struct mlx5_flow_namespace *ns = mlx5e_fs_get_ns(fs, false); + struct mlx5e_flow_table *ft = &accel_tcp->tables[type]; struct mlx5_flow_table_attr ft_attr = {}; int err; @@ -277,21 +275,21 @@ static int accel_fs_tcp_create_table(struct mlx5e_priv *priv, enum accel_fs_tcp_ ft_attr.level = MLX5E_ACCEL_FS_TCP_FT_LEVEL; ft_attr.prio = MLX5E_NIC_PRIO; - ft->t = mlx5_create_flow_table(priv->fs->ns, &ft_attr); + ft->t = mlx5_create_flow_table(ns, &ft_attr); if (IS_ERR(ft->t)) { err = PTR_ERR(ft->t); ft->t = NULL; return err; } - netdev_dbg(priv->netdev, "Created fs accel table id %u level %u\n", - ft->t->id, ft->t->level); + fs_dbg(fs, "Created fs accel table id %u level %u\n", + ft->t->id, ft->t->level); err = accel_fs_tcp_create_groups(ft, type); if (err) goto err; - err = accel_fs_tcp_add_default_rule(priv, type); + err = accel_fs_tcp_add_default_rule(fs, type); if (err) goto err; @@ -301,17 +299,18 @@ err: return err; } -static int accel_fs_tcp_disable(struct mlx5e_priv *priv) +static int accel_fs_tcp_disable(struct mlx5e_flow_steering *fs) { + struct mlx5_ttc_table *ttc = mlx5e_fs_get_ttc(fs, false); int err, i; for (i = 0; i < ACCEL_FS_TCP_NUM_TYPES; i++) { /* Modify ttc rules destination to point back to the indir TIRs */ - err = mlx5_ttc_fwd_default_dest(priv->fs->ttc, fs_accel2tt(i)); + err = mlx5_ttc_fwd_default_dest(ttc, fs_accel2tt(i)); if (err) { - netdev_err(priv->netdev, - "%s: modify ttc[%d] default destination failed, err(%d)\n", - __func__, fs_accel2tt(i), err); + fs_err(fs, + "%s: modify ttc[%d] default destination failed, err(%d)\n", + __func__, fs_accel2tt(i), err); return err; } } @@ -319,32 +318,32 @@ static int accel_fs_tcp_disable(struct mlx5e_priv *priv) return 0; } -static int accel_fs_tcp_enable(struct mlx5e_priv *priv) +static int accel_fs_tcp_enable(struct mlx5e_flow_steering *fs) { + struct mlx5e_accel_fs_tcp *accel_tcp = mlx5e_fs_get_accel_tcp(fs); + struct mlx5_ttc_table *ttc = mlx5e_fs_get_ttc(fs, false); struct mlx5_flow_destination dest = {}; int err, i; dest.type = MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE; for (i = 0; i < ACCEL_FS_TCP_NUM_TYPES; i++) { - dest.ft = priv->fs->accel_tcp->tables[i].t; + dest.ft = accel_tcp->tables[i].t; /* Modify ttc rules destination to point on the accel_fs FTs */ - err = mlx5_ttc_fwd_dest(priv->fs->ttc, fs_accel2tt(i), &dest); + err = mlx5_ttc_fwd_dest(ttc, fs_accel2tt(i), &dest); if (err) { - netdev_err(priv->netdev, - "%s: modify ttc[%d] destination to accel failed, err(%d)\n", - __func__, fs_accel2tt(i), err); + fs_err(fs, "%s: modify ttc[%d] destination to accel failed, err(%d)\n", + __func__, fs_accel2tt(i), err); return err; } } return 0; } -static void accel_fs_tcp_destroy_table(struct mlx5e_priv *priv, int i) +static void accel_fs_tcp_destroy_table(struct mlx5e_flow_steering *fs, int i) { - struct mlx5e_accel_fs_tcp *fs_tcp; + struct mlx5e_accel_fs_tcp *fs_tcp = mlx5e_fs_get_accel_tcp(fs); - fs_tcp = priv->fs->accel_tcp; if (IS_ERR_OR_NULL(fs_tcp->tables[i].t)) return; @@ -353,40 +352,43 @@ static void accel_fs_tcp_destroy_table(struct mlx5e_priv *priv, int i) fs_tcp->tables[i].t = NULL; } -void mlx5e_accel_fs_tcp_destroy(struct mlx5e_priv *priv) +void mlx5e_accel_fs_tcp_destroy(struct mlx5e_flow_steering *fs) { + struct mlx5e_accel_fs_tcp *accel_tcp = mlx5e_fs_get_accel_tcp(fs); int i; - if (!priv->fs->accel_tcp) + if (!accel_tcp) return; - accel_fs_tcp_disable(priv); + accel_fs_tcp_disable(fs); for (i = 0; i < ACCEL_FS_TCP_NUM_TYPES; i++) - accel_fs_tcp_destroy_table(priv, i); + accel_fs_tcp_destroy_table(fs, i); - kfree(priv->fs->accel_tcp); - priv->fs->accel_tcp = NULL; + kfree(accel_tcp); + mlx5e_fs_set_accel_tcp(fs, NULL); } -int mlx5e_accel_fs_tcp_create(struct mlx5e_priv *priv) +int mlx5e_accel_fs_tcp_create(struct mlx5e_flow_steering *fs) { + struct mlx5e_accel_fs_tcp *accel_tcp; int i, err; - if (!MLX5_CAP_FLOWTABLE_NIC_RX(priv->mdev, ft_field_support.outer_ip_version)) + if (!MLX5_CAP_FLOWTABLE_NIC_RX(mlx5e_fs_get_mdev(fs), ft_field_support.outer_ip_version)) return -EOPNOTSUPP; - priv->fs->accel_tcp = kzalloc(sizeof(*priv->fs->accel_tcp), GFP_KERNEL); - if (!priv->fs->accel_tcp) + accel_tcp = kvzalloc(sizeof(*accel_tcp), GFP_KERNEL); + if (!accel_tcp) return -ENOMEM; + mlx5e_fs_set_accel_tcp(fs, accel_tcp); for (i = 0; i < ACCEL_FS_TCP_NUM_TYPES; i++) { - err = accel_fs_tcp_create_table(priv, i); + err = accel_fs_tcp_create_table(fs, i); if (err) goto err_destroy_tables; } - err = accel_fs_tcp_enable(priv); + err = accel_fs_tcp_enable(fs); if (err) goto err_destroy_tables; @@ -394,9 +396,8 @@ int mlx5e_accel_fs_tcp_create(struct mlx5e_priv *priv) err_destroy_tables: while (--i >= 0) - accel_fs_tcp_destroy_table(priv, i); - - kfree(priv->fs->accel_tcp); - priv->fs->accel_tcp = NULL; + accel_fs_tcp_destroy_table(fs, i); + kfree(accel_tcp); + mlx5e_fs_set_accel_tcp(fs, NULL); return err; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/fs_tcp.h b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/fs_tcp.h index 589235824543..a032bff482a6 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/fs_tcp.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/fs_tcp.h @@ -4,19 +4,19 @@ #ifndef __MLX5E_ACCEL_FS_TCP_H__ #define __MLX5E_ACCEL_FS_TCP_H__ -#include "en.h" +#include "en/fs.h" #ifdef CONFIG_MLX5_EN_TLS -int mlx5e_accel_fs_tcp_create(struct mlx5e_priv *priv); -void mlx5e_accel_fs_tcp_destroy(struct mlx5e_priv *priv); -struct mlx5_flow_handle *mlx5e_accel_fs_add_sk(struct mlx5e_priv *priv, +int mlx5e_accel_fs_tcp_create(struct mlx5e_flow_steering *fs); +void mlx5e_accel_fs_tcp_destroy(struct mlx5e_flow_steering *fs); +struct mlx5_flow_handle *mlx5e_accel_fs_add_sk(struct mlx5e_flow_steering *fs, struct sock *sk, u32 tirn, uint32_t flow_tag); void mlx5e_accel_fs_del_sk(struct mlx5_flow_handle *rule); #else -static inline int mlx5e_accel_fs_tcp_create(struct mlx5e_priv *priv) { return 0; } -static inline void mlx5e_accel_fs_tcp_destroy(struct mlx5e_priv *priv) {} -static inline struct mlx5_flow_handle *mlx5e_accel_fs_add_sk(struct mlx5e_priv *priv, +static inline int mlx5e_accel_fs_tcp_create(struct mlx5e_flow_steering *fs) { return 0; } +static inline void mlx5e_accel_fs_tcp_destroy(struct mlx5e_flow_steering *fs) {} +static inline struct mlx5_flow_handle *mlx5e_accel_fs_add_sk(struct mlx5e_flow_steering *fs, struct sock *sk, u32 tirn, uint32_t flow_tag) { return ERR_PTR(-EOPNOTSUPP); } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_fs.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_fs.c index f8113fd23265..b859e4a4c744 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_fs.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_fs.c @@ -174,6 +174,8 @@ static void rx_destroy(struct mlx5e_priv *priv, enum accel_fs_esp_type type) static int rx_create(struct mlx5e_priv *priv, enum accel_fs_esp_type type) { + struct mlx5_flow_namespace *ns = mlx5e_fs_get_ns(priv->fs, false); + struct mlx5_ttc_table *ttc = mlx5e_fs_get_ttc(priv->fs, false); struct mlx5_flow_table_attr ft_attr = {}; struct mlx5e_accel_fs_esp_prot *fs_prot; struct mlx5e_accel_fs_esp *accel_esp; @@ -182,15 +184,14 @@ static int rx_create(struct mlx5e_priv *priv, enum accel_fs_esp_type type) accel_esp = priv->ipsec->rx_fs; fs_prot = &accel_esp->fs_prot[type]; - fs_prot->default_dest = - mlx5_ttc_get_default_dest(priv->fs->ttc, fs_esp2tt(type)); + mlx5_ttc_get_default_dest(ttc, fs_esp2tt(type)); ft_attr.max_fte = 1; ft_attr.autogroup.max_num_groups = 1; ft_attr.level = MLX5E_ACCEL_FS_ESP_FT_ERR_LEVEL; ft_attr.prio = MLX5E_NIC_PRIO; - ft = mlx5_create_auto_grouped_flow_table(priv->fs->ns, &ft_attr); + ft = mlx5_create_auto_grouped_flow_table(ns, &ft_attr); if (IS_ERR(ft)) return PTR_ERR(ft); @@ -205,7 +206,7 @@ static int rx_create(struct mlx5e_priv *priv, enum accel_fs_esp_type type) ft_attr.prio = MLX5E_NIC_PRIO; ft_attr.autogroup.num_reserved_entries = 1; ft_attr.autogroup.max_num_groups = 1; - ft = mlx5_create_auto_grouped_flow_table(priv->fs->ns, &ft_attr); + ft = mlx5_create_auto_grouped_flow_table(ns, &ft_attr); if (IS_ERR(ft)) { err = PTR_ERR(ft); goto err_fs_ft; @@ -230,6 +231,7 @@ err_add: static int rx_ft_get(struct mlx5e_priv *priv, enum accel_fs_esp_type type) { + struct mlx5_ttc_table *ttc = mlx5e_fs_get_ttc(priv->fs, false); struct mlx5e_accel_fs_esp_prot *fs_prot; struct mlx5_flow_destination dest = {}; struct mlx5e_accel_fs_esp *accel_esp; @@ -249,7 +251,7 @@ static int rx_ft_get(struct mlx5e_priv *priv, enum accel_fs_esp_type type) /* connect */ dest.type = MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE; dest.ft = fs_prot->ft; - mlx5_ttc_fwd_dest(priv->fs->ttc, fs_esp2tt(type), &dest); + mlx5_ttc_fwd_dest(ttc, fs_esp2tt(type), &dest); skip: fs_prot->refcnt++; @@ -260,6 +262,7 @@ out: static void rx_ft_put(struct mlx5e_priv *priv, enum accel_fs_esp_type type) { + struct mlx5_ttc_table *ttc = mlx5e_fs_get_ttc(priv->fs, false); struct mlx5e_accel_fs_esp_prot *fs_prot; struct mlx5e_accel_fs_esp *accel_esp; @@ -271,7 +274,7 @@ static void rx_ft_put(struct mlx5e_priv *priv, enum accel_fs_esp_type type) goto out; /* disconnect */ - mlx5_ttc_fwd_default_dest(priv->fs->ttc, fs_esp2tt(type)); + mlx5_ttc_fwd_default_dest(ttc, fs_esp2tt(type)); /* remove FT */ rx_destroy(priv, type); @@ -385,7 +388,8 @@ static void setup_fte_common(struct mlx5_accel_esp_xfrm_attrs *attrs, 0xff, 16); } - flow_act->ipsec_obj_id = ipsec_obj_id; + flow_act->crypto.type = MLX5_FLOW_CONTEXT_ENCRYPT_DECRYPT_TYPE_IPSEC; + flow_act->crypto.obj_id = ipsec_obj_id; flow_act->flags |= FLOW_ACT_NO_APPEND; } @@ -441,7 +445,7 @@ static int rx_add_rule(struct mlx5e_priv *priv, } flow_act.action = MLX5_FLOW_CONTEXT_ACTION_FWD_DEST | - MLX5_FLOW_CONTEXT_ACTION_IPSEC_DECRYPT | + MLX5_FLOW_CONTEXT_ACTION_CRYPTO_DECRYPT | MLX5_FLOW_CONTEXT_ACTION_MOD_HDR; dest.type = MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE; flow_act.modify_hdr = modify_hdr; @@ -497,7 +501,7 @@ static int tx_add_rule(struct mlx5e_priv *priv, MLX5_ETH_WQE_FT_META_IPSEC); flow_act.action = MLX5_FLOW_CONTEXT_ACTION_ALLOW | - MLX5_FLOW_CONTEXT_ACTION_IPSEC_ENCRYPT; + MLX5_FLOW_CONTEXT_ACTION_CRYPTO_ENCRYPT; rule = mlx5_add_flow_rules(priv->ipsec->tx_fs->ft, spec, &flow_act, NULL, 0); if (IS_ERR(rule)) { err = PTR_ERR(rule); @@ -573,7 +577,7 @@ int mlx5e_accel_ipsec_fs_init(struct mlx5e_ipsec *ipsec) int err = -ENOMEM; ns = mlx5_get_flow_namespace(ipsec->mdev, - MLX5_FLOW_NAMESPACE_EGRESS_KERNEL); + MLX5_FLOW_NAMESPACE_EGRESS_IPSEC); if (!ns) return -EOPNOTSUPP; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_rxtx.h b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_rxtx.h index 0ae4e12ce528..1878a70b9031 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_rxtx.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_rxtx.h @@ -39,9 +39,9 @@ #include "en.h" #include "en/txrx.h" -/* Bit31: IPsec marker, Bit30-24: IPsec syndrome, Bit23-0: IPsec obj id */ +/* Bit31: IPsec marker, Bit30: reserved, Bit29-24: IPsec syndrome, Bit23-0: IPsec obj id */ #define MLX5_IPSEC_METADATA_MARKER(metadata) (((metadata) >> 31) & 0x1) -#define MLX5_IPSEC_METADATA_SYNDROM(metadata) (((metadata) >> 24) & GENMASK(6, 0)) +#define MLX5_IPSEC_METADATA_SYNDROM(metadata) (((metadata) >> 24) & GENMASK(5, 0)) #define MLX5_IPSEC_METADATA_HANDLE(metadata) ((metadata) & GENMASK(23, 0)) struct mlx5e_accel_tx_ipsec_state { @@ -77,11 +77,6 @@ static inline bool mlx5_ipsec_is_rx_flow(struct mlx5_cqe64 *cqe) return MLX5_IPSEC_METADATA_MARKER(be32_to_cpu(cqe->ft_metadata)); } -static inline bool mlx5e_ipsec_is_tx_flow(struct mlx5e_accel_tx_ipsec_state *ipsec_st) -{ - return ipsec_st->x; -} - static inline bool mlx5e_ipsec_eseg_meta(struct mlx5_wqe_eth_seg *eseg) { return eseg->flow_table_metadata & cpu_to_be32(MLX5_ETH_WQE_FT_META_IPSEC); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls.c index 30a70d139046..da2184c94203 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls.c @@ -92,6 +92,24 @@ static const struct tlsdev_ops mlx5e_ktls_ops = { .tls_dev_resync = mlx5e_ktls_resync, }; +bool mlx5e_is_ktls_rx(struct mlx5_core_dev *mdev) +{ + u8 max_sq_wqebbs = mlx5e_get_max_sq_wqebbs(mdev); + + if (is_kdump_kernel() || !MLX5_CAP_GEN(mdev, tls_rx)) + return false; + + /* Check the possibility to post the required ICOSQ WQEs. */ + if (WARN_ON_ONCE(max_sq_wqebbs < MLX5E_TLS_SET_STATIC_PARAMS_WQEBBS)) + return false; + if (WARN_ON_ONCE(max_sq_wqebbs < MLX5E_TLS_SET_PROGRESS_PARAMS_WQEBBS)) + return false; + if (WARN_ON_ONCE(max_sq_wqebbs < MLX5E_KTLS_GET_PROGRESS_WQEBBS)) + return false; + + return true; +} + void mlx5e_ktls_build_netdev(struct mlx5e_priv *priv) { struct net_device *netdev = priv->netdev; @@ -118,9 +136,9 @@ int mlx5e_ktls_set_feature_rx(struct net_device *netdev, bool enable) mutex_lock(&priv->state_lock); if (enable) - err = mlx5e_accel_fs_tcp_create(priv); + err = mlx5e_accel_fs_tcp_create(priv->fs); else - mlx5e_accel_fs_tcp_destroy(priv); + mlx5e_accel_fs_tcp_destroy(priv->fs); mutex_unlock(&priv->state_lock); return err; @@ -138,7 +156,7 @@ int mlx5e_ktls_init_rx(struct mlx5e_priv *priv) return -ENOMEM; if (priv->netdev->features & NETIF_F_HW_TLS_RX) { - err = mlx5e_accel_fs_tcp_create(priv); + err = mlx5e_accel_fs_tcp_create(priv->fs); if (err) { destroy_workqueue(priv->tls->rx_wq); return err; @@ -154,7 +172,7 @@ void mlx5e_ktls_cleanup_rx(struct mlx5e_priv *priv) return; if (priv->netdev->features & NETIF_F_HW_TLS_RX) - mlx5e_accel_fs_tcp_destroy(priv); + mlx5e_accel_fs_tcp_destroy(priv->fs); destroy_workqueue(priv->tls->rx_wq); } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls.h b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls.h index 948400dee525..1c35045e41fb 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls.h @@ -25,7 +25,8 @@ static inline bool mlx5e_is_ktls_device(struct mlx5_core_dev *mdev) if (!MLX5_CAP_GEN(mdev, log_max_dek)) return false; - return MLX5_CAP_TLS(mdev, tls_1_2_aes_gcm_128); + return (MLX5_CAP_TLS(mdev, tls_1_2_aes_gcm_128) || + MLX5_CAP_TLS(mdev, tls_1_2_aes_gcm_256)); } static inline bool mlx5e_ktls_type_check(struct mlx5_core_dev *mdev, @@ -36,6 +37,10 @@ static inline bool mlx5e_ktls_type_check(struct mlx5_core_dev *mdev, if (crypto_info->version == TLS_1_2_VERSION) return MLX5_CAP_TLS(mdev, tls_1_2_aes_gcm_128); break; + case TLS_CIPHER_AES_GCM_256: + if (crypto_info->version == TLS_1_2_VERSION) + return MLX5_CAP_TLS(mdev, tls_1_2_aes_gcm_256); + break; } return false; @@ -56,10 +61,7 @@ static inline bool mlx5e_is_ktls_tx(struct mlx5_core_dev *mdev) return !is_kdump_kernel() && MLX5_CAP_GEN(mdev, tls_tx); } -static inline bool mlx5e_is_ktls_rx(struct mlx5_core_dev *mdev) -{ - return !is_kdump_kernel() && MLX5_CAP_GEN(mdev, tls_rx); -} +bool mlx5e_is_ktls_rx(struct mlx5_core_dev *mdev); struct mlx5e_tls_sw_stats { atomic64_t tx_tls_ctx; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls_rx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls_rx.c index 27483aa7be8a..3e54834747ce 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls_rx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls_rx.c @@ -43,7 +43,7 @@ struct mlx5e_ktls_rx_resync_ctx { }; struct mlx5e_ktls_offload_context_rx { - struct tls12_crypto_info_aes_gcm_128 crypto_info; + union mlx5e_crypto_info crypto_info; struct accel_rule rule; struct sock *sk; struct mlx5e_rq_stats *rq_stats; @@ -111,7 +111,7 @@ static void accel_rule_handle_work(struct work_struct *work) if (unlikely(test_bit(MLX5E_PRIV_RX_FLAG_DELETING, priv_rx->flags))) goto out; - rule = mlx5e_accel_fs_add_sk(accel_rule->priv, priv_rx->sk, + rule = mlx5e_accel_fs_add_sk(accel_rule->priv->fs, priv_rx->sk, mlx5e_tir_get_tirn(&priv_rx->tir), MLX5_FS_DEFAULT_FLOW_TAG); if (!IS_ERR_OR_NULL(rule)) @@ -362,7 +362,6 @@ static void resync_init(struct mlx5e_ktls_rx_resync_ctx *resync, static void resync_handle_seq_match(struct mlx5e_ktls_offload_context_rx *priv_rx, struct mlx5e_channel *c) { - struct tls12_crypto_info_aes_gcm_128 *info = &priv_rx->crypto_info; struct mlx5e_ktls_resync_resp *ktls_resync; struct mlx5e_icosq *sq; bool trigger_poll; @@ -373,7 +372,31 @@ static void resync_handle_seq_match(struct mlx5e_ktls_offload_context_rx *priv_r spin_lock_bh(&ktls_resync->lock); spin_lock_bh(&priv_rx->lock); - memcpy(info->rec_seq, &priv_rx->resync.sw_rcd_sn_be, sizeof(info->rec_seq)); + switch (priv_rx->crypto_info.crypto_info.cipher_type) { + case TLS_CIPHER_AES_GCM_128: { + struct tls12_crypto_info_aes_gcm_128 *info = + &priv_rx->crypto_info.crypto_info_128; + + memcpy(info->rec_seq, &priv_rx->resync.sw_rcd_sn_be, + sizeof(info->rec_seq)); + break; + } + case TLS_CIPHER_AES_GCM_256: { + struct tls12_crypto_info_aes_gcm_256 *info = + &priv_rx->crypto_info.crypto_info_256; + + memcpy(info->rec_seq, &priv_rx->resync.sw_rcd_sn_be, + sizeof(info->rec_seq)); + break; + } + default: + WARN_ONCE(1, "Unsupported cipher type %u\n", + priv_rx->crypto_info.crypto_info.cipher_type); + spin_unlock_bh(&priv_rx->lock); + spin_unlock_bh(&ktls_resync->lock); + return; + } + if (list_empty(&priv_rx->list)) { list_add_tail(&priv_rx->list, &ktls_resync->list); trigger_poll = !test_and_set_bit(MLX5E_SQ_STATE_PENDING_TLS_RX_RESYNC, &sq->state); @@ -461,6 +484,7 @@ static void resync_update_sn(struct mlx5e_rq *rq, struct sk_buff *skb) { struct ethhdr *eth = (struct ethhdr *)(skb->data); struct net_device *netdev = rq->netdev; + struct net *net = dev_net(netdev); struct sock *sk = NULL; unsigned int datalen; struct iphdr *iph; @@ -475,7 +499,7 @@ static void resync_update_sn(struct mlx5e_rq *rq, struct sk_buff *skb) depth += sizeof(struct iphdr); th = (void *)iph + sizeof(struct iphdr); - sk = inet_lookup_established(dev_net(netdev), &tcp_hashinfo, + sk = inet_lookup_established(net, net->ipv4.tcp_death_row.hashinfo, iph->saddr, th->source, iph->daddr, th->dest, netdev->ifindex); #if IS_ENABLED(CONFIG_IPV6) @@ -485,7 +509,7 @@ static void resync_update_sn(struct mlx5e_rq *rq, struct sk_buff *skb) depth += sizeof(struct ipv6hdr); th = (void *)ipv6h + sizeof(struct ipv6hdr); - sk = __inet6_lookup_established(dev_net(netdev), &tcp_hashinfo, + sk = __inet6_lookup_established(net, net->ipv4.tcp_death_row.hashinfo, &ipv6h->saddr, th->source, &ipv6h->daddr, ntohs(th->dest), netdev->ifindex, 0); @@ -603,8 +627,20 @@ int mlx5e_ktls_add_rx(struct net_device *netdev, struct sock *sk, INIT_LIST_HEAD(&priv_rx->list); spin_lock_init(&priv_rx->lock); - priv_rx->crypto_info = - *(struct tls12_crypto_info_aes_gcm_128 *)crypto_info; + switch (crypto_info->cipher_type) { + case TLS_CIPHER_AES_GCM_128: + priv_rx->crypto_info.crypto_info_128 = + *(struct tls12_crypto_info_aes_gcm_128 *)crypto_info; + break; + case TLS_CIPHER_AES_GCM_256: + priv_rx->crypto_info.crypto_info_256 = + *(struct tls12_crypto_info_aes_gcm_256 *)crypto_info; + break; + default: + WARN_ONCE(1, "Unsupported cipher type %u\n", + crypto_info->cipher_type); + return -EOPNOTSUPP; + } rxq = mlx5e_ktls_sk_get_rxq(sk); priv_rx->rxq = rxq; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls_tx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls_tx.c index 3a1f76eac542..2e0335246967 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls_tx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls_tx.c @@ -93,7 +93,7 @@ struct mlx5e_ktls_offload_context_tx { bool ctx_post_pending; /* control / resync */ struct list_head list_node; /* member of the pool */ - struct tls12_crypto_info_aes_gcm_128 crypto_info; + union mlx5e_crypto_info crypto_info; struct tls_offload_context_tx *tx_ctx; struct mlx5_core_dev *mdev; struct mlx5e_tls_sw_stats *sw_stats; @@ -485,8 +485,20 @@ int mlx5e_ktls_add_tx(struct net_device *netdev, struct sock *sk, goto err_create_key; priv_tx->expected_seq = start_offload_tcp_sn; - priv_tx->crypto_info = - *(struct tls12_crypto_info_aes_gcm_128 *)crypto_info; + switch (crypto_info->cipher_type) { + case TLS_CIPHER_AES_GCM_128: + priv_tx->crypto_info.crypto_info_128 = + *(struct tls12_crypto_info_aes_gcm_128 *)crypto_info; + break; + case TLS_CIPHER_AES_GCM_256: + priv_tx->crypto_info.crypto_info_256 = + *(struct tls12_crypto_info_aes_gcm_256 *)crypto_info; + break; + default: + WARN_ONCE(1, "Unsupported cipher type %u\n", + crypto_info->cipher_type); + return -EOPNOTSUPP; + } priv_tx->tx_ctx = tls_offload_ctx_tx(tls_ctx); mlx5e_set_ktls_tx_priv_ctx(tls_ctx, priv_tx); @@ -671,14 +683,31 @@ tx_post_resync_params(struct mlx5e_txqsq *sq, struct mlx5e_ktls_offload_context_tx *priv_tx, u64 rcd_sn) { - struct tls12_crypto_info_aes_gcm_128 *info = &priv_tx->crypto_info; __be64 rn_be = cpu_to_be64(rcd_sn); bool skip_static_post; u16 rec_seq_sz; char *rec_seq; - rec_seq = info->rec_seq; - rec_seq_sz = sizeof(info->rec_seq); + switch (priv_tx->crypto_info.crypto_info.cipher_type) { + case TLS_CIPHER_AES_GCM_128: { + struct tls12_crypto_info_aes_gcm_128 *info = &priv_tx->crypto_info.crypto_info_128; + + rec_seq = info->rec_seq; + rec_seq_sz = sizeof(info->rec_seq); + break; + } + case TLS_CIPHER_AES_GCM_256: { + struct tls12_crypto_info_aes_gcm_256 *info = &priv_tx->crypto_info.crypto_info_256; + + rec_seq = info->rec_seq; + rec_seq_sz = sizeof(info->rec_seq); + break; + } + default: + WARN_ONCE(1, "Unsupported cipher type %u\n", + priv_tx->crypto_info.crypto_info.cipher_type); + return; + } skip_static_post = !memcmp(rec_seq, &rn_be, rec_seq_sz); if (!skip_static_post) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls_txrx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls_txrx.c index ac29aeb8af49..570a912dd6fa 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls_txrx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls_txrx.c @@ -21,7 +21,7 @@ enum { static void fill_static_params(struct mlx5_wqe_tls_static_params_seg *params, - struct tls12_crypto_info_aes_gcm_128 *info, + union mlx5e_crypto_info *crypto_info, u32 key_id, u32 resync_tcp_sn) { char *initial_rn, *gcm_iv; @@ -32,7 +32,26 @@ fill_static_params(struct mlx5_wqe_tls_static_params_seg *params, ctx = params->ctx; - EXTRACT_INFO_FIELDS; + switch (crypto_info->crypto_info.cipher_type) { + case TLS_CIPHER_AES_GCM_128: { + struct tls12_crypto_info_aes_gcm_128 *info = + &crypto_info->crypto_info_128; + + EXTRACT_INFO_FIELDS; + break; + } + case TLS_CIPHER_AES_GCM_256: { + struct tls12_crypto_info_aes_gcm_256 *info = + &crypto_info->crypto_info_256; + + EXTRACT_INFO_FIELDS; + break; + } + default: + WARN_ONCE(1, "Unsupported cipher type %u\n", + crypto_info->crypto_info.cipher_type); + return; + } gcm_iv = MLX5_ADDR_OF(tls_static_params, ctx, gcm_iv); initial_rn = MLX5_ADDR_OF(tls_static_params, ctx, initial_record_number); @@ -54,7 +73,7 @@ fill_static_params(struct mlx5_wqe_tls_static_params_seg *params, void mlx5e_ktls_build_static_params(struct mlx5e_set_tls_static_params_wqe *wqe, u16 pc, u32 sqn, - struct tls12_crypto_info_aes_gcm_128 *info, + union mlx5e_crypto_info *crypto_info, u32 tis_tir_num, u32 key_id, u32 resync_tcp_sn, bool fence, enum tls_offload_ctx_dir direction) { @@ -75,7 +94,7 @@ mlx5e_ktls_build_static_params(struct mlx5e_set_tls_static_params_wqe *wqe, ucseg->flags = MLX5_UMR_INLINE; ucseg->bsf_octowords = cpu_to_be16(MLX5_ST_SZ_BYTES(tls_static_params) / 16); - fill_static_params(&wqe->params, info, key_id, resync_tcp_sn); + fill_static_params(&wqe->params, crypto_info, key_id, resync_tcp_sn); } static void diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls_utils.h b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls_utils.h index 0dc715c4c10d..3d79cd379890 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls_utils.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls_utils.h @@ -27,6 +27,12 @@ int mlx5e_ktls_add_rx(struct net_device *netdev, struct sock *sk, void mlx5e_ktls_del_rx(struct net_device *netdev, struct tls_context *tls_ctx); void mlx5e_ktls_rx_resync(struct net_device *netdev, struct sock *sk, u32 seq, u8 *rcd_sn); +union mlx5e_crypto_info { + struct tls_crypto_info crypto_info; + struct tls12_crypto_info_aes_gcm_128 crypto_info_128; + struct tls12_crypto_info_aes_gcm_256 crypto_info_256; +}; + struct mlx5e_set_tls_static_params_wqe { struct mlx5_wqe_ctrl_seg ctrl; struct mlx5_wqe_umr_ctrl_seg uctrl; @@ -72,7 +78,7 @@ struct mlx5e_get_tls_progress_params_wqe { void mlx5e_ktls_build_static_params(struct mlx5e_set_tls_static_params_wqe *wqe, u16 pc, u32 sqn, - struct tls12_crypto_info_aes_gcm_128 *info, + union mlx5e_crypto_info *crypto_info, u32 tis_tir_num, u32 key_id, u32 resync_tcp_sn, bool fence, enum tls_offload_ctx_dir direction); void diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/macsec.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/macsec.c new file mode 100644 index 000000000000..5da746da898d --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/macsec.c @@ -0,0 +1,1870 @@ +// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB +/* Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. */ + +#include <linux/mlx5/device.h> +#include <linux/mlx5/mlx5_ifc.h> +#include <linux/xarray.h> + +#include "en.h" +#include "lib/aso.h" +#include "lib/mlx5.h" +#include "en_accel/macsec.h" +#include "en_accel/macsec_fs.h" + +#define MLX5_MACSEC_EPN_SCOPE_MID 0x80000000L +#define MLX5E_MACSEC_ASO_CTX_SZ MLX5_ST_SZ_BYTES(macsec_aso) + +enum mlx5_macsec_aso_event_arm { + MLX5E_ASO_EPN_ARM = BIT(0), +}; + +enum { + MLX5_MACSEC_ASO_REMOVE_FLOW_PKT_CNT_OFFSET, +}; + +struct mlx5e_macsec_handle { + struct mlx5e_macsec *macsec; + u32 obj_id; + u8 idx; +}; + +enum { + MLX5_MACSEC_EPN, +}; + +struct mlx5e_macsec_aso_out { + u8 event_arm; + u32 mode_param; +}; + +struct mlx5e_macsec_aso_in { + u8 mode; + u32 obj_id; +}; + +struct mlx5e_macsec_epn_state { + u32 epn_msb; + u8 epn_enabled; + u8 overlap; +}; + +struct mlx5e_macsec_async_work { + struct mlx5e_macsec *macsec; + struct mlx5_core_dev *mdev; + struct work_struct work; + u32 obj_id; +}; + +struct mlx5e_macsec_sa { + bool active; + u8 assoc_num; + u32 macsec_obj_id; + u32 enc_key_id; + u32 next_pn; + sci_t sci; + salt_t salt; + + struct rhash_head hash; + u32 fs_id; + union mlx5e_macsec_rule *macsec_rule; + struct rcu_head rcu_head; + struct mlx5e_macsec_epn_state epn_state; +}; + +struct mlx5e_macsec_rx_sc; +struct mlx5e_macsec_rx_sc_xarray_element { + u32 fs_id; + struct mlx5e_macsec_rx_sc *rx_sc; +}; + +struct mlx5e_macsec_rx_sc { + bool active; + sci_t sci; + struct mlx5e_macsec_sa *rx_sa[MACSEC_NUM_AN]; + struct list_head rx_sc_list_element; + struct mlx5e_macsec_rx_sc_xarray_element *sc_xarray_element; + struct metadata_dst *md_dst; + struct rcu_head rcu_head; +}; + +struct mlx5e_macsec_umr { + dma_addr_t dma_addr; + u8 ctx[MLX5_ST_SZ_BYTES(macsec_aso)]; + u32 mkey; +}; + +struct mlx5e_macsec_aso { + /* ASO */ + struct mlx5_aso *maso; + /* Protects macsec ASO */ + struct mutex aso_lock; + /* UMR */ + struct mlx5e_macsec_umr *umr; + + u32 pdn; +}; + +static const struct rhashtable_params rhash_sci = { + .key_len = sizeof_field(struct mlx5e_macsec_sa, sci), + .key_offset = offsetof(struct mlx5e_macsec_sa, sci), + .head_offset = offsetof(struct mlx5e_macsec_sa, hash), + .automatic_shrinking = true, + .min_size = 1, +}; + +struct mlx5e_macsec_device { + const struct net_device *netdev; + struct mlx5e_macsec_sa *tx_sa[MACSEC_NUM_AN]; + struct list_head macsec_rx_sc_list_head; + unsigned char *dev_addr; + struct list_head macsec_device_list_element; +}; + +struct mlx5e_macsec { + struct list_head macsec_device_list_head; + int num_of_devices; + struct mlx5e_macsec_fs *macsec_fs; + struct mutex lock; /* Protects mlx5e_macsec internal contexts */ + + /* Tx sci -> fs id mapping handling */ + struct rhashtable sci_hash; /* sci -> mlx5e_macsec_sa */ + + /* Rx fs_id -> rx_sc mapping */ + struct xarray sc_xarray; + + struct mlx5_core_dev *mdev; + + /* Stats manage */ + struct mlx5e_macsec_stats stats; + + /* ASO */ + struct mlx5e_macsec_aso aso; + + struct notifier_block nb; + struct workqueue_struct *wq; +}; + +struct mlx5_macsec_obj_attrs { + u32 aso_pdn; + u32 next_pn; + __be64 sci; + u32 enc_key_id; + bool encrypt; + struct mlx5e_macsec_epn_state epn_state; + salt_t salt; + __be32 ssci; + bool replay_protect; + u32 replay_window; +}; + +struct mlx5_aso_ctrl_param { + u8 data_mask_mode; + u8 condition_0_operand; + u8 condition_1_operand; + u8 condition_0_offset; + u8 condition_1_offset; + u8 data_offset; + u8 condition_operand; + u32 condition_0_data; + u32 condition_0_mask; + u32 condition_1_data; + u32 condition_1_mask; + u64 bitwise_data; + u64 data_mask; +}; + +static int mlx5e_macsec_aso_reg_mr(struct mlx5_core_dev *mdev, struct mlx5e_macsec_aso *aso) +{ + struct mlx5e_macsec_umr *umr; + struct device *dma_device; + dma_addr_t dma_addr; + int err; + + umr = kzalloc(sizeof(*umr), GFP_KERNEL); + if (!umr) { + err = -ENOMEM; + return err; + } + + dma_device = &mdev->pdev->dev; + dma_addr = dma_map_single(dma_device, umr->ctx, sizeof(umr->ctx), DMA_BIDIRECTIONAL); + err = dma_mapping_error(dma_device, dma_addr); + if (err) { + mlx5_core_err(mdev, "Can't map dma device, err=%d\n", err); + goto out_dma; + } + + err = mlx5e_create_mkey(mdev, aso->pdn, &umr->mkey); + if (err) { + mlx5_core_err(mdev, "Can't create mkey, err=%d\n", err); + goto out_mkey; + } + + umr->dma_addr = dma_addr; + + aso->umr = umr; + + return 0; + +out_mkey: + dma_unmap_single(dma_device, dma_addr, sizeof(umr->ctx), DMA_BIDIRECTIONAL); +out_dma: + kfree(umr); + return err; +} + +static void mlx5e_macsec_aso_dereg_mr(struct mlx5_core_dev *mdev, struct mlx5e_macsec_aso *aso) +{ + struct mlx5e_macsec_umr *umr = aso->umr; + + mlx5_core_destroy_mkey(mdev, umr->mkey); + dma_unmap_single(&mdev->pdev->dev, umr->dma_addr, sizeof(umr->ctx), DMA_BIDIRECTIONAL); + kfree(umr); +} + +static int macsec_set_replay_protection(struct mlx5_macsec_obj_attrs *attrs, void *aso_ctx) +{ + u8 window_sz; + + if (!attrs->replay_protect) + return 0; + + switch (attrs->replay_window) { + case 256: + window_sz = MLX5_MACSEC_ASO_REPLAY_WIN_256BIT; + break; + case 128: + window_sz = MLX5_MACSEC_ASO_REPLAY_WIN_128BIT; + break; + case 64: + window_sz = MLX5_MACSEC_ASO_REPLAY_WIN_64BIT; + break; + case 32: + window_sz = MLX5_MACSEC_ASO_REPLAY_WIN_32BIT; + break; + default: + return -EINVAL; + } + MLX5_SET(macsec_aso, aso_ctx, window_size, window_sz); + MLX5_SET(macsec_aso, aso_ctx, mode, MLX5_MACSEC_ASO_REPLAY_PROTECTION); + + return 0; +} + +static int mlx5e_macsec_create_object(struct mlx5_core_dev *mdev, + struct mlx5_macsec_obj_attrs *attrs, + bool is_tx, + u32 *macsec_obj_id) +{ + u32 in[MLX5_ST_SZ_DW(create_macsec_obj_in)] = {}; + u32 out[MLX5_ST_SZ_DW(general_obj_out_cmd_hdr)]; + void *aso_ctx; + void *obj; + int err; + + obj = MLX5_ADDR_OF(create_macsec_obj_in, in, macsec_object); + aso_ctx = MLX5_ADDR_OF(macsec_offload_obj, obj, macsec_aso); + + MLX5_SET(macsec_offload_obj, obj, confidentiality_en, attrs->encrypt); + MLX5_SET(macsec_offload_obj, obj, dekn, attrs->enc_key_id); + MLX5_SET(macsec_offload_obj, obj, aso_return_reg, MLX5_MACSEC_ASO_REG_C_4_5); + MLX5_SET(macsec_offload_obj, obj, macsec_aso_access_pd, attrs->aso_pdn); + MLX5_SET(macsec_aso, aso_ctx, mode_parameter, attrs->next_pn); + + /* Epn */ + if (attrs->epn_state.epn_enabled) { + void *salt_p; + int i; + + MLX5_SET(macsec_aso, aso_ctx, epn_event_arm, 1); + MLX5_SET(macsec_offload_obj, obj, epn_en, 1); + MLX5_SET(macsec_offload_obj, obj, epn_msb, attrs->epn_state.epn_msb); + MLX5_SET(macsec_offload_obj, obj, epn_overlap, attrs->epn_state.overlap); + MLX5_SET64(macsec_offload_obj, obj, sci, (__force u64)attrs->ssci); + salt_p = MLX5_ADDR_OF(macsec_offload_obj, obj, salt); + for (i = 0; i < 3 ; i++) + memcpy((u32 *)salt_p + i, &attrs->salt.bytes[4 * (2 - i)], 4); + } else { + MLX5_SET64(macsec_offload_obj, obj, sci, (__force u64)(attrs->sci)); + } + + MLX5_SET(macsec_aso, aso_ctx, valid, 0x1); + if (is_tx) { + MLX5_SET(macsec_aso, aso_ctx, mode, MLX5_MACSEC_ASO_INC_SN); + } else { + err = macsec_set_replay_protection(attrs, aso_ctx); + if (err) + return err; + } + + /* general object fields set */ + MLX5_SET(general_obj_in_cmd_hdr, in, opcode, MLX5_CMD_OP_CREATE_GENERAL_OBJECT); + MLX5_SET(general_obj_in_cmd_hdr, in, obj_type, MLX5_GENERAL_OBJECT_TYPES_MACSEC); + + err = mlx5_cmd_exec(mdev, in, sizeof(in), out, sizeof(out)); + if (err) { + mlx5_core_err(mdev, + "MACsec offload: Failed to create MACsec object (err = %d)\n", + err); + return err; + } + + *macsec_obj_id = MLX5_GET(general_obj_out_cmd_hdr, out, obj_id); + + return err; +} + +static void mlx5e_macsec_destroy_object(struct mlx5_core_dev *mdev, u32 macsec_obj_id) +{ + u32 in[MLX5_ST_SZ_DW(general_obj_in_cmd_hdr)] = {}; + u32 out[MLX5_ST_SZ_DW(general_obj_out_cmd_hdr)]; + + MLX5_SET(general_obj_in_cmd_hdr, in, opcode, MLX5_CMD_OP_DESTROY_GENERAL_OBJECT); + MLX5_SET(general_obj_in_cmd_hdr, in, obj_type, MLX5_GENERAL_OBJECT_TYPES_MACSEC); + MLX5_SET(general_obj_in_cmd_hdr, in, obj_id, macsec_obj_id); + + mlx5_cmd_exec(mdev, in, sizeof(in), out, sizeof(out)); +} + +static void mlx5e_macsec_cleanup_sa(struct mlx5e_macsec *macsec, + struct mlx5e_macsec_sa *sa, + bool is_tx) +{ + int action = (is_tx) ? MLX5_ACCEL_MACSEC_ACTION_ENCRYPT : + MLX5_ACCEL_MACSEC_ACTION_DECRYPT; + + if ((is_tx) && sa->fs_id) { + /* Make sure ongoing datapath readers sees a valid SA */ + rhashtable_remove_fast(&macsec->sci_hash, &sa->hash, rhash_sci); + sa->fs_id = 0; + } + + if (!sa->macsec_rule) + return; + + mlx5e_macsec_fs_del_rule(macsec->macsec_fs, sa->macsec_rule, action); + mlx5e_macsec_destroy_object(macsec->mdev, sa->macsec_obj_id); + sa->macsec_rule = NULL; +} + +static int mlx5e_macsec_init_sa(struct macsec_context *ctx, + struct mlx5e_macsec_sa *sa, + bool encrypt, + bool is_tx) +{ + struct mlx5e_priv *priv = netdev_priv(ctx->netdev); + struct mlx5e_macsec *macsec = priv->macsec; + struct mlx5_macsec_rule_attrs rule_attrs; + struct mlx5_core_dev *mdev = priv->mdev; + struct mlx5_macsec_obj_attrs obj_attrs; + union mlx5e_macsec_rule *macsec_rule; + struct macsec_key *key; + int err; + + obj_attrs.next_pn = sa->next_pn; + obj_attrs.sci = cpu_to_be64((__force u64)sa->sci); + obj_attrs.enc_key_id = sa->enc_key_id; + obj_attrs.encrypt = encrypt; + obj_attrs.aso_pdn = macsec->aso.pdn; + obj_attrs.epn_state = sa->epn_state; + + if (is_tx) { + obj_attrs.ssci = cpu_to_be32((__force u32)ctx->sa.tx_sa->ssci); + key = &ctx->sa.tx_sa->key; + } else { + obj_attrs.ssci = cpu_to_be32((__force u32)ctx->sa.rx_sa->ssci); + key = &ctx->sa.rx_sa->key; + } + + memcpy(&obj_attrs.salt, &key->salt, sizeof(key->salt)); + obj_attrs.replay_window = ctx->secy->replay_window; + obj_attrs.replay_protect = ctx->secy->replay_protect; + + err = mlx5e_macsec_create_object(mdev, &obj_attrs, is_tx, &sa->macsec_obj_id); + if (err) + return err; + + rule_attrs.macsec_obj_id = sa->macsec_obj_id; + rule_attrs.sci = sa->sci; + rule_attrs.assoc_num = sa->assoc_num; + rule_attrs.action = (is_tx) ? MLX5_ACCEL_MACSEC_ACTION_ENCRYPT : + MLX5_ACCEL_MACSEC_ACTION_DECRYPT; + + macsec_rule = mlx5e_macsec_fs_add_rule(macsec->macsec_fs, ctx, &rule_attrs, &sa->fs_id); + if (!macsec_rule) { + err = -ENOMEM; + goto destroy_macsec_object; + } + + sa->macsec_rule = macsec_rule; + + if (is_tx) { + err = rhashtable_insert_fast(&macsec->sci_hash, &sa->hash, rhash_sci); + if (err) + goto destroy_macsec_object_and_rule; + } + + return 0; + +destroy_macsec_object_and_rule: + mlx5e_macsec_cleanup_sa(macsec, sa, is_tx); +destroy_macsec_object: + mlx5e_macsec_destroy_object(mdev, sa->macsec_obj_id); + + return err; +} + +static struct mlx5e_macsec_rx_sc * +mlx5e_macsec_get_rx_sc_from_sc_list(const struct list_head *list, sci_t sci) +{ + struct mlx5e_macsec_rx_sc *iter; + + list_for_each_entry_rcu(iter, list, rx_sc_list_element) { + if (iter->sci == sci) + return iter; + } + + return NULL; +} + +static int mlx5e_macsec_update_rx_sa(struct mlx5e_macsec *macsec, + struct mlx5e_macsec_sa *rx_sa, + bool active) +{ + struct mlx5_core_dev *mdev = macsec->mdev; + struct mlx5_macsec_obj_attrs attrs; + int err = 0; + + if (rx_sa->active != active) + return 0; + + rx_sa->active = active; + if (!active) { + mlx5e_macsec_cleanup_sa(macsec, rx_sa, false); + return 0; + } + + attrs.sci = rx_sa->sci; + attrs.enc_key_id = rx_sa->enc_key_id; + err = mlx5e_macsec_create_object(mdev, &attrs, false, &rx_sa->macsec_obj_id); + if (err) + return err; + + return 0; +} + +static bool mlx5e_macsec_secy_features_validate(struct macsec_context *ctx) +{ + const struct net_device *netdev = ctx->netdev; + const struct macsec_secy *secy = ctx->secy; + + if (secy->validate_frames != MACSEC_VALIDATE_STRICT) { + netdev_err(netdev, + "MACsec offload is supported only when validate_frame is in strict mode\n"); + return false; + } + + if (secy->icv_len != MACSEC_DEFAULT_ICV_LEN) { + netdev_err(netdev, "MACsec offload is supported only when icv_len is %d\n", + MACSEC_DEFAULT_ICV_LEN); + return false; + } + + if (!secy->protect_frames) { + netdev_err(netdev, + "MACsec offload is supported only when protect_frames is set\n"); + return false; + } + + return true; +} + +static struct mlx5e_macsec_device * +mlx5e_macsec_get_macsec_device_context(const struct mlx5e_macsec *macsec, + const struct macsec_context *ctx) +{ + struct mlx5e_macsec_device *iter; + const struct list_head *list; + + list = &macsec->macsec_device_list_head; + list_for_each_entry_rcu(iter, list, macsec_device_list_element) { + if (iter->netdev == ctx->secy->netdev) + return iter; + } + + return NULL; +} + +static void update_macsec_epn(struct mlx5e_macsec_sa *sa, const struct macsec_key *key, + const pn_t *next_pn_halves) +{ + struct mlx5e_macsec_epn_state *epn_state = &sa->epn_state; + + sa->salt = key->salt; + epn_state->epn_enabled = 1; + epn_state->epn_msb = next_pn_halves->upper; + epn_state->overlap = next_pn_halves->lower < MLX5_MACSEC_EPN_SCOPE_MID ? 0 : 1; +} + +static int mlx5e_macsec_add_txsa(struct macsec_context *ctx) +{ + const struct macsec_tx_sc *tx_sc = &ctx->secy->tx_sc; + const struct macsec_tx_sa *ctx_tx_sa = ctx->sa.tx_sa; + struct mlx5e_priv *priv = netdev_priv(ctx->netdev); + const struct macsec_secy *secy = ctx->secy; + struct mlx5e_macsec_device *macsec_device; + struct mlx5_core_dev *mdev = priv->mdev; + u8 assoc_num = ctx->sa.assoc_num; + struct mlx5e_macsec_sa *tx_sa; + struct mlx5e_macsec *macsec; + int err = 0; + + mutex_lock(&priv->macsec->lock); + + macsec = priv->macsec; + macsec_device = mlx5e_macsec_get_macsec_device_context(macsec, ctx); + if (!macsec_device) { + netdev_err(ctx->netdev, "MACsec offload: Failed to find device context\n"); + err = -EEXIST; + goto out; + } + + if (macsec_device->tx_sa[assoc_num]) { + netdev_err(ctx->netdev, "MACsec offload tx_sa: %d already exist\n", assoc_num); + err = -EEXIST; + goto out; + } + + tx_sa = kzalloc(sizeof(*tx_sa), GFP_KERNEL); + if (!tx_sa) { + err = -ENOMEM; + goto out; + } + + tx_sa->active = ctx_tx_sa->active; + tx_sa->next_pn = ctx_tx_sa->next_pn_halves.lower; + tx_sa->sci = secy->sci; + tx_sa->assoc_num = assoc_num; + + if (secy->xpn) + update_macsec_epn(tx_sa, &ctx_tx_sa->key, &ctx_tx_sa->next_pn_halves); + + err = mlx5_create_encryption_key(mdev, ctx->sa.key, secy->key_len, + MLX5_ACCEL_OBJ_MACSEC_KEY, + &tx_sa->enc_key_id); + if (err) + goto destroy_sa; + + macsec_device->tx_sa[assoc_num] = tx_sa; + if (!secy->operational || + assoc_num != tx_sc->encoding_sa || + !tx_sa->active) + goto out; + + err = mlx5e_macsec_init_sa(ctx, tx_sa, tx_sc->encrypt, true); + if (err) + goto destroy_encryption_key; + + mutex_unlock(&macsec->lock); + + return 0; + +destroy_encryption_key: + macsec_device->tx_sa[assoc_num] = NULL; + mlx5_destroy_encryption_key(mdev, tx_sa->enc_key_id); +destroy_sa: + kfree(tx_sa); +out: + mutex_unlock(&macsec->lock); + + return err; +} + +static int mlx5e_macsec_upd_txsa(struct macsec_context *ctx) +{ + const struct macsec_tx_sc *tx_sc = &ctx->secy->tx_sc; + const struct macsec_tx_sa *ctx_tx_sa = ctx->sa.tx_sa; + struct mlx5e_priv *priv = netdev_priv(ctx->netdev); + struct mlx5e_macsec_device *macsec_device; + u8 assoc_num = ctx->sa.assoc_num; + struct mlx5e_macsec_sa *tx_sa; + struct mlx5e_macsec *macsec; + struct net_device *netdev; + int err = 0; + + mutex_lock(&priv->macsec->lock); + + macsec = priv->macsec; + netdev = ctx->netdev; + macsec_device = mlx5e_macsec_get_macsec_device_context(macsec, ctx); + if (!macsec_device) { + netdev_err(netdev, "MACsec offload: Failed to find device context\n"); + err = -EINVAL; + goto out; + } + + tx_sa = macsec_device->tx_sa[assoc_num]; + if (!tx_sa) { + netdev_err(netdev, "MACsec offload: TX sa 0x%x doesn't exist\n", assoc_num); + err = -EEXIST; + goto out; + } + + if (tx_sa->next_pn != ctx_tx_sa->next_pn_halves.lower) { + netdev_err(netdev, "MACsec offload: update TX sa %d PN isn't supported\n", + assoc_num); + err = -EINVAL; + goto out; + } + + if (tx_sa->active == ctx_tx_sa->active) + goto out; + + if (tx_sa->assoc_num != tx_sc->encoding_sa) + goto out; + + if (ctx_tx_sa->active) { + err = mlx5e_macsec_init_sa(ctx, tx_sa, tx_sc->encrypt, true); + if (err) + goto out; + } else { + if (!tx_sa->macsec_rule) { + err = -EINVAL; + goto out; + } + + mlx5e_macsec_cleanup_sa(macsec, tx_sa, true); + } + + tx_sa->active = ctx_tx_sa->active; +out: + mutex_unlock(&macsec->lock); + + return err; +} + +static int mlx5e_macsec_del_txsa(struct macsec_context *ctx) +{ + struct mlx5e_priv *priv = netdev_priv(ctx->netdev); + struct mlx5e_macsec_device *macsec_device; + u8 assoc_num = ctx->sa.assoc_num; + struct mlx5e_macsec_sa *tx_sa; + struct mlx5e_macsec *macsec; + int err = 0; + + mutex_lock(&priv->macsec->lock); + macsec = priv->macsec; + macsec_device = mlx5e_macsec_get_macsec_device_context(macsec, ctx); + if (!macsec_device) { + netdev_err(ctx->netdev, "MACsec offload: Failed to find device context\n"); + err = -EINVAL; + goto out; + } + + tx_sa = macsec_device->tx_sa[assoc_num]; + if (!tx_sa) { + netdev_err(ctx->netdev, "MACsec offload: TX sa 0x%x doesn't exist\n", assoc_num); + err = -EEXIST; + goto out; + } + + mlx5e_macsec_cleanup_sa(macsec, tx_sa, true); + mlx5_destroy_encryption_key(macsec->mdev, tx_sa->enc_key_id); + kfree_rcu(tx_sa); + macsec_device->tx_sa[assoc_num] = NULL; + +out: + mutex_unlock(&macsec->lock); + + return err; +} + +static u32 mlx5e_macsec_get_sa_from_hashtable(struct rhashtable *sci_hash, sci_t *sci) +{ + struct mlx5e_macsec_sa *macsec_sa; + u32 fs_id = 0; + + rcu_read_lock(); + macsec_sa = rhashtable_lookup(sci_hash, sci, rhash_sci); + if (macsec_sa) + fs_id = macsec_sa->fs_id; + rcu_read_unlock(); + + return fs_id; +} + +static int mlx5e_macsec_add_rxsc(struct macsec_context *ctx) +{ + struct mlx5e_macsec_rx_sc_xarray_element *sc_xarray_element; + struct mlx5e_priv *priv = netdev_priv(ctx->netdev); + const struct macsec_rx_sc *ctx_rx_sc = ctx->rx_sc; + struct mlx5e_macsec_device *macsec_device; + struct mlx5e_macsec_rx_sc *rx_sc; + struct list_head *rx_sc_list; + struct mlx5e_macsec *macsec; + int err = 0; + + mutex_lock(&priv->macsec->lock); + macsec = priv->macsec; + macsec_device = mlx5e_macsec_get_macsec_device_context(macsec, ctx); + if (!macsec_device) { + netdev_err(ctx->netdev, "MACsec offload: Failed to find device context\n"); + err = -EINVAL; + goto out; + } + + rx_sc_list = &macsec_device->macsec_rx_sc_list_head; + rx_sc = mlx5e_macsec_get_rx_sc_from_sc_list(rx_sc_list, ctx_rx_sc->sci); + if (rx_sc) { + netdev_err(ctx->netdev, "MACsec offload: rx_sc (sci %lld) already exists\n", + ctx_rx_sc->sci); + err = -EEXIST; + goto out; + } + + rx_sc = kzalloc(sizeof(*rx_sc), GFP_KERNEL); + if (!rx_sc) { + err = -ENOMEM; + goto out; + } + + sc_xarray_element = kzalloc(sizeof(*sc_xarray_element), GFP_KERNEL); + if (!sc_xarray_element) { + err = -ENOMEM; + goto destroy_rx_sc; + } + + sc_xarray_element->rx_sc = rx_sc; + err = xa_alloc(&macsec->sc_xarray, &sc_xarray_element->fs_id, sc_xarray_element, + XA_LIMIT(1, USHRT_MAX), GFP_KERNEL); + if (err) + goto destroy_sc_xarray_elemenet; + + rx_sc->md_dst = metadata_dst_alloc(0, METADATA_MACSEC, GFP_KERNEL); + if (!rx_sc->md_dst) { + err = -ENOMEM; + goto erase_xa_alloc; + } + + rx_sc->sci = ctx_rx_sc->sci; + rx_sc->active = ctx_rx_sc->active; + list_add_rcu(&rx_sc->rx_sc_list_element, rx_sc_list); + + rx_sc->sc_xarray_element = sc_xarray_element; + rx_sc->md_dst->u.macsec_info.sci = rx_sc->sci; + mutex_unlock(&macsec->lock); + + return 0; + +erase_xa_alloc: + xa_erase(&macsec->sc_xarray, sc_xarray_element->fs_id); +destroy_sc_xarray_elemenet: + kfree(sc_xarray_element); +destroy_rx_sc: + kfree(rx_sc); + +out: + mutex_unlock(&macsec->lock); + + return err; +} + +static int mlx5e_macsec_upd_rxsc(struct macsec_context *ctx) +{ + struct mlx5e_priv *priv = netdev_priv(ctx->netdev); + const struct macsec_rx_sc *ctx_rx_sc = ctx->rx_sc; + struct mlx5e_macsec_device *macsec_device; + struct mlx5e_macsec_rx_sc *rx_sc; + struct mlx5e_macsec_sa *rx_sa; + struct mlx5e_macsec *macsec; + struct list_head *list; + int i; + int err = 0; + + mutex_lock(&priv->macsec->lock); + + macsec = priv->macsec; + macsec_device = mlx5e_macsec_get_macsec_device_context(macsec, ctx); + if (!macsec_device) { + netdev_err(ctx->netdev, "MACsec offload: Failed to find device context\n"); + err = -EINVAL; + goto out; + } + + list = &macsec_device->macsec_rx_sc_list_head; + rx_sc = mlx5e_macsec_get_rx_sc_from_sc_list(list, ctx_rx_sc->sci); + if (!rx_sc) { + err = -EINVAL; + goto out; + } + + rx_sc->active = ctx_rx_sc->active; + if (rx_sc->active == ctx_rx_sc->active) + goto out; + + for (i = 0; i < MACSEC_NUM_AN; ++i) { + rx_sa = rx_sc->rx_sa[i]; + if (!rx_sa) + continue; + + err = mlx5e_macsec_update_rx_sa(macsec, rx_sa, rx_sa->active && ctx_rx_sc->active); + if (err) + goto out; + } + +out: + mutex_unlock(&macsec->lock); + + return err; +} + +static int mlx5e_macsec_del_rxsc(struct macsec_context *ctx) +{ + struct mlx5e_priv *priv = netdev_priv(ctx->netdev); + struct mlx5e_macsec_device *macsec_device; + struct mlx5e_macsec_rx_sc *rx_sc; + struct mlx5e_macsec_sa *rx_sa; + struct mlx5e_macsec *macsec; + struct list_head *list; + int err = 0; + int i; + + mutex_lock(&priv->macsec->lock); + + macsec = priv->macsec; + macsec_device = mlx5e_macsec_get_macsec_device_context(macsec, ctx); + if (!macsec_device) { + netdev_err(ctx->netdev, "MACsec offload: Failed to find device context\n"); + err = -EINVAL; + goto out; + } + + list = &macsec_device->macsec_rx_sc_list_head; + rx_sc = mlx5e_macsec_get_rx_sc_from_sc_list(list, ctx->rx_sc->sci); + if (!rx_sc) { + netdev_err(ctx->netdev, + "MACsec offload rx_sc sci %lld doesn't exist\n", + ctx->sa.rx_sa->sc->sci); + err = -EINVAL; + goto out; + } + + for (i = 0; i < MACSEC_NUM_AN; ++i) { + rx_sa = rx_sc->rx_sa[i]; + if (!rx_sa) + continue; + + mlx5e_macsec_cleanup_sa(macsec, rx_sa, false); + mlx5_destroy_encryption_key(macsec->mdev, rx_sa->enc_key_id); + + kfree(rx_sa); + rx_sc->rx_sa[i] = NULL; + } + +/* + * At this point the relevant MACsec offload Rx rule already removed at + * mlx5e_macsec_cleanup_sa need to wait for datapath to finish current + * Rx related data propagating using xa_erase which uses rcu to sync, + * once fs_id is erased then this rx_sc is hidden from datapath. + */ + list_del_rcu(&rx_sc->rx_sc_list_element); + xa_erase(&macsec->sc_xarray, rx_sc->sc_xarray_element->fs_id); + metadata_dst_free(rx_sc->md_dst); + kfree(rx_sc->sc_xarray_element); + + kfree_rcu(rx_sc); + +out: + mutex_unlock(&macsec->lock); + + return err; +} + +static int mlx5e_macsec_add_rxsa(struct macsec_context *ctx) +{ + const struct macsec_rx_sa *ctx_rx_sa = ctx->sa.rx_sa; + struct mlx5e_priv *priv = netdev_priv(ctx->netdev); + struct mlx5e_macsec_device *macsec_device; + struct mlx5_core_dev *mdev = priv->mdev; + u8 assoc_num = ctx->sa.assoc_num; + struct mlx5e_macsec_rx_sc *rx_sc; + sci_t sci = ctx_rx_sa->sc->sci; + struct mlx5e_macsec_sa *rx_sa; + struct mlx5e_macsec *macsec; + struct list_head *list; + int err = 0; + + mutex_lock(&priv->macsec->lock); + + macsec = priv->macsec; + macsec_device = mlx5e_macsec_get_macsec_device_context(macsec, ctx); + if (!macsec_device) { + netdev_err(ctx->netdev, "MACsec offload: Failed to find device context\n"); + err = -EINVAL; + goto out; + } + + list = &macsec_device->macsec_rx_sc_list_head; + rx_sc = mlx5e_macsec_get_rx_sc_from_sc_list(list, sci); + if (!rx_sc) { + netdev_err(ctx->netdev, + "MACsec offload rx_sc sci %lld doesn't exist\n", + ctx->sa.rx_sa->sc->sci); + err = -EINVAL; + goto out; + } + + if (rx_sc->rx_sa[assoc_num]) { + netdev_err(ctx->netdev, + "MACsec offload rx_sc sci %lld rx_sa %d already exist\n", + sci, assoc_num); + err = -EEXIST; + goto out; + } + + rx_sa = kzalloc(sizeof(*rx_sa), GFP_KERNEL); + if (!rx_sa) { + err = -ENOMEM; + goto out; + } + + rx_sa->active = ctx_rx_sa->active; + rx_sa->next_pn = ctx_rx_sa->next_pn; + rx_sa->sci = sci; + rx_sa->assoc_num = assoc_num; + rx_sa->fs_id = rx_sc->sc_xarray_element->fs_id; + + if (ctx->secy->xpn) + update_macsec_epn(rx_sa, &ctx_rx_sa->key, &ctx_rx_sa->next_pn_halves); + + err = mlx5_create_encryption_key(mdev, ctx->sa.key, ctx->secy->key_len, + MLX5_ACCEL_OBJ_MACSEC_KEY, + &rx_sa->enc_key_id); + if (err) + goto destroy_sa; + + rx_sc->rx_sa[assoc_num] = rx_sa; + if (!rx_sa->active) + goto out; + + //TODO - add support for both authentication and encryption flows + err = mlx5e_macsec_init_sa(ctx, rx_sa, true, false); + if (err) + goto destroy_encryption_key; + + goto out; + +destroy_encryption_key: + rx_sc->rx_sa[assoc_num] = NULL; + mlx5_destroy_encryption_key(mdev, rx_sa->enc_key_id); +destroy_sa: + kfree(rx_sa); +out: + mutex_unlock(&macsec->lock); + + return err; +} + +static int mlx5e_macsec_upd_rxsa(struct macsec_context *ctx) +{ + const struct macsec_rx_sa *ctx_rx_sa = ctx->sa.rx_sa; + struct mlx5e_priv *priv = netdev_priv(ctx->netdev); + struct mlx5e_macsec_device *macsec_device; + u8 assoc_num = ctx->sa.assoc_num; + struct mlx5e_macsec_rx_sc *rx_sc; + sci_t sci = ctx_rx_sa->sc->sci; + struct mlx5e_macsec_sa *rx_sa; + struct mlx5e_macsec *macsec; + struct list_head *list; + int err = 0; + + mutex_lock(&priv->macsec->lock); + + macsec = priv->macsec; + macsec_device = mlx5e_macsec_get_macsec_device_context(macsec, ctx); + if (!macsec_device) { + netdev_err(ctx->netdev, "MACsec offload: Failed to find device context\n"); + err = -EINVAL; + goto out; + } + + list = &macsec_device->macsec_rx_sc_list_head; + rx_sc = mlx5e_macsec_get_rx_sc_from_sc_list(list, sci); + if (!rx_sc) { + netdev_err(ctx->netdev, + "MACsec offload rx_sc sci %lld doesn't exist\n", + ctx->sa.rx_sa->sc->sci); + err = -EINVAL; + goto out; + } + + rx_sa = rx_sc->rx_sa[assoc_num]; + if (rx_sa) { + netdev_err(ctx->netdev, + "MACsec offload rx_sc sci %lld rx_sa %d already exist\n", + sci, assoc_num); + err = -EEXIST; + goto out; + } + + if (rx_sa->next_pn != ctx_rx_sa->next_pn_halves.lower) { + netdev_err(ctx->netdev, + "MACsec offload update RX sa %d PN isn't supported\n", + assoc_num); + err = -EINVAL; + goto out; + } + + err = mlx5e_macsec_update_rx_sa(macsec, rx_sa, ctx_rx_sa->active); +out: + mutex_unlock(&macsec->lock); + + return err; +} + +static int mlx5e_macsec_del_rxsa(struct macsec_context *ctx) +{ + struct mlx5e_priv *priv = netdev_priv(ctx->netdev); + struct mlx5e_macsec_device *macsec_device; + sci_t sci = ctx->sa.rx_sa->sc->sci; + struct mlx5e_macsec_rx_sc *rx_sc; + u8 assoc_num = ctx->sa.assoc_num; + struct mlx5e_macsec_sa *rx_sa; + struct mlx5e_macsec *macsec; + struct list_head *list; + int err = 0; + + mutex_lock(&priv->macsec->lock); + + macsec = priv->macsec; + macsec_device = mlx5e_macsec_get_macsec_device_context(macsec, ctx); + if (!macsec_device) { + netdev_err(ctx->netdev, "MACsec offload: Failed to find device context\n"); + err = -EINVAL; + goto out; + } + + list = &macsec_device->macsec_rx_sc_list_head; + rx_sc = mlx5e_macsec_get_rx_sc_from_sc_list(list, sci); + if (!rx_sc) { + netdev_err(ctx->netdev, + "MACsec offload rx_sc sci %lld doesn't exist\n", + ctx->sa.rx_sa->sc->sci); + err = -EINVAL; + goto out; + } + + rx_sa = rx_sc->rx_sa[assoc_num]; + if (rx_sa) { + netdev_err(ctx->netdev, + "MACsec offload rx_sc sci %lld rx_sa %d already exist\n", + sci, assoc_num); + err = -EEXIST; + goto out; + } + + mlx5e_macsec_cleanup_sa(macsec, rx_sa, false); + mlx5_destroy_encryption_key(macsec->mdev, rx_sa->enc_key_id); + kfree(rx_sa); + rx_sc->rx_sa[assoc_num] = NULL; + +out: + mutex_unlock(&macsec->lock); + + return err; +} + +static int mlx5e_macsec_add_secy(struct macsec_context *ctx) +{ + struct mlx5e_priv *priv = netdev_priv(ctx->netdev); + const struct net_device *dev = ctx->secy->netdev; + const struct net_device *netdev = ctx->netdev; + struct mlx5e_macsec_device *macsec_device; + struct mlx5e_macsec *macsec; + int err = 0; + + if (!mlx5e_macsec_secy_features_validate(ctx)) + return -EINVAL; + + mutex_lock(&priv->macsec->lock); + macsec = priv->macsec; + if (mlx5e_macsec_get_macsec_device_context(macsec, ctx)) { + netdev_err(netdev, "MACsec offload: MACsec net_device already exist\n"); + goto out; + } + + if (macsec->num_of_devices >= MLX5_MACSEC_NUM_OF_SUPPORTED_INTERFACES) { + netdev_err(netdev, "Currently, only %d MACsec offload devices can be set\n", + MLX5_MACSEC_NUM_OF_SUPPORTED_INTERFACES); + err = -EBUSY; + goto out; + } + + macsec_device = kzalloc(sizeof(*macsec_device), GFP_KERNEL); + if (!macsec_device) { + err = -ENOMEM; + goto out; + } + + macsec_device->dev_addr = kmemdup(dev->dev_addr, dev->addr_len, GFP_KERNEL); + if (!macsec_device->dev_addr) { + kfree(macsec_device); + err = -ENOMEM; + goto out; + } + + macsec_device->netdev = dev; + + INIT_LIST_HEAD_RCU(&macsec_device->macsec_rx_sc_list_head); + list_add_rcu(&macsec_device->macsec_device_list_element, &macsec->macsec_device_list_head); + + ++macsec->num_of_devices; +out: + mutex_unlock(&macsec->lock); + + return err; +} + +static int macsec_upd_secy_hw_address(struct macsec_context *ctx, + struct mlx5e_macsec_device *macsec_device) +{ + struct mlx5e_priv *priv = netdev_priv(ctx->netdev); + const struct net_device *dev = ctx->secy->netdev; + struct mlx5e_macsec *macsec = priv->macsec; + struct mlx5e_macsec_rx_sc *rx_sc, *tmp; + struct mlx5e_macsec_sa *rx_sa; + struct list_head *list; + int i, err = 0; + + + list = &macsec_device->macsec_rx_sc_list_head; + list_for_each_entry_safe(rx_sc, tmp, list, rx_sc_list_element) { + for (i = 0; i < MACSEC_NUM_AN; ++i) { + rx_sa = rx_sc->rx_sa[i]; + if (!rx_sa || !rx_sa->macsec_rule) + continue; + + mlx5e_macsec_cleanup_sa(macsec, rx_sa, false); + } + } + + list_for_each_entry_safe(rx_sc, tmp, list, rx_sc_list_element) { + for (i = 0; i < MACSEC_NUM_AN; ++i) { + rx_sa = rx_sc->rx_sa[i]; + if (!rx_sa) + continue; + + if (rx_sa->active) { + err = mlx5e_macsec_init_sa(ctx, rx_sa, false, false); + if (err) + goto out; + } + } + } + + memcpy(macsec_device->dev_addr, dev->dev_addr, dev->addr_len); +out: + return err; +} + +/* this function is called from 2 macsec ops functions: + * macsec_set_mac_address – MAC address was changed, therefore we need to destroy + * and create new Tx contexts(macsec object + steering). + * macsec_changelink – in this case the tx SC or SecY may be changed, therefore need to + * destroy Tx and Rx contexts(macsec object + steering) + */ +static int mlx5e_macsec_upd_secy(struct macsec_context *ctx) +{ + const struct macsec_tx_sc *tx_sc = &ctx->secy->tx_sc; + struct mlx5e_priv *priv = netdev_priv(ctx->netdev); + const struct net_device *dev = ctx->secy->netdev; + struct mlx5e_macsec_device *macsec_device; + struct mlx5e_macsec_sa *tx_sa; + struct mlx5e_macsec *macsec; + int i, err = 0; + + if (!mlx5e_macsec_secy_features_validate(ctx)) + return -EINVAL; + + mutex_lock(&priv->macsec->lock); + + macsec = priv->macsec; + macsec_device = mlx5e_macsec_get_macsec_device_context(macsec, ctx); + if (!macsec_device) { + netdev_err(ctx->netdev, "MACsec offload: Failed to find device context\n"); + err = -EINVAL; + goto out; + } + + /* if the dev_addr hasn't change, it mean the callback is from macsec_changelink */ + if (!memcmp(macsec_device->dev_addr, dev->dev_addr, dev->addr_len)) { + err = macsec_upd_secy_hw_address(ctx, macsec_device); + if (err) + goto out; + } + + for (i = 0; i < MACSEC_NUM_AN; ++i) { + tx_sa = macsec_device->tx_sa[i]; + if (!tx_sa) + continue; + + mlx5e_macsec_cleanup_sa(macsec, tx_sa, true); + } + + for (i = 0; i < MACSEC_NUM_AN; ++i) { + tx_sa = macsec_device->tx_sa[i]; + if (!tx_sa) + continue; + + if (tx_sa->assoc_num == tx_sc->encoding_sa && tx_sa->active) { + err = mlx5e_macsec_init_sa(ctx, tx_sa, tx_sc->encrypt, true); + if (err) + goto out; + } + } + +out: + mutex_unlock(&macsec->lock); + + return err; +} + +static int mlx5e_macsec_del_secy(struct macsec_context *ctx) +{ + struct mlx5e_priv *priv = netdev_priv(ctx->netdev); + struct mlx5e_macsec_device *macsec_device; + struct mlx5e_macsec_rx_sc *rx_sc, *tmp; + struct mlx5e_macsec_sa *rx_sa; + struct mlx5e_macsec_sa *tx_sa; + struct mlx5e_macsec *macsec; + struct list_head *list; + int err = 0; + int i; + + mutex_lock(&priv->macsec->lock); + macsec = priv->macsec; + macsec_device = mlx5e_macsec_get_macsec_device_context(macsec, ctx); + if (!macsec_device) { + netdev_err(ctx->netdev, "MACsec offload: Failed to find device context\n"); + err = -EINVAL; + + goto out; + } + + for (i = 0; i < MACSEC_NUM_AN; ++i) { + tx_sa = macsec_device->tx_sa[i]; + if (!tx_sa) + continue; + + mlx5e_macsec_cleanup_sa(macsec, tx_sa, true); + mlx5_destroy_encryption_key(macsec->mdev, tx_sa->enc_key_id); + kfree(tx_sa); + macsec_device->tx_sa[i] = NULL; + } + + list = &macsec_device->macsec_rx_sc_list_head; + list_for_each_entry_safe(rx_sc, tmp, list, rx_sc_list_element) { + for (i = 0; i < MACSEC_NUM_AN; ++i) { + rx_sa = rx_sc->rx_sa[i]; + if (!rx_sa) + continue; + + mlx5e_macsec_cleanup_sa(macsec, rx_sa, false); + mlx5_destroy_encryption_key(macsec->mdev, rx_sa->enc_key_id); + kfree(rx_sa); + rx_sc->rx_sa[i] = NULL; + } + + list_del_rcu(&rx_sc->rx_sc_list_element); + + kfree_rcu(rx_sc); + } + + kfree(macsec_device->dev_addr); + macsec_device->dev_addr = NULL; + + list_del_rcu(&macsec_device->macsec_device_list_element); + --macsec->num_of_devices; + +out: + mutex_unlock(&macsec->lock); + + return err; +} + +static void macsec_build_accel_attrs(struct mlx5e_macsec_sa *sa, + struct mlx5_macsec_obj_attrs *attrs) +{ + attrs->epn_state.epn_msb = sa->epn_state.epn_msb; + attrs->epn_state.overlap = sa->epn_state.overlap; +} + +static void macsec_aso_build_wqe_ctrl_seg(struct mlx5e_macsec_aso *macsec_aso, + struct mlx5_wqe_aso_ctrl_seg *aso_ctrl, + struct mlx5_aso_ctrl_param *param) +{ + memset(aso_ctrl, 0, sizeof(*aso_ctrl)); + if (macsec_aso->umr->dma_addr) { + aso_ctrl->va_l = cpu_to_be32(macsec_aso->umr->dma_addr | ASO_CTRL_READ_EN); + aso_ctrl->va_h = cpu_to_be32((u64)macsec_aso->umr->dma_addr >> 32); + aso_ctrl->l_key = cpu_to_be32(macsec_aso->umr->mkey); + } + + if (!param) + return; + + aso_ctrl->data_mask_mode = param->data_mask_mode << 6; + aso_ctrl->condition_1_0_operand = param->condition_1_operand | + param->condition_0_operand << 4; + aso_ctrl->condition_1_0_offset = param->condition_1_offset | + param->condition_0_offset << 4; + aso_ctrl->data_offset_condition_operand = param->data_offset | + param->condition_operand << 6; + aso_ctrl->condition_0_data = cpu_to_be32(param->condition_0_data); + aso_ctrl->condition_0_mask = cpu_to_be32(param->condition_0_mask); + aso_ctrl->condition_1_data = cpu_to_be32(param->condition_1_data); + aso_ctrl->condition_1_mask = cpu_to_be32(param->condition_1_mask); + aso_ctrl->bitwise_data = cpu_to_be64(param->bitwise_data); + aso_ctrl->data_mask = cpu_to_be64(param->data_mask); +} + +static int mlx5e_macsec_modify_obj(struct mlx5_core_dev *mdev, struct mlx5_macsec_obj_attrs *attrs, + u32 macsec_id) +{ + u32 in[MLX5_ST_SZ_DW(modify_macsec_obj_in)] = {}; + u32 out[MLX5_ST_SZ_DW(query_macsec_obj_out)]; + u64 modify_field_select = 0; + void *obj; + int err; + + /* General object fields set */ + MLX5_SET(general_obj_in_cmd_hdr, in, opcode, MLX5_CMD_OP_QUERY_GENERAL_OBJECT); + MLX5_SET(general_obj_in_cmd_hdr, in, obj_type, MLX5_GENERAL_OBJECT_TYPES_MACSEC); + MLX5_SET(general_obj_in_cmd_hdr, in, obj_id, macsec_id); + err = mlx5_cmd_exec(mdev, in, sizeof(in), out, sizeof(out)); + if (err) { + mlx5_core_err(mdev, "Query MACsec object failed (Object id %d), err = %d\n", + macsec_id, err); + return err; + } + + obj = MLX5_ADDR_OF(query_macsec_obj_out, out, macsec_object); + modify_field_select = MLX5_GET64(macsec_offload_obj, obj, modify_field_select); + + /* EPN */ + if (!(modify_field_select & MLX5_MODIFY_MACSEC_BITMASK_EPN_OVERLAP) || + !(modify_field_select & MLX5_MODIFY_MACSEC_BITMASK_EPN_MSB)) { + mlx5_core_dbg(mdev, "MACsec object field is not modifiable (Object id %d)\n", + macsec_id); + return -EOPNOTSUPP; + } + + obj = MLX5_ADDR_OF(modify_macsec_obj_in, in, macsec_object); + MLX5_SET64(macsec_offload_obj, obj, modify_field_select, + MLX5_MODIFY_MACSEC_BITMASK_EPN_OVERLAP | MLX5_MODIFY_MACSEC_BITMASK_EPN_MSB); + MLX5_SET(macsec_offload_obj, obj, epn_msb, attrs->epn_state.epn_msb); + MLX5_SET(macsec_offload_obj, obj, epn_overlap, attrs->epn_state.overlap); + + /* General object fields set */ + MLX5_SET(general_obj_in_cmd_hdr, in, opcode, MLX5_CMD_OP_MODIFY_GENERAL_OBJECT); + + return mlx5_cmd_exec(mdev, in, sizeof(in), out, sizeof(out)); +} + +static void macsec_aso_build_ctrl(struct mlx5e_macsec_aso *aso, + struct mlx5_wqe_aso_ctrl_seg *aso_ctrl, + struct mlx5e_macsec_aso_in *in) +{ + struct mlx5_aso_ctrl_param param = {}; + + param.data_mask_mode = MLX5_ASO_DATA_MASK_MODE_BITWISE_64BIT; + param.condition_0_operand = MLX5_ASO_ALWAYS_TRUE; + param.condition_1_operand = MLX5_ASO_ALWAYS_TRUE; + if (in->mode == MLX5_MACSEC_EPN) { + param.data_offset = MLX5_MACSEC_ASO_REMOVE_FLOW_PKT_CNT_OFFSET; + param.bitwise_data = BIT_ULL(54); + param.data_mask = param.bitwise_data; + } + macsec_aso_build_wqe_ctrl_seg(aso, aso_ctrl, ¶m); +} + +static int macsec_aso_set_arm_event(struct mlx5_core_dev *mdev, struct mlx5e_macsec *macsec, + struct mlx5e_macsec_aso_in *in) +{ + struct mlx5e_macsec_aso *aso; + struct mlx5_aso_wqe *aso_wqe; + struct mlx5_aso *maso; + int err; + + aso = &macsec->aso; + maso = aso->maso; + + mutex_lock(&aso->aso_lock); + aso_wqe = mlx5_aso_get_wqe(maso); + mlx5_aso_build_wqe(maso, MLX5_MACSEC_ASO_DS_CNT, aso_wqe, in->obj_id, + MLX5_ACCESS_ASO_OPC_MOD_MACSEC); + macsec_aso_build_ctrl(aso, &aso_wqe->aso_ctrl, in); + mlx5_aso_post_wqe(maso, false, &aso_wqe->ctrl); + err = mlx5_aso_poll_cq(maso, false, 10); + mutex_unlock(&aso->aso_lock); + + return err; +} + +static int macsec_aso_query(struct mlx5_core_dev *mdev, struct mlx5e_macsec *macsec, + struct mlx5e_macsec_aso_in *in, struct mlx5e_macsec_aso_out *out) +{ + struct mlx5e_macsec_aso *aso; + struct mlx5_aso_wqe *aso_wqe; + struct mlx5_aso *maso; + int err; + + aso = &macsec->aso; + maso = aso->maso; + + mutex_lock(&aso->aso_lock); + + aso_wqe = mlx5_aso_get_wqe(maso); + mlx5_aso_build_wqe(maso, MLX5_MACSEC_ASO_DS_CNT, aso_wqe, in->obj_id, + MLX5_ACCESS_ASO_OPC_MOD_MACSEC); + macsec_aso_build_wqe_ctrl_seg(aso, &aso_wqe->aso_ctrl, NULL); + + mlx5_aso_post_wqe(maso, false, &aso_wqe->ctrl); + err = mlx5_aso_poll_cq(maso, false, 10); + if (err) + goto err_out; + + if (MLX5_GET(macsec_aso, aso->umr->ctx, epn_event_arm)) + out->event_arm |= MLX5E_ASO_EPN_ARM; + + out->mode_param = MLX5_GET(macsec_aso, aso->umr->ctx, mode_parameter); + +err_out: + mutex_unlock(&aso->aso_lock); + return err; +} + +static struct mlx5e_macsec_sa *get_macsec_tx_sa_from_obj_id(const struct mlx5e_macsec *macsec, + const u32 obj_id) +{ + const struct list_head *device_list; + struct mlx5e_macsec_sa *macsec_sa; + struct mlx5e_macsec_device *iter; + int i; + + device_list = &macsec->macsec_device_list_head; + + list_for_each_entry(iter, device_list, macsec_device_list_element) { + for (i = 0; i < MACSEC_NUM_AN; ++i) { + macsec_sa = iter->tx_sa[i]; + if (!macsec_sa || !macsec_sa->active) + continue; + if (macsec_sa->macsec_obj_id == obj_id) + return macsec_sa; + } + } + + return NULL; +} + +static struct mlx5e_macsec_sa *get_macsec_rx_sa_from_obj_id(const struct mlx5e_macsec *macsec, + const u32 obj_id) +{ + const struct list_head *device_list, *sc_list; + struct mlx5e_macsec_rx_sc *mlx5e_rx_sc; + struct mlx5e_macsec_sa *macsec_sa; + struct mlx5e_macsec_device *iter; + int i; + + device_list = &macsec->macsec_device_list_head; + + list_for_each_entry(iter, device_list, macsec_device_list_element) { + sc_list = &iter->macsec_rx_sc_list_head; + list_for_each_entry(mlx5e_rx_sc, sc_list, rx_sc_list_element) { + for (i = 0; i < MACSEC_NUM_AN; ++i) { + macsec_sa = mlx5e_rx_sc->rx_sa[i]; + if (!macsec_sa || !macsec_sa->active) + continue; + if (macsec_sa->macsec_obj_id == obj_id) + return macsec_sa; + } + } + } + + return NULL; +} + +static void macsec_epn_update(struct mlx5e_macsec *macsec, struct mlx5_core_dev *mdev, + struct mlx5e_macsec_sa *sa, u32 obj_id, u32 mode_param) +{ + struct mlx5_macsec_obj_attrs attrs = {}; + struct mlx5e_macsec_aso_in in = {}; + + /* When the bottom of the replay protection window (mode_param) crosses 2^31 (half sequence + * number wraparound) hence mode_param > MLX5_MACSEC_EPN_SCOPE_MID the SW should update the + * esn_overlap to OLD (1). + * When the bottom of the replay protection window (mode_param) crosses 2^32 (full sequence + * number wraparound) hence mode_param < MLX5_MACSEC_EPN_SCOPE_MID since it did a + * wraparound, the SW should update the esn_overlap to NEW (0), and increment the esn_msb. + */ + + if (mode_param < MLX5_MACSEC_EPN_SCOPE_MID) { + sa->epn_state.epn_msb++; + sa->epn_state.overlap = 0; + } else { + sa->epn_state.overlap = 1; + } + + macsec_build_accel_attrs(sa, &attrs); + mlx5e_macsec_modify_obj(mdev, &attrs, obj_id); + + /* Re-set EPN arm event */ + in.obj_id = obj_id; + in.mode = MLX5_MACSEC_EPN; + macsec_aso_set_arm_event(mdev, macsec, &in); +} + +static void macsec_async_event(struct work_struct *work) +{ + struct mlx5e_macsec_async_work *async_work; + struct mlx5e_macsec_aso_out out = {}; + struct mlx5e_macsec_aso_in in = {}; + struct mlx5e_macsec_sa *macsec_sa; + struct mlx5e_macsec *macsec; + struct mlx5_core_dev *mdev; + u32 obj_id; + + async_work = container_of(work, struct mlx5e_macsec_async_work, work); + macsec = async_work->macsec; + mdev = async_work->mdev; + obj_id = async_work->obj_id; + macsec_sa = get_macsec_tx_sa_from_obj_id(macsec, obj_id); + if (!macsec_sa) { + macsec_sa = get_macsec_rx_sa_from_obj_id(macsec, obj_id); + if (!macsec_sa) { + mlx5_core_dbg(mdev, "MACsec SA is not found (SA object id %d)\n", obj_id); + goto out_async_work; + } + } + + /* Query MACsec ASO context */ + in.obj_id = obj_id; + macsec_aso_query(mdev, macsec, &in, &out); + + /* EPN case */ + if (macsec_sa->epn_state.epn_enabled && !(out.event_arm & MLX5E_ASO_EPN_ARM)) + macsec_epn_update(macsec, mdev, macsec_sa, obj_id, out.mode_param); + +out_async_work: + kfree(async_work); +} + +static int macsec_obj_change_event(struct notifier_block *nb, unsigned long event, void *data) +{ + struct mlx5e_macsec *macsec = container_of(nb, struct mlx5e_macsec, nb); + struct mlx5e_macsec_async_work *async_work; + struct mlx5_eqe_obj_change *obj_change; + struct mlx5_eqe *eqe = data; + u16 obj_type; + u32 obj_id; + + if (event != MLX5_EVENT_TYPE_OBJECT_CHANGE) + return NOTIFY_DONE; + + obj_change = &eqe->data.obj_change; + obj_type = be16_to_cpu(obj_change->obj_type); + obj_id = be32_to_cpu(obj_change->obj_id); + + if (obj_type != MLX5_GENERAL_OBJECT_TYPES_MACSEC) + return NOTIFY_DONE; + + async_work = kzalloc(sizeof(*async_work), GFP_ATOMIC); + if (!async_work) + return NOTIFY_DONE; + + async_work->macsec = macsec; + async_work->mdev = macsec->mdev; + async_work->obj_id = obj_id; + + INIT_WORK(&async_work->work, macsec_async_event); + + WARN_ON(!queue_work(macsec->wq, &async_work->work)); + + return NOTIFY_OK; +} + +static int mlx5e_macsec_aso_init(struct mlx5e_macsec_aso *aso, struct mlx5_core_dev *mdev) +{ + struct mlx5_aso *maso; + int err; + + err = mlx5_core_alloc_pd(mdev, &aso->pdn); + if (err) { + mlx5_core_err(mdev, + "MACsec offload: Failed to alloc pd for MACsec ASO, err=%d\n", + err); + return err; + } + + maso = mlx5_aso_create(mdev, aso->pdn); + if (IS_ERR(maso)) { + err = PTR_ERR(maso); + goto err_aso; + } + + err = mlx5e_macsec_aso_reg_mr(mdev, aso); + if (err) + goto err_aso_reg; + + mutex_init(&aso->aso_lock); + + aso->maso = maso; + + return 0; + +err_aso_reg: + mlx5_aso_destroy(maso); +err_aso: + mlx5_core_dealloc_pd(mdev, aso->pdn); + return err; +} + +static void mlx5e_macsec_aso_cleanup(struct mlx5e_macsec_aso *aso, struct mlx5_core_dev *mdev) +{ + if (!aso) + return; + + mlx5e_macsec_aso_dereg_mr(mdev, aso); + + mlx5_aso_destroy(aso->maso); + + mlx5_core_dealloc_pd(mdev, aso->pdn); +} + +bool mlx5e_is_macsec_device(const struct mlx5_core_dev *mdev) +{ + if (!(MLX5_CAP_GEN_64(mdev, general_obj_types) & + MLX5_GENERAL_OBJ_TYPES_CAP_MACSEC_OFFLOAD)) + return false; + + if (!MLX5_CAP_GEN(mdev, log_max_dek)) + return false; + + if (!MLX5_CAP_MACSEC(mdev, log_max_macsec_offload)) + return false; + + if (!MLX5_CAP_FLOWTABLE_NIC_RX(mdev, macsec_decrypt) || + !MLX5_CAP_FLOWTABLE_NIC_RX(mdev, reformat_remove_macsec)) + return false; + + if (!MLX5_CAP_FLOWTABLE_NIC_TX(mdev, macsec_encrypt) || + !MLX5_CAP_FLOWTABLE_NIC_TX(mdev, reformat_add_macsec)) + return false; + + if (!MLX5_CAP_MACSEC(mdev, macsec_crypto_esp_aes_gcm_128_encrypt) && + !MLX5_CAP_MACSEC(mdev, macsec_crypto_esp_aes_gcm_256_encrypt)) + return false; + + if (!MLX5_CAP_MACSEC(mdev, macsec_crypto_esp_aes_gcm_128_decrypt) && + !MLX5_CAP_MACSEC(mdev, macsec_crypto_esp_aes_gcm_256_decrypt)) + return false; + + return true; +} + +void mlx5e_macsec_get_stats_fill(struct mlx5e_macsec *macsec, void *macsec_stats) +{ + mlx5e_macsec_fs_get_stats_fill(macsec->macsec_fs, macsec_stats); +} + +struct mlx5e_macsec_stats *mlx5e_macsec_get_stats(struct mlx5e_macsec *macsec) +{ + if (!macsec) + return NULL; + + return &macsec->stats; +} + +static const struct macsec_ops macsec_offload_ops = { + .mdo_add_txsa = mlx5e_macsec_add_txsa, + .mdo_upd_txsa = mlx5e_macsec_upd_txsa, + .mdo_del_txsa = mlx5e_macsec_del_txsa, + .mdo_add_rxsc = mlx5e_macsec_add_rxsc, + .mdo_upd_rxsc = mlx5e_macsec_upd_rxsc, + .mdo_del_rxsc = mlx5e_macsec_del_rxsc, + .mdo_add_rxsa = mlx5e_macsec_add_rxsa, + .mdo_upd_rxsa = mlx5e_macsec_upd_rxsa, + .mdo_del_rxsa = mlx5e_macsec_del_rxsa, + .mdo_add_secy = mlx5e_macsec_add_secy, + .mdo_upd_secy = mlx5e_macsec_upd_secy, + .mdo_del_secy = mlx5e_macsec_del_secy, +}; + +bool mlx5e_macsec_handle_tx_skb(struct mlx5e_macsec *macsec, struct sk_buff *skb) +{ + struct metadata_dst *md_dst = skb_metadata_dst(skb); + u32 fs_id; + + fs_id = mlx5e_macsec_get_sa_from_hashtable(&macsec->sci_hash, &md_dst->u.macsec_info.sci); + if (!fs_id) + goto err_out; + + return true; + +err_out: + dev_kfree_skb_any(skb); + return false; +} + +void mlx5e_macsec_tx_build_eseg(struct mlx5e_macsec *macsec, + struct sk_buff *skb, + struct mlx5_wqe_eth_seg *eseg) +{ + struct metadata_dst *md_dst = skb_metadata_dst(skb); + u32 fs_id; + + fs_id = mlx5e_macsec_get_sa_from_hashtable(&macsec->sci_hash, &md_dst->u.macsec_info.sci); + if (!fs_id) + return; + + eseg->flow_table_metadata = cpu_to_be32(MLX5_ETH_WQE_FT_META_MACSEC | fs_id << 2); +} + +void mlx5e_macsec_offload_handle_rx_skb(struct net_device *netdev, + struct sk_buff *skb, + struct mlx5_cqe64 *cqe) +{ + struct mlx5e_macsec_rx_sc_xarray_element *sc_xarray_element; + u32 macsec_meta_data = be32_to_cpu(cqe->ft_metadata); + struct mlx5e_priv *priv = netdev_priv(netdev); + struct mlx5e_macsec_rx_sc *rx_sc; + struct mlx5e_macsec *macsec; + u32 fs_id; + + macsec = priv->macsec; + if (!macsec) + return; + + fs_id = MLX5_MACSEC_METADATA_HANDLE(macsec_meta_data); + + rcu_read_lock(); + sc_xarray_element = xa_load(&macsec->sc_xarray, fs_id); + rx_sc = sc_xarray_element->rx_sc; + if (rx_sc) { + dst_hold(&rx_sc->md_dst->dst); + skb_dst_set(skb, &rx_sc->md_dst->dst); + } + + rcu_read_unlock(); +} + +void mlx5e_macsec_build_netdev(struct mlx5e_priv *priv) +{ + struct net_device *netdev = priv->netdev; + + if (!mlx5e_is_macsec_device(priv->mdev)) + return; + + /* Enable MACsec */ + mlx5_core_dbg(priv->mdev, "mlx5e: MACsec acceleration enabled\n"); + netdev->macsec_ops = &macsec_offload_ops; + netdev->features |= NETIF_F_HW_MACSEC; + netif_keep_dst(netdev); +} + +int mlx5e_macsec_init(struct mlx5e_priv *priv) +{ + struct mlx5_core_dev *mdev = priv->mdev; + struct mlx5e_macsec *macsec = NULL; + struct mlx5e_macsec_fs *macsec_fs; + int err; + + if (!mlx5e_is_macsec_device(priv->mdev)) { + mlx5_core_dbg(mdev, "Not a MACsec offload device\n"); + return 0; + } + + macsec = kzalloc(sizeof(*macsec), GFP_KERNEL); + if (!macsec) + return -ENOMEM; + + INIT_LIST_HEAD(&macsec->macsec_device_list_head); + mutex_init(&macsec->lock); + + err = rhashtable_init(&macsec->sci_hash, &rhash_sci); + if (err) { + mlx5_core_err(mdev, "MACsec offload: Failed to init SCI hash table, err=%d\n", + err); + goto err_hash; + } + + err = mlx5e_macsec_aso_init(&macsec->aso, priv->mdev); + if (err) { + mlx5_core_err(mdev, "MACsec offload: Failed to init aso, err=%d\n", err); + goto err_aso; + } + + macsec->wq = alloc_ordered_workqueue("mlx5e_macsec_%s", 0, priv->netdev->name); + if (!macsec->wq) { + err = -ENOMEM; + goto err_wq; + } + + xa_init_flags(&macsec->sc_xarray, XA_FLAGS_ALLOC1); + + priv->macsec = macsec; + + macsec->mdev = mdev; + + macsec_fs = mlx5e_macsec_fs_init(mdev, priv->netdev); + if (!macsec_fs) { + err = -ENOMEM; + goto err_out; + } + + macsec->macsec_fs = macsec_fs; + + macsec->nb.notifier_call = macsec_obj_change_event; + mlx5_notifier_register(mdev, &macsec->nb); + + mlx5_core_dbg(mdev, "MACsec attached to netdevice\n"); + + return 0; + +err_out: + destroy_workqueue(macsec->wq); +err_wq: + mlx5e_macsec_aso_cleanup(&macsec->aso, priv->mdev); +err_aso: + rhashtable_destroy(&macsec->sci_hash); +err_hash: + kfree(macsec); + priv->macsec = NULL; + return err; +} + +void mlx5e_macsec_cleanup(struct mlx5e_priv *priv) +{ + struct mlx5e_macsec *macsec = priv->macsec; + struct mlx5_core_dev *mdev = macsec->mdev; + + if (!macsec) + return; + + mlx5_notifier_unregister(mdev, &macsec->nb); + + mlx5e_macsec_fs_cleanup(macsec->macsec_fs); + + /* Cleanup workqueue */ + destroy_workqueue(macsec->wq); + + mlx5e_macsec_aso_cleanup(&macsec->aso, mdev); + + priv->macsec = NULL; + + rhashtable_destroy(&macsec->sci_hash); + + mutex_destroy(&macsec->lock); + + kfree(macsec); +} diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/macsec.h b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/macsec.h new file mode 100644 index 000000000000..d580b4a91253 --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/macsec.h @@ -0,0 +1,71 @@ +/* SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB */ +/* Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. */ + +#ifndef __MLX5_EN_ACCEL_MACSEC_H__ +#define __MLX5_EN_ACCEL_MACSEC_H__ + +#ifdef CONFIG_MLX5_EN_MACSEC + +#include <linux/mlx5/driver.h> +#include <net/macsec.h> +#include <net/dst_metadata.h> + +/* Bit31 - 30: MACsec marker, Bit3-0: MACsec id */ +#define MLX5_MACSEC_METADATA_MARKER(metadata) ((((metadata) >> 30) & 0x3) == 0x1) +#define MLX5_MACSEC_METADATA_HANDLE(metadata) ((metadata) & GENMASK(3, 0)) + +struct mlx5e_priv; +struct mlx5e_macsec; + +struct mlx5e_macsec_stats { + u64 macsec_rx_pkts; + u64 macsec_rx_bytes; + u64 macsec_rx_pkts_drop; + u64 macsec_rx_bytes_drop; + u64 macsec_tx_pkts; + u64 macsec_tx_bytes; + u64 macsec_tx_pkts_drop; + u64 macsec_tx_bytes_drop; +}; + +void mlx5e_macsec_build_netdev(struct mlx5e_priv *priv); +int mlx5e_macsec_init(struct mlx5e_priv *priv); +void mlx5e_macsec_cleanup(struct mlx5e_priv *priv); +bool mlx5e_macsec_handle_tx_skb(struct mlx5e_macsec *macsec, struct sk_buff *skb); +void mlx5e_macsec_tx_build_eseg(struct mlx5e_macsec *macsec, + struct sk_buff *skb, + struct mlx5_wqe_eth_seg *eseg); + +static inline bool mlx5e_macsec_skb_is_offload(struct sk_buff *skb) +{ + struct metadata_dst *md_dst = skb_metadata_dst(skb); + + return md_dst && (md_dst->type == METADATA_MACSEC); +} + +static inline bool mlx5e_macsec_is_rx_flow(struct mlx5_cqe64 *cqe) +{ + return MLX5_MACSEC_METADATA_MARKER(be32_to_cpu(cqe->ft_metadata)); +} + +void mlx5e_macsec_offload_handle_rx_skb(struct net_device *netdev, struct sk_buff *skb, + struct mlx5_cqe64 *cqe); +bool mlx5e_is_macsec_device(const struct mlx5_core_dev *mdev); +void mlx5e_macsec_get_stats_fill(struct mlx5e_macsec *macsec, void *macsec_stats); +struct mlx5e_macsec_stats *mlx5e_macsec_get_stats(struct mlx5e_macsec *macsec); + +#else + +static inline void mlx5e_macsec_build_netdev(struct mlx5e_priv *priv) {} +static inline int mlx5e_macsec_init(struct mlx5e_priv *priv) { return 0; } +static inline void mlx5e_macsec_cleanup(struct mlx5e_priv *priv) {} +static inline bool mlx5e_macsec_skb_is_offload(struct sk_buff *skb) { return false; } +static inline bool mlx5e_macsec_is_rx_flow(struct mlx5_cqe64 *cqe) { return false; } +static inline void mlx5e_macsec_offload_handle_rx_skb(struct net_device *netdev, + struct sk_buff *skb, + struct mlx5_cqe64 *cqe) +{} +static inline bool mlx5e_is_macsec_device(const struct mlx5_core_dev *mdev) { return false; } +#endif /* CONFIG_MLX5_EN_MACSEC */ + +#endif /* __MLX5_ACCEL_EN_MACSEC_H__ */ diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/macsec_fs.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/macsec_fs.c new file mode 100644 index 000000000000..13dc628b988a --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/macsec_fs.c @@ -0,0 +1,1384 @@ +// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB +/* Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. */ + +#include <net/macsec.h> +#include <linux/netdevice.h> +#include <linux/mlx5/qp.h> +#include "fs_core.h" +#include "en/fs.h" +#include "en_accel/macsec_fs.h" +#include "mlx5_core.h" + +/* MACsec TX flow steering */ +#define CRYPTO_NUM_MAXSEC_FTE BIT(15) +#define CRYPTO_TABLE_DEFAULT_RULE_GROUP_SIZE 1 + +#define TX_CRYPTO_TABLE_LEVEL 0 +#define TX_CRYPTO_TABLE_NUM_GROUPS 3 +#define TX_CRYPTO_TABLE_MKE_GROUP_SIZE 1 +#define TX_CRYPTO_TABLE_SA_GROUP_SIZE \ + (CRYPTO_NUM_MAXSEC_FTE - (TX_CRYPTO_TABLE_MKE_GROUP_SIZE + \ + CRYPTO_TABLE_DEFAULT_RULE_GROUP_SIZE)) +#define TX_CHECK_TABLE_LEVEL 1 +#define TX_CHECK_TABLE_NUM_FTE 2 +#define RX_CRYPTO_TABLE_LEVEL 0 +#define RX_CHECK_TABLE_LEVEL 1 +#define RX_CHECK_TABLE_NUM_FTE 3 +#define RX_CRYPTO_TABLE_NUM_GROUPS 3 +#define RX_CRYPTO_TABLE_SA_RULE_WITH_SCI_GROUP_SIZE \ + ((CRYPTO_NUM_MAXSEC_FTE - CRYPTO_TABLE_DEFAULT_RULE_GROUP_SIZE) / 2) +#define RX_CRYPTO_TABLE_SA_RULE_WITHOUT_SCI_GROUP_SIZE \ + (CRYPTO_NUM_MAXSEC_FTE - RX_CRYPTO_TABLE_SA_RULE_WITH_SCI_GROUP_SIZE) +#define RX_NUM_OF_RULES_PER_SA 2 + +#define MLX5_MACSEC_TAG_LEN 8 /* SecTAG length with ethertype and without the optional SCI */ +#define MLX5_MACSEC_SECTAG_TCI_AN_FIELD_BITMASK 0x23 +#define MLX5_MACSEC_SECTAG_TCI_AN_FIELD_OFFSET 0x8 +#define MLX5_MACSEC_SECTAG_TCI_SC_FIELD_OFFSET 0x5 +#define MLX5_MACSEC_SECTAG_TCI_SC_FIELD_BIT (0x1 << MLX5_MACSEC_SECTAG_TCI_SC_FIELD_OFFSET) +#define MLX5_SECTAG_HEADER_SIZE_WITHOUT_SCI 0x8 +#define MLX5_SECTAG_HEADER_SIZE_WITH_SCI (MLX5_SECTAG_HEADER_SIZE_WITHOUT_SCI + MACSEC_SCI_LEN) + +/* MACsec RX flow steering */ +#define MLX5_ETH_WQE_FT_META_MACSEC_MASK 0x3E + +struct mlx5_sectag_header { + __be16 ethertype; + u8 tci_an; + u8 sl; + u32 pn; + u8 sci[MACSEC_SCI_LEN]; /* optional */ +} __packed; + +struct mlx5e_macsec_tx_rule { + struct mlx5_flow_handle *rule; + struct mlx5_pkt_reformat *pkt_reformat; + u32 fs_id; +}; + +struct mlx5e_macsec_tables { + struct mlx5e_flow_table ft_crypto; + struct mlx5_flow_handle *crypto_miss_rule; + + struct mlx5_flow_table *ft_check; + struct mlx5_flow_group *ft_check_group; + struct mlx5_fc *check_miss_rule_counter; + struct mlx5_flow_handle *check_miss_rule; + struct mlx5_fc *check_rule_counter; + + u32 refcnt; +}; + +struct mlx5e_macsec_tx { + struct mlx5_flow_handle *crypto_mke_rule; + struct mlx5_flow_handle *check_rule; + + struct ida tx_halloc; + + struct mlx5e_macsec_tables tables; +}; + +struct mlx5e_macsec_rx_rule { + struct mlx5_flow_handle *rule[RX_NUM_OF_RULES_PER_SA]; + struct mlx5_modify_hdr *meta_modhdr; +}; + +struct mlx5e_macsec_rx { + struct mlx5_flow_handle *check_rule[2]; + struct mlx5_pkt_reformat *check_rule_pkt_reformat[2]; + + struct mlx5e_macsec_tables tables; +}; + +union mlx5e_macsec_rule { + struct mlx5e_macsec_tx_rule tx_rule; + struct mlx5e_macsec_rx_rule rx_rule; +}; + +struct mlx5e_macsec_fs { + struct mlx5_core_dev *mdev; + struct net_device *netdev; + struct mlx5e_macsec_tx *tx_fs; + struct mlx5e_macsec_rx *rx_fs; +}; + +static void macsec_fs_tx_destroy(struct mlx5e_macsec_fs *macsec_fs) +{ + struct mlx5e_macsec_tx *tx_fs = macsec_fs->tx_fs; + struct mlx5e_macsec_tables *tx_tables; + + tx_tables = &tx_fs->tables; + + /* Tx check table */ + if (tx_fs->check_rule) { + mlx5_del_flow_rules(tx_fs->check_rule); + tx_fs->check_rule = NULL; + } + + if (tx_tables->check_miss_rule) { + mlx5_del_flow_rules(tx_tables->check_miss_rule); + tx_tables->check_miss_rule = NULL; + } + + if (tx_tables->ft_check_group) { + mlx5_destroy_flow_group(tx_tables->ft_check_group); + tx_tables->ft_check_group = NULL; + } + + if (tx_tables->ft_check) { + mlx5_destroy_flow_table(tx_tables->ft_check); + tx_tables->ft_check = NULL; + } + + /* Tx crypto table */ + if (tx_fs->crypto_mke_rule) { + mlx5_del_flow_rules(tx_fs->crypto_mke_rule); + tx_fs->crypto_mke_rule = NULL; + } + + if (tx_tables->crypto_miss_rule) { + mlx5_del_flow_rules(tx_tables->crypto_miss_rule); + tx_tables->crypto_miss_rule = NULL; + } + + mlx5e_destroy_flow_table(&tx_tables->ft_crypto); +} + +static int macsec_fs_tx_create_crypto_table_groups(struct mlx5e_flow_table *ft) +{ + int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in); + int mclen = MLX5_ST_SZ_BYTES(fte_match_param); + int ix = 0; + u32 *in; + int err; + u8 *mc; + + ft->g = kcalloc(TX_CRYPTO_TABLE_NUM_GROUPS, sizeof(*ft->g), GFP_KERNEL); + if (!ft->g) + return -ENOMEM; + in = kvzalloc(inlen, GFP_KERNEL); + + if (!in) { + kfree(ft->g); + return -ENOMEM; + } + + mc = MLX5_ADDR_OF(create_flow_group_in, in, match_criteria); + + /* Flow Group for MKE match */ + MLX5_SET_CFG(in, match_criteria_enable, MLX5_MATCH_OUTER_HEADERS); + MLX5_SET_TO_ONES(fte_match_param, mc, outer_headers.ethertype); + + MLX5_SET_CFG(in, start_flow_index, ix); + ix += TX_CRYPTO_TABLE_MKE_GROUP_SIZE; + MLX5_SET_CFG(in, end_flow_index, ix - 1); + ft->g[ft->num_groups] = mlx5_create_flow_group(ft->t, in); + if (IS_ERR(ft->g[ft->num_groups])) + goto err; + ft->num_groups++; + + /* Flow Group for SA rules */ + memset(in, 0, inlen); + memset(mc, 0, mclen); + MLX5_SET_CFG(in, match_criteria_enable, MLX5_MATCH_MISC_PARAMETERS_2); + MLX5_SET(fte_match_param, mc, misc_parameters_2.metadata_reg_a, + MLX5_ETH_WQE_FT_META_MACSEC_MASK); + + MLX5_SET_CFG(in, start_flow_index, ix); + ix += TX_CRYPTO_TABLE_SA_GROUP_SIZE; + MLX5_SET_CFG(in, end_flow_index, ix - 1); + ft->g[ft->num_groups] = mlx5_create_flow_group(ft->t, in); + if (IS_ERR(ft->g[ft->num_groups])) + goto err; + ft->num_groups++; + + /* Flow Group for l2 traps */ + memset(in, 0, inlen); + memset(mc, 0, mclen); + MLX5_SET_CFG(in, start_flow_index, ix); + ix += CRYPTO_TABLE_DEFAULT_RULE_GROUP_SIZE; + MLX5_SET_CFG(in, end_flow_index, ix - 1); + ft->g[ft->num_groups] = mlx5_create_flow_group(ft->t, in); + if (IS_ERR(ft->g[ft->num_groups])) + goto err; + ft->num_groups++; + + kvfree(in); + return 0; + +err: + err = PTR_ERR(ft->g[ft->num_groups]); + ft->g[ft->num_groups] = NULL; + kvfree(in); + + return err; +} + +static struct mlx5_flow_table + *macsec_fs_auto_group_table_create(struct mlx5_flow_namespace *ns, int flags, + int level, int max_fte) +{ + struct mlx5_flow_table_attr ft_attr = {}; + struct mlx5_flow_table *fdb = NULL; + + /* reserve entry for the match all miss group and rule */ + ft_attr.autogroup.num_reserved_entries = 1; + ft_attr.autogroup.max_num_groups = 1; + ft_attr.prio = 0; + ft_attr.flags = flags; + ft_attr.level = level; + ft_attr.max_fte = max_fte; + + fdb = mlx5_create_auto_grouped_flow_table(ns, &ft_attr); + + return fdb; +} + +static int macsec_fs_tx_create(struct mlx5e_macsec_fs *macsec_fs) +{ + int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in); + struct mlx5e_macsec_tx *tx_fs = macsec_fs->tx_fs; + struct net_device *netdev = macsec_fs->netdev; + struct mlx5_flow_table_attr ft_attr = {}; + struct mlx5_flow_destination dest = {}; + struct mlx5e_macsec_tables *tx_tables; + struct mlx5_flow_act flow_act = {}; + struct mlx5e_flow_table *ft_crypto; + struct mlx5_flow_table *flow_table; + struct mlx5_flow_group *flow_group; + struct mlx5_flow_namespace *ns; + struct mlx5_flow_handle *rule; + struct mlx5_flow_spec *spec; + u32 *flow_group_in; + int err = 0; + + ns = mlx5_get_flow_namespace(macsec_fs->mdev, MLX5_FLOW_NAMESPACE_EGRESS_MACSEC); + if (!ns) + return -ENOMEM; + + spec = kvzalloc(sizeof(*spec), GFP_KERNEL); + if (!spec) + return -ENOMEM; + + flow_group_in = kvzalloc(inlen, GFP_KERNEL); + if (!flow_group_in) + goto out_spec; + + tx_tables = &tx_fs->tables; + ft_crypto = &tx_tables->ft_crypto; + + /* Tx crypto table */ + ft_attr.flags = MLX5_FLOW_TABLE_TUNNEL_EN_REFORMAT; + ft_attr.level = TX_CRYPTO_TABLE_LEVEL; + ft_attr.max_fte = CRYPTO_NUM_MAXSEC_FTE; + + flow_table = mlx5_create_flow_table(ns, &ft_attr); + if (IS_ERR(flow_table)) { + err = PTR_ERR(flow_table); + netdev_err(netdev, "Failed to create MACsec Tx crypto table err(%d)\n", err); + goto out_flow_group; + } + ft_crypto->t = flow_table; + + /* Tx crypto table groups */ + err = macsec_fs_tx_create_crypto_table_groups(ft_crypto); + if (err) { + netdev_err(netdev, + "Failed to create default flow group for MACsec Tx crypto table err(%d)\n", + err); + goto err; + } + + /* Tx crypto table MKE rule - MKE packets shouldn't be offloaded */ + memset(&flow_act, 0, sizeof(flow_act)); + memset(spec, 0, sizeof(*spec)); + spec->match_criteria_enable = MLX5_MATCH_OUTER_HEADERS; + + MLX5_SET_TO_ONES(fte_match_param, spec->match_criteria, outer_headers.ethertype); + MLX5_SET(fte_match_param, spec->match_value, outer_headers.ethertype, ETH_P_PAE); + flow_act.action = MLX5_FLOW_CONTEXT_ACTION_ALLOW; + + rule = mlx5_add_flow_rules(ft_crypto->t, spec, &flow_act, NULL, 0); + if (IS_ERR(rule)) { + err = PTR_ERR(rule); + netdev_err(netdev, "Failed to add MACsec TX MKE rule, err=%d\n", err); + goto err; + } + tx_fs->crypto_mke_rule = rule; + + /* Tx crypto table Default miss rule */ + memset(&flow_act, 0, sizeof(flow_act)); + flow_act.action = MLX5_FLOW_CONTEXT_ACTION_ALLOW; + rule = mlx5_add_flow_rules(ft_crypto->t, NULL, &flow_act, NULL, 0); + if (IS_ERR(rule)) { + err = PTR_ERR(rule); + netdev_err(netdev, "Failed to add MACsec Tx table default miss rule %d\n", err); + goto err; + } + tx_tables->crypto_miss_rule = rule; + + /* Tx check table */ + flow_table = macsec_fs_auto_group_table_create(ns, 0, TX_CHECK_TABLE_LEVEL, + TX_CHECK_TABLE_NUM_FTE); + if (IS_ERR(flow_table)) { + err = PTR_ERR(flow_table); + netdev_err(netdev, "fail to create MACsec TX check table, err(%d)\n", err); + goto err; + } + tx_tables->ft_check = flow_table; + + /* Tx check table Default miss group/rule */ + memset(flow_group_in, 0, inlen); + MLX5_SET(create_flow_group_in, flow_group_in, start_flow_index, flow_table->max_fte - 1); + MLX5_SET(create_flow_group_in, flow_group_in, end_flow_index, flow_table->max_fte - 1); + flow_group = mlx5_create_flow_group(tx_tables->ft_check, flow_group_in); + if (IS_ERR(flow_group)) { + err = PTR_ERR(flow_group); + netdev_err(netdev, + "Failed to create default flow group for MACsec Tx crypto table err(%d)\n", + err); + goto err; + } + tx_tables->ft_check_group = flow_group; + + /* Tx check table default drop rule */ + memset(&dest, 0, sizeof(struct mlx5_flow_destination)); + memset(&flow_act, 0, sizeof(flow_act)); + dest.type = MLX5_FLOW_DESTINATION_TYPE_COUNTER; + dest.counter_id = mlx5_fc_id(tx_tables->check_miss_rule_counter); + flow_act.action = MLX5_FLOW_CONTEXT_ACTION_DROP | MLX5_FLOW_CONTEXT_ACTION_COUNT; + rule = mlx5_add_flow_rules(tx_tables->ft_check, NULL, &flow_act, &dest, 1); + if (IS_ERR(rule)) { + err = PTR_ERR(rule); + netdev_err(netdev, "Failed to added MACsec tx check drop rule, err(%d)\n", err); + goto err; + } + tx_tables->check_miss_rule = rule; + + /* Tx check table rule */ + memset(spec, 0, sizeof(struct mlx5_flow_spec)); + memset(&dest, 0, sizeof(struct mlx5_flow_destination)); + memset(&flow_act, 0, sizeof(flow_act)); + + MLX5_SET_TO_ONES(fte_match_param, spec->match_criteria, misc_parameters_2.metadata_reg_c_4); + MLX5_SET(fte_match_param, spec->match_value, misc_parameters_2.metadata_reg_c_4, 0); + spec->match_criteria_enable = MLX5_MATCH_MISC_PARAMETERS_2; + + flow_act.flags = FLOW_ACT_NO_APPEND; + flow_act.action = MLX5_FLOW_CONTEXT_ACTION_ALLOW | MLX5_FLOW_CONTEXT_ACTION_COUNT; + dest.type = MLX5_FLOW_DESTINATION_TYPE_COUNTER; + dest.counter_id = mlx5_fc_id(tx_tables->check_rule_counter); + rule = mlx5_add_flow_rules(tx_tables->ft_check, spec, &flow_act, &dest, 1); + if (IS_ERR(rule)) { + err = PTR_ERR(rule); + netdev_err(netdev, "Failed to add MACsec check rule, err=%d\n", err); + goto err; + } + tx_fs->check_rule = rule; + + goto out_flow_group; + +err: + macsec_fs_tx_destroy(macsec_fs); +out_flow_group: + kvfree(flow_group_in); +out_spec: + kvfree(spec); + return err; +} + +static int macsec_fs_tx_ft_get(struct mlx5e_macsec_fs *macsec_fs) +{ + struct mlx5e_macsec_tx *tx_fs = macsec_fs->tx_fs; + struct mlx5e_macsec_tables *tx_tables; + int err = 0; + + tx_tables = &tx_fs->tables; + if (tx_tables->refcnt) + goto out; + + err = macsec_fs_tx_create(macsec_fs); + if (err) + return err; + +out: + tx_tables->refcnt++; + return err; +} + +static void macsec_fs_tx_ft_put(struct mlx5e_macsec_fs *macsec_fs) +{ + struct mlx5e_macsec_tables *tx_tables = &macsec_fs->tx_fs->tables; + + if (--tx_tables->refcnt) + return; + + macsec_fs_tx_destroy(macsec_fs); +} + +static int macsec_fs_tx_setup_fte(struct mlx5e_macsec_fs *macsec_fs, + struct mlx5_flow_spec *spec, + struct mlx5_flow_act *flow_act, + u32 macsec_obj_id, + u32 *fs_id) +{ + struct mlx5e_macsec_tx *tx_fs = macsec_fs->tx_fs; + int err = 0; + u32 id; + + err = ida_alloc_range(&tx_fs->tx_halloc, 1, + MLX5_MACSEC_NUM_OF_SUPPORTED_INTERFACES, + GFP_KERNEL); + if (err < 0) + return err; + + id = err; + spec->match_criteria_enable |= MLX5_MATCH_MISC_PARAMETERS_2; + + /* Metadata match */ + MLX5_SET(fte_match_param, spec->match_criteria, misc_parameters_2.metadata_reg_a, + MLX5_ETH_WQE_FT_META_MACSEC_MASK); + MLX5_SET(fte_match_param, spec->match_value, misc_parameters_2.metadata_reg_a, + MLX5_ETH_WQE_FT_META_MACSEC | id << 2); + + *fs_id = id; + flow_act->crypto.type = MLX5_FLOW_CONTEXT_ENCRYPT_DECRYPT_TYPE_MACSEC; + flow_act->crypto.obj_id = macsec_obj_id; + + mlx5_core_dbg(macsec_fs->mdev, "Tx fte: macsec obj_id %u, fs_id %u\n", macsec_obj_id, id); + return 0; +} + +static void macsec_fs_tx_create_sectag_header(const struct macsec_context *ctx, + char *reformatbf, + size_t *reformat_size) +{ + const struct macsec_secy *secy = ctx->secy; + bool sci_present = macsec_send_sci(secy); + struct mlx5_sectag_header sectag = {}; + const struct macsec_tx_sc *tx_sc; + + tx_sc = &secy->tx_sc; + sectag.ethertype = htons(ETH_P_MACSEC); + + if (sci_present) { + sectag.tci_an |= MACSEC_TCI_SC; + memcpy(§ag.sci, &secy->sci, + sizeof(sectag.sci)); + } else { + if (tx_sc->end_station) + sectag.tci_an |= MACSEC_TCI_ES; + if (tx_sc->scb) + sectag.tci_an |= MACSEC_TCI_SCB; + } + + /* With GCM, C/E clear for !encrypt, both set for encrypt */ + if (tx_sc->encrypt) + sectag.tci_an |= MACSEC_TCI_CONFID; + else if (secy->icv_len != MACSEC_DEFAULT_ICV_LEN) + sectag.tci_an |= MACSEC_TCI_C; + + sectag.tci_an |= tx_sc->encoding_sa; + + *reformat_size = MLX5_MACSEC_TAG_LEN + (sci_present ? MACSEC_SCI_LEN : 0); + + memcpy(reformatbf, §ag, *reformat_size); +} + +static void macsec_fs_tx_del_rule(struct mlx5e_macsec_fs *macsec_fs, + struct mlx5e_macsec_tx_rule *tx_rule) +{ + if (tx_rule->rule) { + mlx5_del_flow_rules(tx_rule->rule); + tx_rule->rule = NULL; + } + + if (tx_rule->pkt_reformat) { + mlx5_packet_reformat_dealloc(macsec_fs->mdev, tx_rule->pkt_reformat); + tx_rule->pkt_reformat = NULL; + } + + if (tx_rule->fs_id) { + ida_free(&macsec_fs->tx_fs->tx_halloc, tx_rule->fs_id); + tx_rule->fs_id = 0; + } + + kfree(tx_rule); + + macsec_fs_tx_ft_put(macsec_fs); +} + +static union mlx5e_macsec_rule * +macsec_fs_tx_add_rule(struct mlx5e_macsec_fs *macsec_fs, + const struct macsec_context *macsec_ctx, + struct mlx5_macsec_rule_attrs *attrs, + u32 *sa_fs_id) +{ + char reformatbf[MLX5_MACSEC_TAG_LEN + MACSEC_SCI_LEN]; + struct mlx5_pkt_reformat_params reformat_params = {}; + struct mlx5e_macsec_tx *tx_fs = macsec_fs->tx_fs; + struct net_device *netdev = macsec_fs->netdev; + union mlx5e_macsec_rule *macsec_rule = NULL; + struct mlx5_flow_destination dest = {}; + struct mlx5e_macsec_tables *tx_tables; + struct mlx5e_macsec_tx_rule *tx_rule; + struct mlx5_flow_act flow_act = {}; + struct mlx5_flow_handle *rule; + struct mlx5_flow_spec *spec; + size_t reformat_size; + int err = 0; + u32 fs_id; + + tx_tables = &tx_fs->tables; + + spec = kvzalloc(sizeof(*spec), GFP_KERNEL); + if (!spec) + return NULL; + + err = macsec_fs_tx_ft_get(macsec_fs); + if (err) + goto out_spec; + + macsec_rule = kzalloc(sizeof(*macsec_rule), GFP_KERNEL); + if (!macsec_rule) { + macsec_fs_tx_ft_put(macsec_fs); + goto out_spec; + } + + tx_rule = &macsec_rule->tx_rule; + + /* Tx crypto table crypto rule */ + macsec_fs_tx_create_sectag_header(macsec_ctx, reformatbf, &reformat_size); + + reformat_params.type = MLX5_REFORMAT_TYPE_ADD_MACSEC; + reformat_params.size = reformat_size; + reformat_params.data = reformatbf; + flow_act.pkt_reformat = mlx5_packet_reformat_alloc(macsec_fs->mdev, + &reformat_params, + MLX5_FLOW_NAMESPACE_EGRESS_MACSEC); + if (IS_ERR(flow_act.pkt_reformat)) { + err = PTR_ERR(flow_act.pkt_reformat); + netdev_err(netdev, "Failed to allocate MACsec Tx reformat context err=%d\n", err); + goto err; + } + tx_rule->pkt_reformat = flow_act.pkt_reformat; + + err = macsec_fs_tx_setup_fte(macsec_fs, spec, &flow_act, attrs->macsec_obj_id, &fs_id); + if (err) { + netdev_err(netdev, + "Failed to add packet reformat for MACsec TX crypto rule, err=%d\n", + err); + goto err; + } + + tx_rule->fs_id = fs_id; + *sa_fs_id = fs_id; + + flow_act.action = MLX5_FLOW_CONTEXT_ACTION_FWD_DEST | + MLX5_FLOW_CONTEXT_ACTION_CRYPTO_ENCRYPT | + MLX5_FLOW_CONTEXT_ACTION_PACKET_REFORMAT; + dest.type = MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE; + dest.ft = tx_tables->ft_check; + rule = mlx5_add_flow_rules(tx_tables->ft_crypto.t, spec, &flow_act, &dest, 1); + if (IS_ERR(rule)) { + err = PTR_ERR(rule); + netdev_err(netdev, "Failed to add MACsec TX crypto rule, err=%d\n", err); + goto err; + } + tx_rule->rule = rule; + + goto out_spec; + +err: + macsec_fs_tx_del_rule(macsec_fs, tx_rule); + macsec_rule = NULL; +out_spec: + kvfree(spec); + + return macsec_rule; +} + +static void macsec_fs_tx_cleanup(struct mlx5e_macsec_fs *macsec_fs) +{ + struct mlx5e_macsec_tx *tx_fs = macsec_fs->tx_fs; + struct mlx5_core_dev *mdev = macsec_fs->mdev; + struct mlx5e_macsec_tables *tx_tables; + + if (!tx_fs) + return; + + tx_tables = &tx_fs->tables; + if (tx_tables->refcnt) { + netdev_err(macsec_fs->netdev, + "Can't destroy MACsec offload tx_fs, refcnt(%u) isn't 0\n", + tx_tables->refcnt); + return; + } + + ida_destroy(&tx_fs->tx_halloc); + + if (tx_tables->check_miss_rule_counter) { + mlx5_fc_destroy(mdev, tx_tables->check_miss_rule_counter); + tx_tables->check_miss_rule_counter = NULL; + } + + if (tx_tables->check_rule_counter) { + mlx5_fc_destroy(mdev, tx_tables->check_rule_counter); + tx_tables->check_rule_counter = NULL; + } + + kfree(tx_fs); + macsec_fs->tx_fs = NULL; +} + +static int macsec_fs_tx_init(struct mlx5e_macsec_fs *macsec_fs) +{ + struct net_device *netdev = macsec_fs->netdev; + struct mlx5_core_dev *mdev = macsec_fs->mdev; + struct mlx5e_macsec_tables *tx_tables; + struct mlx5e_macsec_tx *tx_fs; + struct mlx5_fc *flow_counter; + int err; + + tx_fs = kzalloc(sizeof(*tx_fs), GFP_KERNEL); + if (!tx_fs) + return -ENOMEM; + + tx_tables = &tx_fs->tables; + + flow_counter = mlx5_fc_create(mdev, false); + if (IS_ERR(flow_counter)) { + err = PTR_ERR(flow_counter); + netdev_err(netdev, + "Failed to create MACsec Tx encrypt flow counter, err(%d)\n", + err); + goto err_encrypt_counter; + } + tx_tables->check_rule_counter = flow_counter; + + flow_counter = mlx5_fc_create(mdev, false); + if (IS_ERR(flow_counter)) { + err = PTR_ERR(flow_counter); + netdev_err(netdev, + "Failed to create MACsec Tx drop flow counter, err(%d)\n", + err); + goto err_drop_counter; + } + tx_tables->check_miss_rule_counter = flow_counter; + + ida_init(&tx_fs->tx_halloc); + + macsec_fs->tx_fs = tx_fs; + + return 0; + +err_drop_counter: + mlx5_fc_destroy(mdev, tx_tables->check_rule_counter); + tx_tables->check_rule_counter = NULL; + +err_encrypt_counter: + kfree(tx_fs); + macsec_fs->tx_fs = NULL; + + return err; +} + +static void macsec_fs_rx_destroy(struct mlx5e_macsec_fs *macsec_fs) +{ + struct mlx5e_macsec_rx *rx_fs = macsec_fs->rx_fs; + struct mlx5e_macsec_tables *rx_tables; + int i; + + /* Rx check table */ + for (i = 1; i >= 0; --i) { + if (rx_fs->check_rule[i]) { + mlx5_del_flow_rules(rx_fs->check_rule[i]); + rx_fs->check_rule[i] = NULL; + } + + if (rx_fs->check_rule_pkt_reformat[i]) { + mlx5_packet_reformat_dealloc(macsec_fs->mdev, + rx_fs->check_rule_pkt_reformat[i]); + rx_fs->check_rule_pkt_reformat[i] = NULL; + } + } + + rx_tables = &rx_fs->tables; + + if (rx_tables->check_miss_rule) { + mlx5_del_flow_rules(rx_tables->check_miss_rule); + rx_tables->check_miss_rule = NULL; + } + + if (rx_tables->ft_check_group) { + mlx5_destroy_flow_group(rx_tables->ft_check_group); + rx_tables->ft_check_group = NULL; + } + + if (rx_tables->ft_check) { + mlx5_destroy_flow_table(rx_tables->ft_check); + rx_tables->ft_check = NULL; + } + + /* Rx crypto table */ + if (rx_tables->crypto_miss_rule) { + mlx5_del_flow_rules(rx_tables->crypto_miss_rule); + rx_tables->crypto_miss_rule = NULL; + } + + mlx5e_destroy_flow_table(&rx_tables->ft_crypto); +} + +static int macsec_fs_rx_create_crypto_table_groups(struct mlx5e_flow_table *ft) +{ + int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in); + int mclen = MLX5_ST_SZ_BYTES(fte_match_param); + int ix = 0; + u32 *in; + int err; + u8 *mc; + + ft->g = kcalloc(RX_CRYPTO_TABLE_NUM_GROUPS, sizeof(*ft->g), GFP_KERNEL); + if (!ft->g) + return -ENOMEM; + + in = kvzalloc(inlen, GFP_KERNEL); + if (!in) { + kfree(ft->g); + return -ENOMEM; + } + + mc = MLX5_ADDR_OF(create_flow_group_in, in, match_criteria); + + /* Flow group for SA rule with SCI */ + MLX5_SET_CFG(in, match_criteria_enable, MLX5_MATCH_OUTER_HEADERS | + MLX5_MATCH_MISC_PARAMETERS_5); + MLX5_SET_TO_ONES(fte_match_param, mc, outer_headers.ethertype); + + MLX5_SET(fte_match_param, mc, misc_parameters_5.macsec_tag_0, + MLX5_MACSEC_SECTAG_TCI_AN_FIELD_BITMASK << + MLX5_MACSEC_SECTAG_TCI_AN_FIELD_OFFSET); + MLX5_SET_TO_ONES(fte_match_param, mc, misc_parameters_5.macsec_tag_2); + MLX5_SET_TO_ONES(fte_match_param, mc, misc_parameters_5.macsec_tag_3); + + MLX5_SET_CFG(in, start_flow_index, ix); + ix += RX_CRYPTO_TABLE_SA_RULE_WITH_SCI_GROUP_SIZE; + MLX5_SET_CFG(in, end_flow_index, ix - 1); + ft->g[ft->num_groups] = mlx5_create_flow_group(ft->t, in); + if (IS_ERR(ft->g[ft->num_groups])) + goto err; + ft->num_groups++; + + /* Flow group for SA rule without SCI */ + memset(in, 0, inlen); + memset(mc, 0, mclen); + MLX5_SET_CFG(in, match_criteria_enable, MLX5_MATCH_OUTER_HEADERS | + MLX5_MATCH_MISC_PARAMETERS_5); + MLX5_SET_TO_ONES(fte_match_param, mc, outer_headers.smac_47_16); + MLX5_SET_TO_ONES(fte_match_param, mc, outer_headers.smac_15_0); + MLX5_SET_TO_ONES(fte_match_param, mc, outer_headers.ethertype); + + MLX5_SET(fte_match_param, mc, misc_parameters_5.macsec_tag_0, + MLX5_MACSEC_SECTAG_TCI_AN_FIELD_BITMASK << MLX5_MACSEC_SECTAG_TCI_AN_FIELD_OFFSET); + + MLX5_SET_CFG(in, start_flow_index, ix); + ix += RX_CRYPTO_TABLE_SA_RULE_WITHOUT_SCI_GROUP_SIZE; + MLX5_SET_CFG(in, end_flow_index, ix - 1); + ft->g[ft->num_groups] = mlx5_create_flow_group(ft->t, in); + if (IS_ERR(ft->g[ft->num_groups])) + goto err; + ft->num_groups++; + + /* Flow Group for l2 traps */ + memset(in, 0, inlen); + memset(mc, 0, mclen); + MLX5_SET_CFG(in, start_flow_index, ix); + ix += CRYPTO_TABLE_DEFAULT_RULE_GROUP_SIZE; + MLX5_SET_CFG(in, end_flow_index, ix - 1); + ft->g[ft->num_groups] = mlx5_create_flow_group(ft->t, in); + if (IS_ERR(ft->g[ft->num_groups])) + goto err; + ft->num_groups++; + + kvfree(in); + return 0; + +err: + err = PTR_ERR(ft->g[ft->num_groups]); + ft->g[ft->num_groups] = NULL; + kvfree(in); + + return err; +} + +static int macsec_fs_rx_create_check_decap_rule(struct mlx5e_macsec_fs *macsec_fs, + struct mlx5_flow_destination *dest, + struct mlx5_flow_act *flow_act, + struct mlx5_flow_spec *spec, + int reformat_param_size) +{ + int rule_index = (reformat_param_size == MLX5_SECTAG_HEADER_SIZE_WITH_SCI) ? 0 : 1; + u8 mlx5_reformat_buf[MLX5_SECTAG_HEADER_SIZE_WITH_SCI]; + struct mlx5_pkt_reformat_params reformat_params = {}; + struct mlx5e_macsec_rx *rx_fs = macsec_fs->rx_fs; + struct net_device *netdev = macsec_fs->netdev; + struct mlx5e_macsec_tables *rx_tables; + struct mlx5_flow_handle *rule; + int err = 0; + + rx_tables = &rx_fs->tables; + + /* Rx check table decap 16B rule */ + memset(dest, 0, sizeof(*dest)); + memset(flow_act, 0, sizeof(*flow_act)); + memset(spec, 0, sizeof(*spec)); + + reformat_params.type = MLX5_REFORMAT_TYPE_DEL_MACSEC; + reformat_params.size = reformat_param_size; + reformat_params.data = mlx5_reformat_buf; + flow_act->pkt_reformat = mlx5_packet_reformat_alloc(macsec_fs->mdev, + &reformat_params, + MLX5_FLOW_NAMESPACE_KERNEL_RX_MACSEC); + if (IS_ERR(flow_act->pkt_reformat)) { + err = PTR_ERR(flow_act->pkt_reformat); + netdev_err(netdev, "Failed to allocate MACsec Rx reformat context err=%d\n", err); + return err; + } + rx_fs->check_rule_pkt_reformat[rule_index] = flow_act->pkt_reformat; + + spec->match_criteria_enable = MLX5_MATCH_MISC_PARAMETERS_2; + /* MACsec syndrome match */ + MLX5_SET_TO_ONES(fte_match_param, spec->match_criteria, misc_parameters_2.macsec_syndrome); + MLX5_SET(fte_match_param, spec->match_value, misc_parameters_2.macsec_syndrome, 0); + /* ASO return reg syndrome match */ + MLX5_SET_TO_ONES(fte_match_param, spec->match_criteria, misc_parameters_2.metadata_reg_c_4); + MLX5_SET(fte_match_param, spec->match_value, misc_parameters_2.metadata_reg_c_4, 0); + + spec->match_criteria_enable |= MLX5_MATCH_MISC_PARAMETERS_5; + /* Sectag TCI SC present bit*/ + MLX5_SET(fte_match_param, spec->match_criteria, misc_parameters_5.macsec_tag_0, + MLX5_MACSEC_SECTAG_TCI_SC_FIELD_BIT << MLX5_MACSEC_SECTAG_TCI_AN_FIELD_OFFSET); + + if (reformat_param_size == MLX5_SECTAG_HEADER_SIZE_WITH_SCI) + MLX5_SET(fte_match_param, spec->match_value, misc_parameters_5.macsec_tag_0, + MLX5_MACSEC_SECTAG_TCI_SC_FIELD_BIT << + MLX5_MACSEC_SECTAG_TCI_AN_FIELD_OFFSET); + + flow_act->flags = FLOW_ACT_NO_APPEND; + flow_act->action = MLX5_FLOW_CONTEXT_ACTION_FWD_NEXT_PRIO | + MLX5_FLOW_CONTEXT_ACTION_PACKET_REFORMAT | + MLX5_FLOW_CONTEXT_ACTION_COUNT; + dest->type = MLX5_FLOW_DESTINATION_TYPE_COUNTER; + dest->counter_id = mlx5_fc_id(rx_tables->check_rule_counter); + rule = mlx5_add_flow_rules(rx_tables->ft_check, spec, flow_act, dest, 1); + if (IS_ERR(rule)) { + err = PTR_ERR(rule); + netdev_err(netdev, "Failed to add MACsec Rx check rule, err=%d\n", err); + return err; + } + + rx_fs->check_rule[rule_index] = rule; + + return 0; +} + +static int macsec_fs_rx_create(struct mlx5e_macsec_fs *macsec_fs) +{ + int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in); + struct mlx5e_macsec_rx *rx_fs = macsec_fs->rx_fs; + struct net_device *netdev = macsec_fs->netdev; + struct mlx5_flow_table_attr ft_attr = {}; + struct mlx5_flow_destination dest = {}; + struct mlx5e_macsec_tables *rx_tables; + struct mlx5e_flow_table *ft_crypto; + struct mlx5_flow_table *flow_table; + struct mlx5_flow_group *flow_group; + struct mlx5_flow_act flow_act = {}; + struct mlx5_flow_namespace *ns; + struct mlx5_flow_handle *rule; + struct mlx5_flow_spec *spec; + u32 *flow_group_in; + int err = 0; + + ns = mlx5_get_flow_namespace(macsec_fs->mdev, MLX5_FLOW_NAMESPACE_KERNEL_RX_MACSEC); + if (!ns) + return -ENOMEM; + + spec = kvzalloc(sizeof(*spec), GFP_KERNEL); + if (!spec) + return -ENOMEM; + + flow_group_in = kvzalloc(inlen, GFP_KERNEL); + if (!flow_group_in) + goto free_spec; + + rx_tables = &rx_fs->tables; + ft_crypto = &rx_tables->ft_crypto; + + /* Rx crypto table */ + ft_attr.level = RX_CRYPTO_TABLE_LEVEL; + ft_attr.max_fte = CRYPTO_NUM_MAXSEC_FTE; + + flow_table = mlx5_create_flow_table(ns, &ft_attr); + if (IS_ERR(flow_table)) { + err = PTR_ERR(flow_table); + netdev_err(netdev, "Failed to create MACsec Rx crypto table err(%d)\n", err); + goto out_flow_group; + } + ft_crypto->t = flow_table; + + /* Rx crypto table groups */ + err = macsec_fs_rx_create_crypto_table_groups(ft_crypto); + if (err) { + netdev_err(netdev, + "Failed to create default flow group for MACsec Tx crypto table err(%d)\n", + err); + goto err; + } + + flow_act.action = MLX5_FLOW_CONTEXT_ACTION_FWD_NEXT_PRIO; + rule = mlx5_add_flow_rules(ft_crypto->t, NULL, &flow_act, NULL, 0); + if (IS_ERR(rule)) { + err = PTR_ERR(rule); + netdev_err(netdev, + "Failed to add MACsec Rx crypto table default miss rule %d\n", + err); + goto err; + } + rx_tables->crypto_miss_rule = rule; + + /* Rx check table */ + flow_table = macsec_fs_auto_group_table_create(ns, + MLX5_FLOW_TABLE_TUNNEL_EN_REFORMAT, + RX_CHECK_TABLE_LEVEL, + RX_CHECK_TABLE_NUM_FTE); + if (IS_ERR(flow_table)) { + err = PTR_ERR(flow_table); + netdev_err(netdev, "fail to create MACsec RX check table, err(%d)\n", err); + goto err; + } + rx_tables->ft_check = flow_table; + + /* Rx check table Default miss group/rule */ + MLX5_SET(create_flow_group_in, flow_group_in, start_flow_index, flow_table->max_fte - 1); + MLX5_SET(create_flow_group_in, flow_group_in, end_flow_index, flow_table->max_fte - 1); + flow_group = mlx5_create_flow_group(rx_tables->ft_check, flow_group_in); + if (IS_ERR(flow_group)) { + err = PTR_ERR(flow_group); + netdev_err(netdev, + "Failed to create default flow group for MACsec Rx check table err(%d)\n", + err); + goto err; + } + rx_tables->ft_check_group = flow_group; + + /* Rx check table default drop rule */ + memset(&flow_act, 0, sizeof(flow_act)); + + dest.type = MLX5_FLOW_DESTINATION_TYPE_COUNTER; + dest.counter_id = mlx5_fc_id(rx_tables->check_miss_rule_counter); + flow_act.action = MLX5_FLOW_CONTEXT_ACTION_DROP | MLX5_FLOW_CONTEXT_ACTION_COUNT; + rule = mlx5_add_flow_rules(rx_tables->ft_check, NULL, &flow_act, &dest, 1); + if (IS_ERR(rule)) { + err = PTR_ERR(rule); + netdev_err(netdev, "Failed to added MACsec Rx check drop rule, err(%d)\n", err); + goto err; + } + rx_tables->check_miss_rule = rule; + + /* Rx check table decap rules */ + err = macsec_fs_rx_create_check_decap_rule(macsec_fs, &dest, &flow_act, spec, + MLX5_SECTAG_HEADER_SIZE_WITH_SCI); + if (err) + goto err; + + err = macsec_fs_rx_create_check_decap_rule(macsec_fs, &dest, &flow_act, spec, + MLX5_SECTAG_HEADER_SIZE_WITHOUT_SCI); + if (err) + goto err; + + goto out_flow_group; + +err: + macsec_fs_rx_destroy(macsec_fs); +out_flow_group: + kvfree(flow_group_in); +free_spec: + kvfree(spec); + return err; +} + +static int macsec_fs_rx_ft_get(struct mlx5e_macsec_fs *macsec_fs) +{ + struct mlx5e_macsec_tables *rx_tables = &macsec_fs->rx_fs->tables; + int err = 0; + + if (rx_tables->refcnt) + goto out; + + err = macsec_fs_rx_create(macsec_fs); + if (err) + return err; + +out: + rx_tables->refcnt++; + return err; +} + +static void macsec_fs_rx_ft_put(struct mlx5e_macsec_fs *macsec_fs) +{ + struct mlx5e_macsec_tables *rx_tables = &macsec_fs->rx_fs->tables; + + if (--rx_tables->refcnt) + return; + + macsec_fs_rx_destroy(macsec_fs); +} + +static void macsec_fs_rx_del_rule(struct mlx5e_macsec_fs *macsec_fs, + struct mlx5e_macsec_rx_rule *rx_rule) +{ + int i; + + for (i = 0; i < RX_NUM_OF_RULES_PER_SA; ++i) { + if (rx_rule->rule[i]) { + mlx5_del_flow_rules(rx_rule->rule[i]); + rx_rule->rule[i] = NULL; + } + } + + if (rx_rule->meta_modhdr) { + mlx5_modify_header_dealloc(macsec_fs->mdev, rx_rule->meta_modhdr); + rx_rule->meta_modhdr = NULL; + } + + kfree(rx_rule); + + macsec_fs_rx_ft_put(macsec_fs); +} + +static void macsec_fs_rx_setup_fte(struct mlx5_flow_spec *spec, + struct mlx5_flow_act *flow_act, + struct mlx5_macsec_rule_attrs *attrs, + bool sci_present) +{ + u8 tci_an = (sci_present << MLX5_MACSEC_SECTAG_TCI_SC_FIELD_OFFSET) | attrs->assoc_num; + struct mlx5_flow_act_crypto_params *crypto_params = &flow_act->crypto; + __be32 *sci_p = (__be32 *)(&attrs->sci); + + spec->match_criteria_enable = MLX5_MATCH_OUTER_HEADERS; + + /* MACsec ethertype */ + MLX5_SET_TO_ONES(fte_match_param, spec->match_criteria, outer_headers.ethertype); + MLX5_SET(fte_match_param, spec->match_value, outer_headers.ethertype, ETH_P_MACSEC); + + spec->match_criteria_enable |= MLX5_MATCH_MISC_PARAMETERS_5; + + /* Sectag AN + TCI SC present bit*/ + MLX5_SET(fte_match_param, spec->match_criteria, misc_parameters_5.macsec_tag_0, + MLX5_MACSEC_SECTAG_TCI_AN_FIELD_BITMASK << MLX5_MACSEC_SECTAG_TCI_AN_FIELD_OFFSET); + MLX5_SET(fte_match_param, spec->match_value, misc_parameters_5.macsec_tag_0, + tci_an << MLX5_MACSEC_SECTAG_TCI_AN_FIELD_OFFSET); + + if (sci_present) { + MLX5_SET_TO_ONES(fte_match_param, spec->match_criteria, + misc_parameters_5.macsec_tag_2); + MLX5_SET(fte_match_param, spec->match_value, misc_parameters_5.macsec_tag_2, + be32_to_cpu(sci_p[0])); + + MLX5_SET_TO_ONES(fte_match_param, spec->match_criteria, + misc_parameters_5.macsec_tag_3); + MLX5_SET(fte_match_param, spec->match_value, misc_parameters_5.macsec_tag_3, + be32_to_cpu(sci_p[1])); + } else { + /* When SCI isn't present in the Sectag, need to match the source */ + /* MAC address only if the SCI contains the default MACsec PORT */ + MLX5_SET_TO_ONES(fte_match_param, spec->match_criteria, outer_headers.smac_47_16); + MLX5_SET_TO_ONES(fte_match_param, spec->match_criteria, outer_headers.smac_15_0); + memcpy(MLX5_ADDR_OF(fte_match_param, spec->match_value, outer_headers.smac_47_16), + sci_p, ETH_ALEN); + } + + crypto_params->type = MLX5_FLOW_CONTEXT_ENCRYPT_DECRYPT_TYPE_MACSEC; + crypto_params->obj_id = attrs->macsec_obj_id; +} + +static union mlx5e_macsec_rule * +macsec_fs_rx_add_rule(struct mlx5e_macsec_fs *macsec_fs, + const struct macsec_context *macsec_ctx, + struct mlx5_macsec_rule_attrs *attrs, + u32 fs_id) +{ + u8 action[MLX5_UN_SZ_BYTES(set_add_copy_action_in_auto)] = {}; + struct mlx5e_macsec_rx *rx_fs = macsec_fs->rx_fs; + struct net_device *netdev = macsec_fs->netdev; + union mlx5e_macsec_rule *macsec_rule = NULL; + struct mlx5_modify_hdr *modify_hdr = NULL; + struct mlx5_flow_destination dest = {}; + struct mlx5e_macsec_tables *rx_tables; + struct mlx5e_macsec_rx_rule *rx_rule; + struct mlx5_flow_act flow_act = {}; + struct mlx5e_flow_table *ft_crypto; + struct mlx5_flow_handle *rule; + struct mlx5_flow_spec *spec; + int err = 0; + + spec = kvzalloc(sizeof(*spec), GFP_KERNEL); + if (!spec) + return NULL; + + err = macsec_fs_rx_ft_get(macsec_fs); + if (err) + goto out_spec; + + macsec_rule = kzalloc(sizeof(*macsec_rule), GFP_KERNEL); + if (!macsec_rule) { + macsec_fs_rx_ft_put(macsec_fs); + goto out_spec; + } + + rx_rule = &macsec_rule->rx_rule; + rx_tables = &rx_fs->tables; + ft_crypto = &rx_tables->ft_crypto; + + /* Set bit[31 - 30] macsec marker - 0x01 */ + /* Set bit[3-0] fs id */ + MLX5_SET(set_action_in, action, action_type, MLX5_ACTION_TYPE_SET); + MLX5_SET(set_action_in, action, field, MLX5_ACTION_IN_FIELD_METADATA_REG_B); + MLX5_SET(set_action_in, action, data, fs_id | BIT(30)); + MLX5_SET(set_action_in, action, offset, 0); + MLX5_SET(set_action_in, action, length, 32); + + modify_hdr = mlx5_modify_header_alloc(macsec_fs->mdev, MLX5_FLOW_NAMESPACE_KERNEL_RX_MACSEC, + 1, action); + if (IS_ERR(modify_hdr)) { + err = PTR_ERR(modify_hdr); + netdev_err(netdev, "fail to alloc MACsec set modify_header_id err=%d\n", err); + modify_hdr = NULL; + goto err; + } + rx_rule->meta_modhdr = modify_hdr; + + /* Rx crypto table with SCI rule */ + macsec_fs_rx_setup_fte(spec, &flow_act, attrs, true); + + flow_act.modify_hdr = modify_hdr; + flow_act.action = MLX5_FLOW_CONTEXT_ACTION_FWD_DEST | + MLX5_FLOW_CONTEXT_ACTION_CRYPTO_DECRYPT | + MLX5_FLOW_CONTEXT_ACTION_MOD_HDR; + + dest.type = MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE; + dest.ft = rx_tables->ft_check; + rule = mlx5_add_flow_rules(ft_crypto->t, spec, &flow_act, &dest, 1); + if (IS_ERR(rule)) { + err = PTR_ERR(rule); + netdev_err(netdev, + "Failed to add SA with SCI rule to Rx crypto rule, err=%d\n", + err); + goto err; + } + rx_rule->rule[0] = rule; + + /* Rx crypto table without SCI rule */ + if (cpu_to_be64((__force u64)attrs->sci) & ntohs(MACSEC_PORT_ES)) { + memset(spec, 0, sizeof(struct mlx5_flow_spec)); + memset(&dest, 0, sizeof(struct mlx5_flow_destination)); + memset(&flow_act, 0, sizeof(flow_act)); + + macsec_fs_rx_setup_fte(spec, &flow_act, attrs, false); + + flow_act.modify_hdr = modify_hdr; + flow_act.action = MLX5_FLOW_CONTEXT_ACTION_FWD_DEST | + MLX5_FLOW_CONTEXT_ACTION_CRYPTO_DECRYPT | + MLX5_FLOW_CONTEXT_ACTION_MOD_HDR; + + dest.type = MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE; + dest.ft = rx_tables->ft_check; + rule = mlx5_add_flow_rules(ft_crypto->t, spec, &flow_act, &dest, 1); + if (IS_ERR(rule)) { + err = PTR_ERR(rule); + netdev_err(netdev, + "Failed to add SA without SCI rule to Rx crypto rule, err=%d\n", + err); + goto err; + } + rx_rule->rule[1] = rule; + } + + return macsec_rule; + +err: + macsec_fs_rx_del_rule(macsec_fs, rx_rule); + macsec_rule = NULL; +out_spec: + kvfree(spec); + return macsec_rule; +} + +static int macsec_fs_rx_init(struct mlx5e_macsec_fs *macsec_fs) +{ + struct net_device *netdev = macsec_fs->netdev; + struct mlx5_core_dev *mdev = macsec_fs->mdev; + struct mlx5e_macsec_tables *rx_tables; + struct mlx5e_macsec_rx *rx_fs; + struct mlx5_fc *flow_counter; + int err; + + rx_fs = kzalloc(sizeof(*rx_fs), GFP_KERNEL); + if (!rx_fs) + return -ENOMEM; + + flow_counter = mlx5_fc_create(mdev, false); + if (IS_ERR(flow_counter)) { + err = PTR_ERR(flow_counter); + netdev_err(netdev, + "Failed to create MACsec Rx encrypt flow counter, err(%d)\n", + err); + goto err_encrypt_counter; + } + + rx_tables = &rx_fs->tables; + rx_tables->check_rule_counter = flow_counter; + + flow_counter = mlx5_fc_create(mdev, false); + if (IS_ERR(flow_counter)) { + err = PTR_ERR(flow_counter); + netdev_err(netdev, + "Failed to create MACsec Rx drop flow counter, err(%d)\n", + err); + goto err_drop_counter; + } + rx_tables->check_miss_rule_counter = flow_counter; + + macsec_fs->rx_fs = rx_fs; + + return 0; + +err_drop_counter: + mlx5_fc_destroy(mdev, rx_tables->check_rule_counter); + rx_tables->check_rule_counter = NULL; + +err_encrypt_counter: + kfree(rx_fs); + macsec_fs->rx_fs = NULL; + + return err; +} + +static void macsec_fs_rx_cleanup(struct mlx5e_macsec_fs *macsec_fs) +{ + struct mlx5e_macsec_rx *rx_fs = macsec_fs->rx_fs; + struct mlx5_core_dev *mdev = macsec_fs->mdev; + struct mlx5e_macsec_tables *rx_tables; + + if (!rx_fs) + return; + + rx_tables = &rx_fs->tables; + + if (rx_tables->refcnt) { + netdev_err(macsec_fs->netdev, + "Can't destroy MACsec offload rx_fs, refcnt(%u) isn't 0\n", + rx_tables->refcnt); + return; + } + + if (rx_tables->check_miss_rule_counter) { + mlx5_fc_destroy(mdev, rx_tables->check_miss_rule_counter); + rx_tables->check_miss_rule_counter = NULL; + } + + if (rx_tables->check_rule_counter) { + mlx5_fc_destroy(mdev, rx_tables->check_rule_counter); + rx_tables->check_rule_counter = NULL; + } + + kfree(rx_fs); + macsec_fs->rx_fs = NULL; +} + +void mlx5e_macsec_fs_get_stats_fill(struct mlx5e_macsec_fs *macsec_fs, void *macsec_stats) +{ + struct mlx5e_macsec_stats *stats = (struct mlx5e_macsec_stats *)macsec_stats; + struct mlx5e_macsec_tables *tx_tables = &macsec_fs->tx_fs->tables; + struct mlx5e_macsec_tables *rx_tables = &macsec_fs->rx_fs->tables; + struct mlx5_core_dev *mdev = macsec_fs->mdev; + + if (tx_tables->check_rule_counter) + mlx5_fc_query(mdev, tx_tables->check_rule_counter, + &stats->macsec_tx_pkts, &stats->macsec_tx_bytes); + + if (tx_tables->check_miss_rule_counter) + mlx5_fc_query(mdev, tx_tables->check_miss_rule_counter, + &stats->macsec_tx_pkts_drop, &stats->macsec_tx_bytes_drop); + + if (rx_tables->check_rule_counter) + mlx5_fc_query(mdev, rx_tables->check_rule_counter, + &stats->macsec_rx_pkts, &stats->macsec_rx_bytes); + + if (rx_tables->check_miss_rule_counter) + mlx5_fc_query(mdev, rx_tables->check_miss_rule_counter, + &stats->macsec_rx_pkts_drop, &stats->macsec_rx_bytes_drop); +} + +union mlx5e_macsec_rule * +mlx5e_macsec_fs_add_rule(struct mlx5e_macsec_fs *macsec_fs, + const struct macsec_context *macsec_ctx, + struct mlx5_macsec_rule_attrs *attrs, + u32 *sa_fs_id) +{ + return (attrs->action == MLX5_ACCEL_MACSEC_ACTION_ENCRYPT) ? + macsec_fs_tx_add_rule(macsec_fs, macsec_ctx, attrs, sa_fs_id) : + macsec_fs_rx_add_rule(macsec_fs, macsec_ctx, attrs, *sa_fs_id); +} + +void mlx5e_macsec_fs_del_rule(struct mlx5e_macsec_fs *macsec_fs, + union mlx5e_macsec_rule *macsec_rule, + int action) +{ + (action == MLX5_ACCEL_MACSEC_ACTION_ENCRYPT) ? + macsec_fs_tx_del_rule(macsec_fs, &macsec_rule->tx_rule) : + macsec_fs_rx_del_rule(macsec_fs, &macsec_rule->rx_rule); +} + +void mlx5e_macsec_fs_cleanup(struct mlx5e_macsec_fs *macsec_fs) +{ + macsec_fs_rx_cleanup(macsec_fs); + macsec_fs_tx_cleanup(macsec_fs); + kfree(macsec_fs); +} + +struct mlx5e_macsec_fs * +mlx5e_macsec_fs_init(struct mlx5_core_dev *mdev, + struct net_device *netdev) +{ + struct mlx5e_macsec_fs *macsec_fs; + int err; + + macsec_fs = kzalloc(sizeof(*macsec_fs), GFP_KERNEL); + if (!macsec_fs) + return NULL; + + macsec_fs->mdev = mdev; + macsec_fs->netdev = netdev; + + err = macsec_fs_tx_init(macsec_fs); + if (err) { + netdev_err(netdev, "MACsec offload: Failed to init tx_fs, err=%d\n", err); + goto err; + } + + err = macsec_fs_rx_init(macsec_fs); + if (err) { + netdev_err(netdev, "MACsec offload: Failed to init tx_fs, err=%d\n", err); + goto tx_cleanup; + } + + return macsec_fs; + +tx_cleanup: + macsec_fs_tx_cleanup(macsec_fs); +err: + kfree(macsec_fs); + return NULL; +} diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/macsec_fs.h b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/macsec_fs.h new file mode 100644 index 000000000000..b429648d4ee7 --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/macsec_fs.h @@ -0,0 +1,47 @@ +/* SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB */ +/* Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. */ + +#ifndef __MLX5_MACSEC_STEERING_H__ +#define __MLX5_MACSEC_STEERING_H__ + +#ifdef CONFIG_MLX5_EN_MACSEC + +#include "en_accel/macsec.h" + +#define MLX5_MACSEC_NUM_OF_SUPPORTED_INTERFACES 16 + +struct mlx5e_macsec_fs; +union mlx5e_macsec_rule; + +struct mlx5_macsec_rule_attrs { + sci_t sci; + u32 macsec_obj_id; + u8 assoc_num; + int action; +}; + +enum mlx5_macsec_action { + MLX5_ACCEL_MACSEC_ACTION_ENCRYPT, + MLX5_ACCEL_MACSEC_ACTION_DECRYPT, +}; + +void mlx5e_macsec_fs_cleanup(struct mlx5e_macsec_fs *macsec_fs); + +struct mlx5e_macsec_fs * +mlx5e_macsec_fs_init(struct mlx5_core_dev *mdev, struct net_device *netdev); + +union mlx5e_macsec_rule * +mlx5e_macsec_fs_add_rule(struct mlx5e_macsec_fs *macsec_fs, + const struct macsec_context *ctx, + struct mlx5_macsec_rule_attrs *attrs, + u32 *sa_fs_id); + +void mlx5e_macsec_fs_del_rule(struct mlx5e_macsec_fs *macsec_fs, + union mlx5e_macsec_rule *macsec_rule, + int action); + +void mlx5e_macsec_fs_get_stats_fill(struct mlx5e_macsec_fs *macsec_fs, void *macsec_stats); + +#endif + +#endif /* __MLX5_MACSEC_STEERING_H__ */ diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/macsec_stats.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/macsec_stats.c new file mode 100644 index 000000000000..e50a2e3f3d18 --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/macsec_stats.c @@ -0,0 +1,72 @@ +// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB +/* Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. */ + +#include <linux/ethtool.h> +#include <net/sock.h> + +#include "en.h" +#include "en_accel/macsec.h" + +static const struct counter_desc mlx5e_macsec_hw_stats_desc[] = { + { MLX5E_DECLARE_STAT(struct mlx5e_macsec_stats, macsec_rx_pkts) }, + { MLX5E_DECLARE_STAT(struct mlx5e_macsec_stats, macsec_rx_bytes) }, + { MLX5E_DECLARE_STAT(struct mlx5e_macsec_stats, macsec_rx_pkts_drop) }, + { MLX5E_DECLARE_STAT(struct mlx5e_macsec_stats, macsec_rx_bytes_drop) }, + { MLX5E_DECLARE_STAT(struct mlx5e_macsec_stats, macsec_tx_pkts) }, + { MLX5E_DECLARE_STAT(struct mlx5e_macsec_stats, macsec_tx_bytes) }, + { MLX5E_DECLARE_STAT(struct mlx5e_macsec_stats, macsec_tx_pkts_drop) }, + { MLX5E_DECLARE_STAT(struct mlx5e_macsec_stats, macsec_tx_bytes_drop) }, +}; + +#define NUM_MACSEC_HW_COUNTERS ARRAY_SIZE(mlx5e_macsec_hw_stats_desc) + +static MLX5E_DECLARE_STATS_GRP_OP_NUM_STATS(macsec_hw) +{ + if (!priv->macsec) + return 0; + + if (mlx5e_is_macsec_device(priv->mdev)) + return NUM_MACSEC_HW_COUNTERS; + + return 0; +} + +static MLX5E_DECLARE_STATS_GRP_OP_UPDATE_STATS(macsec_hw) {} + +static MLX5E_DECLARE_STATS_GRP_OP_FILL_STRS(macsec_hw) +{ + unsigned int i; + + if (!priv->macsec) + return idx; + + if (!mlx5e_is_macsec_device(priv->mdev)) + return idx; + + for (i = 0; i < NUM_MACSEC_HW_COUNTERS; i++) + strcpy(data + (idx++) * ETH_GSTRING_LEN, + mlx5e_macsec_hw_stats_desc[i].format); + + return idx; +} + +static MLX5E_DECLARE_STATS_GRP_OP_FILL_STATS(macsec_hw) +{ + int i; + + if (!priv->macsec) + return idx; + + if (!mlx5e_is_macsec_device(priv->mdev)) + return idx; + + mlx5e_macsec_get_stats_fill(priv->macsec, mlx5e_macsec_get_stats(priv->macsec)); + for (i = 0; i < NUM_MACSEC_HW_COUNTERS; i++) + data[idx++] = MLX5E_READ_CTR64_CPU(mlx5e_macsec_get_stats(priv->macsec), + mlx5e_macsec_hw_stats_desc, + i); + + return idx; +} + +MLX5E_DEFINE_STATS_GRP(macsec_hw, 0); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_arfs.c b/drivers/net/ethernet/mellanox/mlx5/core/en_arfs.c index cd7f245dcf14..0ae1865086ff 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_arfs.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_arfs.c @@ -114,47 +114,49 @@ static enum mlx5_traffic_types arfs_get_tt(enum arfs_type type) } } -static int arfs_disable(struct mlx5e_priv *priv) +static int arfs_disable(struct mlx5e_flow_steering *fs) { + struct mlx5_ttc_table *ttc = mlx5e_fs_get_ttc(fs, false); int err, i; for (i = 0; i < ARFS_NUM_TYPES; i++) { /* Modify ttc rules destination back to their default */ - err = mlx5_ttc_fwd_default_dest(priv->fs->ttc, arfs_get_tt(i)); + err = mlx5_ttc_fwd_default_dest(ttc, arfs_get_tt(i)); if (err) { - netdev_err(priv->netdev, - "%s: modify ttc[%d] default destination failed, err(%d)\n", - __func__, arfs_get_tt(i), err); + fs_err(fs, + "%s: modify ttc[%d] default destination failed, err(%d)\n", + __func__, arfs_get_tt(i), err); return err; } } return 0; } -static void arfs_del_rules(struct mlx5e_priv *priv); +static void arfs_del_rules(struct mlx5e_flow_steering *fs); -int mlx5e_arfs_disable(struct mlx5e_priv *priv) +int mlx5e_arfs_disable(struct mlx5e_flow_steering *fs) { - arfs_del_rules(priv); + arfs_del_rules(fs); - return arfs_disable(priv); + return arfs_disable(fs); } -int mlx5e_arfs_enable(struct mlx5e_priv *priv) +int mlx5e_arfs_enable(struct mlx5e_flow_steering *fs) { + struct mlx5_ttc_table *ttc = mlx5e_fs_get_ttc(fs, false); + struct mlx5e_arfs_tables *arfs = mlx5e_fs_get_arfs(fs); struct mlx5_flow_destination dest = {}; int err, i; dest.type = MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE; for (i = 0; i < ARFS_NUM_TYPES; i++) { - dest.ft = priv->fs->arfs->arfs_tables[i].ft.t; + dest.ft = arfs->arfs_tables[i].ft.t; /* Modify ttc rules destination to point on the aRFS FTs */ - err = mlx5_ttc_fwd_dest(priv->fs->ttc, arfs_get_tt(i), &dest); + err = mlx5_ttc_fwd_dest(ttc, arfs_get_tt(i), &dest); if (err) { - netdev_err(priv->netdev, - "%s: modify ttc[%d] dest to arfs, failed err(%d)\n", - __func__, arfs_get_tt(i), err); - arfs_disable(priv); + fs_err(fs, "%s: modify ttc[%d] dest to arfs, failed err(%d)\n", + __func__, arfs_get_tt(i), err); + arfs_disable(fs); return err; } } @@ -167,31 +169,37 @@ static void arfs_destroy_table(struct arfs_table *arfs_t) mlx5e_destroy_flow_table(&arfs_t->ft); } -static void _mlx5e_cleanup_tables(struct mlx5e_priv *priv) +static void _mlx5e_cleanup_tables(struct mlx5e_flow_steering *fs) { + struct mlx5e_arfs_tables *arfs = mlx5e_fs_get_arfs(fs); int i; - arfs_del_rules(priv); - destroy_workqueue(priv->fs->arfs->wq); + arfs_del_rules(fs); + destroy_workqueue(arfs->wq); for (i = 0; i < ARFS_NUM_TYPES; i++) { - if (!IS_ERR_OR_NULL(priv->fs->arfs->arfs_tables[i].ft.t)) - arfs_destroy_table(&priv->fs->arfs->arfs_tables[i]); + if (!IS_ERR_OR_NULL(arfs->arfs_tables[i].ft.t)) + arfs_destroy_table(&arfs->arfs_tables[i]); } } -void mlx5e_arfs_destroy_tables(struct mlx5e_priv *priv) +void mlx5e_arfs_destroy_tables(struct mlx5e_flow_steering *fs, bool ntuple) { - if (!(priv->netdev->hw_features & NETIF_F_NTUPLE)) + struct mlx5e_arfs_tables *arfs = mlx5e_fs_get_arfs(fs); + + if (!ntuple) return; - _mlx5e_cleanup_tables(priv); - kvfree(priv->fs->arfs); + _mlx5e_cleanup_tables(fs); + mlx5e_fs_set_arfs(fs, NULL); + kvfree(arfs); } -static int arfs_add_default_rule(struct mlx5e_priv *priv, +static int arfs_add_default_rule(struct mlx5e_flow_steering *fs, + struct mlx5e_rx_res *rx_res, enum arfs_type type) { - struct arfs_table *arfs_t = &priv->fs->arfs->arfs_tables[type]; + struct mlx5e_arfs_tables *arfs = mlx5e_fs_get_arfs(fs); + struct arfs_table *arfs_t = &arfs->arfs_tables[type]; struct mlx5_flow_destination dest = {}; MLX5_DECLARE_FLOW_ACT(flow_act); enum mlx5_traffic_types tt; @@ -200,23 +208,21 @@ static int arfs_add_default_rule(struct mlx5e_priv *priv, dest.type = MLX5_FLOW_DESTINATION_TYPE_TIR; tt = arfs_get_tt(type); if (tt == -EINVAL) { - netdev_err(priv->netdev, "%s: bad arfs_type: %d\n", - __func__, type); + fs_err(fs, "%s: bad arfs_type: %d\n", __func__, type); return -EINVAL; } /* FIXME: Must use mlx5_ttc_get_default_dest(), * but can't since TTC default is not setup yet ! */ - dest.tir_num = mlx5e_rx_res_get_tirn_rss(priv->rx_res, tt); + dest.tir_num = mlx5e_rx_res_get_tirn_rss(rx_res, tt); arfs_t->default_rule = mlx5_add_flow_rules(arfs_t->ft.t, NULL, &flow_act, &dest, 1); if (IS_ERR(arfs_t->default_rule)) { err = PTR_ERR(arfs_t->default_rule); arfs_t->default_rule = NULL; - netdev_err(priv->netdev, "%s: add rule failed, arfs type=%d\n", - __func__, type); + fs_err(fs, "%s: add rule failed, arfs type=%d\n", __func__, type); } return err; @@ -318,10 +324,12 @@ out: return err; } -static int arfs_create_table(struct mlx5e_priv *priv, +static int arfs_create_table(struct mlx5e_flow_steering *fs, + struct mlx5e_rx_res *rx_res, enum arfs_type type) { - struct mlx5e_arfs_tables *arfs = priv->fs->arfs; + struct mlx5_flow_namespace *ns = mlx5e_fs_get_ns(fs, false); + struct mlx5e_arfs_tables *arfs = mlx5e_fs_get_arfs(fs); struct mlx5e_flow_table *ft = &arfs->arfs_tables[type].ft; struct mlx5_flow_table_attr ft_attr = {}; int err; @@ -332,7 +340,7 @@ static int arfs_create_table(struct mlx5e_priv *priv, ft_attr.level = MLX5E_ARFS_FT_LEVEL; ft_attr.prio = MLX5E_NIC_PRIO; - ft->t = mlx5_create_flow_table(priv->fs->ns, &ft_attr); + ft->t = mlx5_create_flow_table(ns, &ft_attr); if (IS_ERR(ft->t)) { err = PTR_ERR(ft->t); ft->t = NULL; @@ -343,7 +351,7 @@ static int arfs_create_table(struct mlx5e_priv *priv, if (err) goto err; - err = arfs_add_default_rule(priv, type); + err = arfs_add_default_rule(fs, rx_res, type); if (err) goto err; @@ -353,35 +361,40 @@ err: return err; } -int mlx5e_arfs_create_tables(struct mlx5e_priv *priv) +int mlx5e_arfs_create_tables(struct mlx5e_flow_steering *fs, + struct mlx5e_rx_res *rx_res, bool ntuple) { + struct mlx5e_arfs_tables *arfs; int err = -ENOMEM; int i; - if (!(priv->netdev->hw_features & NETIF_F_NTUPLE)) + if (!ntuple) return 0; - priv->fs->arfs = kvzalloc(sizeof(*priv->fs->arfs), GFP_KERNEL); - if (!priv->fs->arfs) + arfs = kvzalloc(sizeof(*arfs), GFP_KERNEL); + if (!arfs) return -ENOMEM; - spin_lock_init(&priv->fs->arfs->arfs_lock); - INIT_LIST_HEAD(&priv->fs->arfs->rules); - priv->fs->arfs->wq = create_singlethread_workqueue("mlx5e_arfs"); - if (!priv->fs->arfs->wq) + spin_lock_init(&arfs->arfs_lock); + INIT_LIST_HEAD(&arfs->rules); + arfs->wq = create_singlethread_workqueue("mlx5e_arfs"); + if (!arfs->wq) goto err; + mlx5e_fs_set_arfs(fs, arfs); + for (i = 0; i < ARFS_NUM_TYPES; i++) { - err = arfs_create_table(priv, i); + err = arfs_create_table(fs, rx_res, i); if (err) goto err_des; } return 0; err_des: - _mlx5e_cleanup_tables(priv); + _mlx5e_cleanup_tables(fs); err: - kvfree(priv->fs->arfs); + mlx5e_fs_set_arfs(fs, NULL); + kvfree(arfs); return err; } @@ -389,6 +402,7 @@ err: static void arfs_may_expire_flow(struct mlx5e_priv *priv) { + struct mlx5e_arfs_tables *arfs = mlx5e_fs_get_arfs(priv->fs); struct arfs_rule *arfs_rule; struct hlist_node *htmp; HLIST_HEAD(del_list); @@ -396,8 +410,8 @@ static void arfs_may_expire_flow(struct mlx5e_priv *priv) int i; int j; - spin_lock_bh(&priv->fs->arfs->arfs_lock); - mlx5e_for_each_arfs_rule(arfs_rule, htmp, priv->fs->arfs->arfs_tables, i, j) { + spin_lock_bh(&arfs->arfs_lock); + mlx5e_for_each_arfs_rule(arfs_rule, htmp, arfs->arfs_tables, i, j) { if (!work_pending(&arfs_rule->arfs_work) && rps_may_expire_flow(priv->netdev, arfs_rule->rxq, arfs_rule->flow_id, @@ -408,7 +422,7 @@ static void arfs_may_expire_flow(struct mlx5e_priv *priv) break; } } - spin_unlock_bh(&priv->fs->arfs->arfs_lock); + spin_unlock_bh(&arfs->arfs_lock); hlist_for_each_entry_safe(arfs_rule, htmp, &del_list, hlist) { if (arfs_rule->rule) mlx5_del_flow_rules(arfs_rule->rule); @@ -417,20 +431,21 @@ static void arfs_may_expire_flow(struct mlx5e_priv *priv) } } -static void arfs_del_rules(struct mlx5e_priv *priv) +static void arfs_del_rules(struct mlx5e_flow_steering *fs) { + struct mlx5e_arfs_tables *arfs = mlx5e_fs_get_arfs(fs); struct hlist_node *htmp; struct arfs_rule *rule; HLIST_HEAD(del_list); int i; int j; - spin_lock_bh(&priv->fs->arfs->arfs_lock); - mlx5e_for_each_arfs_rule(rule, htmp, priv->fs->arfs->arfs_tables, i, j) { + spin_lock_bh(&arfs->arfs_lock); + mlx5e_for_each_arfs_rule(rule, htmp, arfs->arfs_tables, i, j) { hlist_del_init(&rule->hlist); hlist_add_head(&rule->hlist, &del_list); } - spin_unlock_bh(&priv->fs->arfs->arfs_lock); + spin_unlock_bh(&arfs->arfs_lock); hlist_for_each_entry_safe(rule, htmp, &del_list, hlist) { cancel_work_sync(&rule->arfs_work); @@ -474,7 +489,7 @@ static struct arfs_table *arfs_get_table(struct mlx5e_arfs_tables *arfs, static struct mlx5_flow_handle *arfs_add_rule(struct mlx5e_priv *priv, struct arfs_rule *arfs_rule) { - struct mlx5e_arfs_tables *arfs = priv->fs->arfs; + struct mlx5e_arfs_tables *arfs = mlx5e_fs_get_arfs(priv->fs); struct arfs_tuple *tuple = &arfs_rule->tuple; struct mlx5_flow_handle *rule = NULL; struct mlx5_flow_destination dest = {}; @@ -588,13 +603,15 @@ static void arfs_handle_work(struct work_struct *work) struct arfs_rule, arfs_work); struct mlx5e_priv *priv = arfs_rule->priv; + struct mlx5e_arfs_tables *arfs; struct mlx5_flow_handle *rule; + arfs = mlx5e_fs_get_arfs(priv->fs); mutex_lock(&priv->state_lock); if (!test_bit(MLX5E_STATE_OPENED, &priv->state)) { - spin_lock_bh(&priv->fs->arfs->arfs_lock); + spin_lock_bh(&arfs->arfs_lock); hlist_del(&arfs_rule->hlist); - spin_unlock_bh(&priv->fs->arfs->arfs_lock); + spin_unlock_bh(&arfs->arfs_lock); mutex_unlock(&priv->state_lock); kfree(arfs_rule); @@ -620,6 +637,7 @@ static struct arfs_rule *arfs_alloc_rule(struct mlx5e_priv *priv, const struct flow_keys *fk, u16 rxq, u32 flow_id) { + struct mlx5e_arfs_tables *arfs = mlx5e_fs_get_arfs(priv->fs); struct arfs_rule *rule; struct arfs_tuple *tuple; @@ -647,7 +665,7 @@ static struct arfs_rule *arfs_alloc_rule(struct mlx5e_priv *priv, tuple->dst_port = fk->ports.dst; rule->flow_id = flow_id; - rule->filter_id = priv->fs->arfs->last_filter_id++ % RPS_NO_FILTER; + rule->filter_id = arfs->last_filter_id++ % RPS_NO_FILTER; hlist_add_head(&rule->hlist, arfs_hash_bucket(arfs_t, tuple->src_port, @@ -691,11 +709,12 @@ int mlx5e_rx_flow_steer(struct net_device *dev, const struct sk_buff *skb, u16 rxq_index, u32 flow_id) { struct mlx5e_priv *priv = netdev_priv(dev); - struct mlx5e_arfs_tables *arfs = priv->fs->arfs; - struct arfs_table *arfs_t; + struct mlx5e_arfs_tables *arfs; struct arfs_rule *arfs_rule; + struct arfs_table *arfs_t; struct flow_keys fk; + arfs = mlx5e_fs_get_arfs(priv->fs); if (!skb_flow_dissect_flow_keys(skb, &fk, 0)) return -EPROTONOSUPPORT; @@ -725,7 +744,7 @@ int mlx5e_rx_flow_steer(struct net_device *dev, const struct sk_buff *skb, return -ENOMEM; } } - queue_work(priv->fs->arfs->wq, &arfs_rule->arfs_work); + queue_work(arfs->wq, &arfs_rule->arfs_work); spin_unlock_bh(&arfs->arfs_lock); return arfs_rule->filter_id; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_common.c b/drivers/net/ethernet/mellanox/mlx5/core/en_common.c index c0f409c195bf..68f19324db93 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_common.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_common.c @@ -46,8 +46,7 @@ void mlx5e_mkey_set_relaxed_ordering(struct mlx5_core_dev *mdev, void *mkc) MLX5_SET(mkc, mkc, relaxed_ordering_write, ro_pci_enable && ro_write); } -static int mlx5e_create_mkey(struct mlx5_core_dev *mdev, u32 pdn, - u32 *mkey) +int mlx5e_create_mkey(struct mlx5_core_dev *mdev, u32 pdn, u32 *mkey) { int inlen = MLX5_ST_SZ_BYTES(create_mkey_in); void *mkc; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c b/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c index b811207fe5ed..24aa25da482b 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c @@ -38,18 +38,19 @@ #include "en/xsk/pool.h" #include "en/ptp.h" #include "lib/clock.h" +#include "en/fs_ethtool.h" void mlx5e_ethtool_get_drvinfo(struct mlx5e_priv *priv, struct ethtool_drvinfo *drvinfo) { struct mlx5_core_dev *mdev = priv->mdev; - strlcpy(drvinfo->driver, KBUILD_MODNAME, sizeof(drvinfo->driver)); + strscpy(drvinfo->driver, KBUILD_MODNAME, sizeof(drvinfo->driver)); snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version), "%d.%d.%04d (%.16s)", fw_rev_maj(mdev), fw_rev_min(mdev), fw_rev_sub(mdev), mdev->board_id); - strlcpy(drvinfo->bus_info, dev_name(mdev->device), + strscpy(drvinfo->bus_info, dev_name(mdev->device), sizeof(drvinfo->bus_info)); } @@ -310,7 +311,15 @@ void mlx5e_ethtool_get_ringparam(struct mlx5e_priv *priv, struct ethtool_ringparam *param, struct kernel_ethtool_ringparam *kernel_param) { - param->rx_max_pending = 1 << MLX5E_PARAMS_MAXIMUM_LOG_RQ_SIZE; + /* Limitation for regular RQ. XSK RQ may clamp the queue length in + * mlx5e_mpwqe_get_log_rq_size. + */ + u8 max_log_mpwrq_pkts = mlx5e_mpwrq_max_log_rq_pkts(priv->mdev, + PAGE_SHIFT, + MLX5E_MPWRQ_UMR_MODE_ALIGNED); + + param->rx_max_pending = 1 << min_t(u8, MLX5E_PARAMS_MAXIMUM_LOG_RQ_SIZE, + max_log_mpwrq_pkts); param->tx_max_pending = 1 << MLX5E_PARAMS_MAXIMUM_LOG_SQ_SIZE; param->rx_pending = 1 << priv->channels.params.log_rq_mtu_frames; param->tx_pending = 1 << priv->channels.params.log_sq_size; @@ -494,14 +503,14 @@ int mlx5e_ethtool_set_channels(struct mlx5e_priv *priv, arfs_enabled = opened && (priv->netdev->features & NETIF_F_NTUPLE); if (arfs_enabled) - mlx5e_arfs_disable(priv); + mlx5e_arfs_disable(priv->fs); /* Switch to new channels, set new parameters and close old ones */ err = mlx5e_safe_switch_params(priv, &new_params, mlx5e_num_channels_changed_ctx, NULL, true); if (arfs_enabled) { - int err2 = mlx5e_arfs_enable(priv); + int err2 = mlx5e_arfs_enable(priv->fs); if (err2) netdev_err(priv->netdev, "%s: mlx5e_arfs_enable failed: %d\n", @@ -1996,10 +2005,14 @@ static int set_pflag_rx_striding_rq(struct net_device *netdev, bool enable) struct mlx5e_params new_params; if (enable) { - if (!mlx5e_check_fragmented_striding_rq_cap(mdev)) - return -EOPNOTSUPP; - if (!mlx5e_striding_rq_possible(mdev, &priv->channels.params)) - return -EINVAL; + /* Checking the regular RQ here; mlx5e_validate_xsk_param called + * from mlx5e_open_xsk will check for each XSK queue, and + * mlx5e_safe_switch_params will be reverted if any check fails. + */ + int err = mlx5e_mpwrq_validate_regular(mdev, &priv->channels.params); + + if (err) + return err; } else if (priv->channels.params.packet_merge.type != MLX5E_PACKET_MERGE_NONE) { netdev_warn(netdev, "Can't set legacy RQ with HW-GRO/LRO, disable them first\n"); return -EINVAL; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_fs.c b/drivers/net/ethernet/mellanox/mlx5/core/en_fs.c index e0ce5a233d0b..1892ccb889b3 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_fs.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_fs.c @@ -36,10 +36,38 @@ #include <linux/tcp.h> #include <linux/mlx5/fs.h> #include <linux/mlx5/mpfs.h> -#include "en.h" #include "en_tc.h" #include "lib/mpfs.h" #include "en/ptp.h" +#include "en/fs_ethtool.h" + +struct mlx5e_flow_steering { + struct work_struct set_rx_mode_work; + bool state_destroy; + bool vlan_strip_disable; + struct mlx5_core_dev *mdev; + struct net_device *netdev; + struct mlx5_flow_namespace *ns; + struct mlx5_flow_namespace *egress_ns; +#ifdef CONFIG_MLX5_EN_RXNFC + struct mlx5e_ethtool_steering *ethtool; +#endif + struct mlx5e_tc_table *tc; + struct mlx5e_promisc_table promisc; + struct mlx5e_vlan_table *vlan; + struct mlx5e_l2_table l2; + struct mlx5_ttc_table *ttc; + struct mlx5_ttc_table *inner_ttc; +#ifdef CONFIG_MLX5_EN_ARFS + struct mlx5e_arfs_tables *arfs; +#endif +#ifdef CONFIG_MLX5_EN_TLS + struct mlx5e_accel_fs_tcp *accel_tcp; +#endif + struct mlx5e_fs_udp *udp; + struct mlx5e_fs_any *any; + struct mlx5e_ptp_fs *ptp_fs; +}; static int mlx5e_add_l2_flow_rule(struct mlx5e_flow_steering *fs, struct mlx5e_l2_rule *ai, int type); @@ -148,9 +176,8 @@ static int mlx5e_vport_context_update_vlans(struct mlx5e_flow_steering *fs) max_list_size = 1 << MLX5_CAP_GEN(fs->mdev, log_max_vlan_list); if (list_size > max_list_size) { - mlx5_core_warn(fs->mdev, - "netdev vlans list size (%d) > (%d) max vport list size, some vlans will be dropped\n", - list_size, max_list_size); + fs_warn(fs, "netdev vlans list size (%d) > (%d) max vport list size, some vlans will be dropped\n", + list_size, max_list_size); list_size = max_list_size; } @@ -167,8 +194,8 @@ static int mlx5e_vport_context_update_vlans(struct mlx5e_flow_steering *fs) err = mlx5_modify_nic_vport_vlans(fs->mdev, vlans, list_size); if (err) - mlx5_core_err(fs->mdev, "Failed to modify vport vlans list err(%d)\n", - err); + fs_err(fs, "Failed to modify vport vlans list err(%d)\n", + err); kvfree(vlans); return err; @@ -249,7 +276,7 @@ static int __mlx5e_add_vlan_rule(struct mlx5e_flow_steering *fs, if (IS_ERR(*rule_p)) { err = PTR_ERR(*rule_p); *rule_p = NULL; - mlx5_core_err(fs->mdev, "%s: add rule failed\n", __func__); + fs_err(fs, "%s: add rule failed\n", __func__); } return err; @@ -351,78 +378,78 @@ mlx5e_add_trap_rule(struct mlx5_flow_table *ft, int trap_id, int tir_num) return rule; } -int mlx5e_add_vlan_trap(struct mlx5e_priv *priv, int trap_id, int tir_num) +int mlx5e_add_vlan_trap(struct mlx5e_flow_steering *fs, int trap_id, int tir_num) { - struct mlx5_flow_table *ft = priv->fs->vlan->ft.t; + struct mlx5_flow_table *ft = fs->vlan->ft.t; struct mlx5_flow_handle *rule; int err; rule = mlx5e_add_trap_rule(ft, trap_id, tir_num); if (IS_ERR(rule)) { err = PTR_ERR(rule); - priv->fs->vlan->trap_rule = NULL; - mlx5_core_err(priv->fs->mdev, "%s: add VLAN trap rule failed, err %d\n", - __func__, err); + fs->vlan->trap_rule = NULL; + fs_err(fs, "%s: add VLAN trap rule failed, err %d\n", + __func__, err); return err; } - priv->fs->vlan->trap_rule = rule; + fs->vlan->trap_rule = rule; return 0; } -void mlx5e_remove_vlan_trap(struct mlx5e_priv *priv) +void mlx5e_remove_vlan_trap(struct mlx5e_flow_steering *fs) { - if (priv->fs->vlan->trap_rule) { - mlx5_del_flow_rules(priv->fs->vlan->trap_rule); - priv->fs->vlan->trap_rule = NULL; + if (fs->vlan->trap_rule) { + mlx5_del_flow_rules(fs->vlan->trap_rule); + fs->vlan->trap_rule = NULL; } } -int mlx5e_add_mac_trap(struct mlx5e_priv *priv, int trap_id, int tir_num) +int mlx5e_add_mac_trap(struct mlx5e_flow_steering *fs, int trap_id, int tir_num) { - struct mlx5_flow_table *ft = priv->fs->l2.ft.t; + struct mlx5_flow_table *ft = fs->l2.ft.t; struct mlx5_flow_handle *rule; int err; rule = mlx5e_add_trap_rule(ft, trap_id, tir_num); if (IS_ERR(rule)) { err = PTR_ERR(rule); - priv->fs->l2.trap_rule = NULL; - mlx5_core_err(priv->fs->mdev, "%s: add MAC trap rule failed, err %d\n", - __func__, err); + fs->l2.trap_rule = NULL; + fs_err(fs, "%s: add MAC trap rule failed, err %d\n", + __func__, err); return err; } - priv->fs->l2.trap_rule = rule; + fs->l2.trap_rule = rule; return 0; } -void mlx5e_remove_mac_trap(struct mlx5e_priv *priv) +void mlx5e_remove_mac_trap(struct mlx5e_flow_steering *fs) { - if (priv->fs->l2.trap_rule) { - mlx5_del_flow_rules(priv->fs->l2.trap_rule); - priv->fs->l2.trap_rule = NULL; + if (fs->l2.trap_rule) { + mlx5_del_flow_rules(fs->l2.trap_rule); + fs->l2.trap_rule = NULL; } } -void mlx5e_enable_cvlan_filter(struct mlx5e_priv *priv) +void mlx5e_enable_cvlan_filter(struct mlx5e_flow_steering *fs, bool promisc) { - if (!priv->fs->vlan->cvlan_filter_disabled) + if (!fs->vlan->cvlan_filter_disabled) return; - priv->fs->vlan->cvlan_filter_disabled = false; - if (priv->netdev->flags & IFF_PROMISC) + fs->vlan->cvlan_filter_disabled = false; + if (promisc) return; - mlx5e_fs_del_vlan_rule(priv->fs, MLX5E_VLAN_RULE_TYPE_ANY_CTAG_VID, 0); + mlx5e_fs_del_vlan_rule(fs, MLX5E_VLAN_RULE_TYPE_ANY_CTAG_VID, 0); } -void mlx5e_disable_cvlan_filter(struct mlx5e_priv *priv) +void mlx5e_disable_cvlan_filter(struct mlx5e_flow_steering *fs, bool promisc) { - if (priv->fs->vlan->cvlan_filter_disabled) + if (fs->vlan->cvlan_filter_disabled) return; - priv->fs->vlan->cvlan_filter_disabled = true; - if (priv->netdev->flags & IFF_PROMISC) + fs->vlan->cvlan_filter_disabled = true; + if (promisc) return; - mlx5e_add_vlan_rule(priv->fs, MLX5E_VLAN_RULE_TYPE_ANY_CTAG_VID, 0); + mlx5e_add_vlan_rule(fs, MLX5E_VLAN_RULE_TYPE_ANY_CTAG_VID, 0); } static int mlx5e_vlan_rx_add_cvid(struct mlx5e_flow_steering *fs, u16 vid) @@ -462,7 +489,7 @@ int mlx5e_fs_vlan_rx_add_vid(struct mlx5e_flow_steering *fs, { if (!fs->vlan) { - mlx5_core_err(fs->mdev, "Vlan doesn't exist\n"); + fs_err(fs, "Vlan doesn't exist\n"); return -EINVAL; } @@ -479,7 +506,7 @@ int mlx5e_fs_vlan_rx_kill_vid(struct mlx5e_flow_steering *fs, __be16 proto, u16 vid) { if (!fs->vlan) { - mlx5_core_err(fs->mdev, "Vlan doesn't exist\n"); + fs_err(fs, "Vlan doesn't exist\n"); return -EINVAL; } @@ -512,28 +539,28 @@ static void mlx5e_fs_add_vlan_rules(struct mlx5e_flow_steering *fs) mlx5e_fs_add_any_vid_rules(fs); } -static void mlx5e_del_vlan_rules(struct mlx5e_priv *priv) +static void mlx5e_del_vlan_rules(struct mlx5e_flow_steering *fs) { int i; - mlx5e_fs_del_vlan_rule(priv->fs, MLX5E_VLAN_RULE_TYPE_UNTAGGED, 0); + mlx5e_fs_del_vlan_rule(fs, MLX5E_VLAN_RULE_TYPE_UNTAGGED, 0); - for_each_set_bit(i, priv->fs->vlan->active_cvlans, VLAN_N_VID) { - mlx5e_fs_del_vlan_rule(priv->fs, MLX5E_VLAN_RULE_TYPE_MATCH_CTAG_VID, i); + for_each_set_bit(i, fs->vlan->active_cvlans, VLAN_N_VID) { + mlx5e_fs_del_vlan_rule(fs, MLX5E_VLAN_RULE_TYPE_MATCH_CTAG_VID, i); } - for_each_set_bit(i, priv->fs->vlan->active_svlans, VLAN_N_VID) - mlx5e_fs_del_vlan_rule(priv->fs, MLX5E_VLAN_RULE_TYPE_MATCH_STAG_VID, i); + for_each_set_bit(i, fs->vlan->active_svlans, VLAN_N_VID) + mlx5e_fs_del_vlan_rule(fs, MLX5E_VLAN_RULE_TYPE_MATCH_STAG_VID, i); - WARN_ON_ONCE(priv->fs->state_destroy); + WARN_ON_ONCE(fs->state_destroy); - mlx5e_remove_vlan_trap(priv); + mlx5e_remove_vlan_trap(fs); /* must be called after DESTROY bit is set and * set_rx_mode is called and flushed */ - if (priv->fs->vlan->cvlan_filter_disabled) - mlx5e_fs_del_any_vid_rules(priv->fs); + if (fs->vlan->cvlan_filter_disabled) + mlx5e_fs_del_any_vid_rules(fs); } #define mlx5e_for_each_hash_node(hn, tmp, hash, i) \ @@ -568,8 +595,9 @@ static void mlx5e_execute_l2_action(struct mlx5e_flow_steering *fs, } if (l2_err) - mlx5_core_warn(fs->mdev, "MPFS, failed to %s mac %pM, err(%d)\n", - action == MLX5E_ACTION_ADD ? "add" : "del", mac_addr, l2_err); + fs_warn(fs, "MPFS, failed to %s mac %pM, err(%d)\n", + action == MLX5E_ACTION_ADD ? "add" : "del", + mac_addr, l2_err); } static void mlx5e_sync_netdev_addr(struct mlx5e_flow_steering *fs, @@ -640,9 +668,8 @@ static void mlx5e_vport_context_update_addr_list(struct mlx5e_flow_steering *fs, size++; if (size > max_size) { - mlx5_core_warn(fs->mdev, - "mdev %s list size (%d) > (%d) max vport list size, some addresses will be dropped\n", - is_uc ? "UC" : "MC", size, max_size); + fs_warn(fs, "mdev %s list size (%d) > (%d) max vport list size, some addresses will be dropped\n", + is_uc ? "UC" : "MC", size, max_size); size = max_size; } @@ -658,9 +685,8 @@ static void mlx5e_vport_context_update_addr_list(struct mlx5e_flow_steering *fs, err = mlx5_modify_nic_vport_mac_list(fs->mdev, list_type, addr_array, size); out: if (err) - mlx5_core_err(fs->mdev, - "Failed to modify vport %s list err(%d)\n", - is_uc ? "UC" : "MC", err); + fs_err(fs, "Failed to modify vport %s list err(%d)\n", + is_uc ? "UC" : "MC", err); kfree(addr_array); } @@ -730,7 +756,7 @@ static int mlx5e_add_promisc_rule(struct mlx5e_flow_steering *fs) if (IS_ERR(*rule_p)) { err = PTR_ERR(*rule_p); *rule_p = NULL; - mlx5_core_err(fs->mdev, "%s: add promiscuous rule failed\n", __func__); + fs_err(fs, "%s: add promiscuous rule failed\n", __func__); } kvfree(spec); return err; @@ -750,7 +776,7 @@ static int mlx5e_create_promisc_table(struct mlx5e_flow_steering *fs) ft->t = mlx5_create_auto_grouped_flow_table(fs->ns, &ft_attr); if (IS_ERR(ft->t)) { err = PTR_ERR(ft->t); - mlx5_core_err(fs->mdev, "fail to create promisc table err=%d\n", err); + fs_err(fs, "fail to create promisc table err=%d\n", err); return err; } @@ -807,8 +833,8 @@ void mlx5e_fs_set_rx_mode_work(struct mlx5e_flow_steering *fs, if (err) enable_promisc = false; if (!fs->vlan_strip_disable && !err) - mlx5_core_warn_once(fs->mdev, - "S-tagged traffic will be dropped while C-tag vlan stripping is enabled\n"); + fs_warn_once(fs, + "S-tagged traffic will be dropped while C-tag vlan stripping is enabled\n"); } if (enable_allmulti) mlx5e_add_l2_flow_rule(fs, &ea->allmulti, MLX5E_ALLMULTI); @@ -856,14 +882,15 @@ void mlx5e_destroy_flow_table(struct mlx5e_flow_table *ft) ft->t = NULL; } -static void mlx5e_set_inner_ttc_params(struct mlx5e_priv *priv, +static void mlx5e_set_inner_ttc_params(struct mlx5e_flow_steering *fs, + struct mlx5e_rx_res *rx_res, struct ttc_params *ttc_params) { struct mlx5_flow_table_attr *ft_attr = &ttc_params->ft_attr; int tt; memset(ttc_params, 0, sizeof(*ttc_params)); - ttc_params->ns = mlx5_get_flow_namespace(priv->fs->mdev, + ttc_params->ns = mlx5_get_flow_namespace(fs->mdev, MLX5_FLOW_NAMESPACE_KERNEL); ft_attr->level = MLX5E_INNER_TTC_FT_LEVEL; ft_attr->prio = MLX5E_NIC_PRIO; @@ -872,13 +899,14 @@ static void mlx5e_set_inner_ttc_params(struct mlx5e_priv *priv, ttc_params->dests[tt].type = MLX5_FLOW_DESTINATION_TYPE_TIR; ttc_params->dests[tt].tir_num = tt == MLX5_TT_ANY ? - mlx5e_rx_res_get_tirn_direct(priv->rx_res, 0) : - mlx5e_rx_res_get_tirn_rss_inner(priv->rx_res, + mlx5e_rx_res_get_tirn_direct(rx_res, 0) : + mlx5e_rx_res_get_tirn_rss_inner(rx_res, tt); } } -void mlx5e_set_ttc_params(struct mlx5e_priv *priv, +void mlx5e_set_ttc_params(struct mlx5e_flow_steering *fs, + struct mlx5e_rx_res *rx_res, struct ttc_params *ttc_params, bool tunnel) { @@ -886,7 +914,7 @@ void mlx5e_set_ttc_params(struct mlx5e_priv *priv, int tt; memset(ttc_params, 0, sizeof(*ttc_params)); - ttc_params->ns = mlx5_get_flow_namespace(priv->fs->mdev, + ttc_params->ns = mlx5_get_flow_namespace(fs->mdev, MLX5_FLOW_NAMESPACE_KERNEL); ft_attr->level = MLX5E_TTC_FT_LEVEL; ft_attr->prio = MLX5E_NIC_PRIO; @@ -895,19 +923,19 @@ void mlx5e_set_ttc_params(struct mlx5e_priv *priv, ttc_params->dests[tt].type = MLX5_FLOW_DESTINATION_TYPE_TIR; ttc_params->dests[tt].tir_num = tt == MLX5_TT_ANY ? - mlx5e_rx_res_get_tirn_direct(priv->rx_res, 0) : - mlx5e_rx_res_get_tirn_rss(priv->rx_res, tt); + mlx5e_rx_res_get_tirn_direct(rx_res, 0) : + mlx5e_rx_res_get_tirn_rss(rx_res, tt); } ttc_params->inner_ttc = tunnel; - if (!tunnel || !mlx5_tunnel_inner_ft_supported(priv->fs->mdev)) + if (!tunnel || !mlx5_tunnel_inner_ft_supported(fs->mdev)) return; for (tt = 0; tt < MLX5_NUM_TUNNEL_TT; tt++) { ttc_params->tunnel_dests[tt].type = MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE; ttc_params->tunnel_dests[tt].ft = - mlx5_get_ttc_flow_table(priv->fs->inner_ttc); + mlx5_get_ttc_flow_table(fs->inner_ttc); } } @@ -959,8 +987,7 @@ static int mlx5e_add_l2_flow_rule(struct mlx5e_flow_steering *fs, ai->rule = mlx5_add_flow_rules(ft, spec, &flow_act, &dest, 1); if (IS_ERR(ai->rule)) { - mlx5_core_err(fs->mdev, "%s: add l2 rule(mac:%pM) failed\n", - __func__, mv_dmac); + fs_err(fs, "%s: add l2 rule(mac:%pM) failed\n", __func__, mv_dmac); err = PTR_ERR(ai->rule); ai->rule = NULL; } @@ -1044,14 +1071,14 @@ err_destroy_groups: return err; } -static void mlx5e_destroy_l2_table(struct mlx5e_priv *priv) +static void mlx5e_destroy_l2_table(struct mlx5e_flow_steering *fs) { - mlx5e_destroy_flow_table(&priv->fs->l2.ft); + mlx5e_destroy_flow_table(&fs->l2.ft); } -static int mlx5e_create_l2_table(struct mlx5e_priv *priv) +static int mlx5e_create_l2_table(struct mlx5e_flow_steering *fs) { - struct mlx5e_l2_table *l2_table = &priv->fs->l2; + struct mlx5e_l2_table *l2_table = &fs->l2; struct mlx5e_flow_table *ft = &l2_table->ft; struct mlx5_flow_table_attr ft_attr = {}; int err; @@ -1062,7 +1089,7 @@ static int mlx5e_create_l2_table(struct mlx5e_priv *priv) ft_attr.level = MLX5E_L2_FT_LEVEL; ft_attr.prio = MLX5E_NIC_PRIO; - ft->t = mlx5_create_flow_table(priv->fs->ns, &ft_attr); + ft->t = mlx5_create_flow_table(fs->ns, &ft_attr); if (IS_ERR(ft->t)) { err = PTR_ERR(ft->t); ft->t = NULL; @@ -1221,126 +1248,128 @@ err_destroy_vlan_table: return err; } -static void mlx5e_destroy_vlan_table(struct mlx5e_priv *priv) +static void mlx5e_destroy_vlan_table(struct mlx5e_flow_steering *fs) { - mlx5e_del_vlan_rules(priv); - mlx5e_destroy_flow_table(&priv->fs->vlan->ft); + mlx5e_del_vlan_rules(fs); + mlx5e_destroy_flow_table(&fs->vlan->ft); } -static void mlx5e_destroy_inner_ttc_table(struct mlx5e_priv *priv) +static void mlx5e_destroy_inner_ttc_table(struct mlx5e_flow_steering *fs) { - if (!mlx5_tunnel_inner_ft_supported(priv->fs->mdev)) + if (!mlx5_tunnel_inner_ft_supported(fs->mdev)) return; - mlx5_destroy_ttc_table(priv->fs->inner_ttc); + mlx5_destroy_ttc_table(fs->inner_ttc); } -void mlx5e_destroy_ttc_table(struct mlx5e_priv *priv) +void mlx5e_destroy_ttc_table(struct mlx5e_flow_steering *fs) { - mlx5_destroy_ttc_table(priv->fs->ttc); + mlx5_destroy_ttc_table(fs->ttc); } -static int mlx5e_create_inner_ttc_table(struct mlx5e_priv *priv) +static int mlx5e_create_inner_ttc_table(struct mlx5e_flow_steering *fs, + struct mlx5e_rx_res *rx_res) { struct ttc_params ttc_params = {}; - if (!mlx5_tunnel_inner_ft_supported(priv->fs->mdev)) + if (!mlx5_tunnel_inner_ft_supported(fs->mdev)) return 0; - mlx5e_set_inner_ttc_params(priv, &ttc_params); - priv->fs->inner_ttc = mlx5_create_inner_ttc_table(priv->fs->mdev, - &ttc_params); - if (IS_ERR(priv->fs->inner_ttc)) - return PTR_ERR(priv->fs->inner_ttc); + mlx5e_set_inner_ttc_params(fs, rx_res, &ttc_params); + fs->inner_ttc = mlx5_create_inner_ttc_table(fs->mdev, + &ttc_params); + if (IS_ERR(fs->inner_ttc)) + return PTR_ERR(fs->inner_ttc); return 0; } -int mlx5e_create_ttc_table(struct mlx5e_priv *priv) +int mlx5e_create_ttc_table(struct mlx5e_flow_steering *fs, + struct mlx5e_rx_res *rx_res) { struct ttc_params ttc_params = {}; - mlx5e_set_ttc_params(priv, &ttc_params, true); - priv->fs->ttc = mlx5_create_ttc_table(priv->fs->mdev, &ttc_params); - if (IS_ERR(priv->fs->ttc)) - return PTR_ERR(priv->fs->ttc); + mlx5e_set_ttc_params(fs, rx_res, &ttc_params, true); + fs->ttc = mlx5_create_ttc_table(fs->mdev, &ttc_params); + if (IS_ERR(fs->ttc)) + return PTR_ERR(fs->ttc); return 0; } -int mlx5e_create_flow_steering(struct mlx5e_priv *priv) +int mlx5e_create_flow_steering(struct mlx5e_flow_steering *fs, + struct mlx5e_rx_res *rx_res, + const struct mlx5e_profile *profile, + struct net_device *netdev) { + struct mlx5_flow_namespace *ns = mlx5_get_flow_namespace(fs->mdev, + MLX5_FLOW_NAMESPACE_KERNEL); int err; - priv->fs->ns = mlx5_get_flow_namespace(priv->fs->mdev, - MLX5_FLOW_NAMESPACE_KERNEL); - - if (!priv->fs->ns) + if (!ns) return -EOPNOTSUPP; - err = mlx5e_arfs_create_tables(priv); + mlx5e_fs_set_ns(fs, ns, false); + err = mlx5e_arfs_create_tables(fs, rx_res, + !!(netdev->hw_features & NETIF_F_NTUPLE)); if (err) { - mlx5_core_err(priv->fs->mdev, "Failed to create arfs tables, err=%d\n", - err); - priv->netdev->hw_features &= ~NETIF_F_NTUPLE; + fs_err(fs, "Failed to create arfs tables, err=%d\n", err); + netdev->hw_features &= ~NETIF_F_NTUPLE; } - err = mlx5e_create_inner_ttc_table(priv); + err = mlx5e_create_inner_ttc_table(fs, rx_res); if (err) { - mlx5_core_err(priv->fs->mdev, - "Failed to create inner ttc table, err=%d\n", err); + fs_err(fs, "Failed to create inner ttc table, err=%d\n", err); goto err_destroy_arfs_tables; } - err = mlx5e_create_ttc_table(priv); + err = mlx5e_create_ttc_table(fs, rx_res); if (err) { - mlx5_core_err(priv->fs->mdev, "Failed to create ttc table, err=%d\n", - err); + fs_err(fs, "Failed to create ttc table, err=%d\n", err); goto err_destroy_inner_ttc_table; } - err = mlx5e_create_l2_table(priv); + err = mlx5e_create_l2_table(fs); if (err) { - mlx5_core_err(priv->fs->mdev, "Failed to create l2 table, err=%d\n", - err); + fs_err(fs, "Failed to create l2 table, err=%d\n", err); goto err_destroy_ttc_table; } - err = mlx5e_fs_create_vlan_table(priv->fs); + err = mlx5e_fs_create_vlan_table(fs); if (err) { - mlx5_core_err(priv->fs->mdev, "Failed to create vlan table, err=%d\n", - err); + fs_err(fs, "Failed to create vlan table, err=%d\n", err); goto err_destroy_l2_table; } - err = mlx5e_ptp_alloc_rx_fs(priv); + err = mlx5e_ptp_alloc_rx_fs(fs, profile); if (err) goto err_destory_vlan_table; - mlx5e_ethtool_init_steering(priv); + mlx5e_ethtool_init_steering(fs); return 0; err_destory_vlan_table: - mlx5e_destroy_vlan_table(priv); + mlx5e_destroy_vlan_table(fs); err_destroy_l2_table: - mlx5e_destroy_l2_table(priv); + mlx5e_destroy_l2_table(fs); err_destroy_ttc_table: - mlx5e_destroy_ttc_table(priv); + mlx5e_destroy_ttc_table(fs); err_destroy_inner_ttc_table: - mlx5e_destroy_inner_ttc_table(priv); + mlx5e_destroy_inner_ttc_table(fs); err_destroy_arfs_tables: - mlx5e_arfs_destroy_tables(priv); + mlx5e_arfs_destroy_tables(fs, !!(netdev->hw_features & NETIF_F_NTUPLE)); return err; } -void mlx5e_destroy_flow_steering(struct mlx5e_priv *priv) +void mlx5e_destroy_flow_steering(struct mlx5e_flow_steering *fs, bool ntuple, + const struct mlx5e_profile *profile) { - mlx5e_ptp_free_rx_fs(priv); - mlx5e_destroy_vlan_table(priv); - mlx5e_destroy_l2_table(priv); - mlx5e_destroy_ttc_table(priv); - mlx5e_destroy_inner_ttc_table(priv); - mlx5e_arfs_destroy_tables(priv); - mlx5e_ethtool_cleanup_steering(priv); + mlx5e_ptp_free_rx_fs(fs, profile); + mlx5e_destroy_vlan_table(fs); + mlx5e_destroy_l2_table(fs); + mlx5e_destroy_ttc_table(fs); + mlx5e_destroy_inner_ttc_table(fs); + mlx5e_arfs_destroy_tables(fs, ntuple); + mlx5e_ethtool_cleanup_steering(fs); } static int mlx5e_fs_vlan_alloc(struct mlx5e_flow_steering *fs) @@ -1356,6 +1385,11 @@ static void mlx5e_fs_vlan_free(struct mlx5e_flow_steering *fs) kvfree(fs->vlan); } +struct mlx5e_vlan_table *mlx5e_fs_get_vlan(struct mlx5e_flow_steering *fs) +{ + return fs->vlan; +} + static int mlx5e_fs_tc_alloc(struct mlx5e_flow_steering *fs) { fs->tc = mlx5e_tc_table_alloc(); @@ -1369,6 +1403,32 @@ static void mlx5e_fs_tc_free(struct mlx5e_flow_steering *fs) mlx5e_tc_table_free(fs->tc); } +struct mlx5e_tc_table *mlx5e_fs_get_tc(struct mlx5e_flow_steering *fs) +{ + return fs->tc; +} + +#ifdef CONFIG_MLX5_EN_RXNFC +static int mlx5e_fs_ethtool_alloc(struct mlx5e_flow_steering *fs) +{ + return mlx5e_ethtool_alloc(&fs->ethtool); +} + +static void mlx5e_fs_ethtool_free(struct mlx5e_flow_steering *fs) +{ + mlx5e_ethtool_free(fs->ethtool); +} + +struct mlx5e_ethtool_steering *mlx5e_fs_get_ethtool(struct mlx5e_flow_steering *fs) +{ + return fs->ethtool; +} +#else +static int mlx5e_fs_ethtool_alloc(struct mlx5e_flow_steering *fs) +{ return 0; } +static void mlx5e_fs_ethtool_free(struct mlx5e_flow_steering *fs) { } +#endif + struct mlx5e_flow_steering *mlx5e_fs_init(const struct mlx5e_profile *profile, struct mlx5_core_dev *mdev, bool state_destroy) @@ -1394,8 +1454,13 @@ struct mlx5e_flow_steering *mlx5e_fs_init(const struct mlx5e_profile *profile, goto err_free_vlan; } - return fs; + err = mlx5e_fs_ethtool_alloc(fs); + if (err) + goto err_free_tc; + return fs; +err_free_tc: + mlx5e_fs_tc_free(fs); err_free_vlan: mlx5e_fs_vlan_free(fs); err_free_fs: @@ -1406,7 +1471,109 @@ err: void mlx5e_fs_cleanup(struct mlx5e_flow_steering *fs) { + mlx5e_fs_ethtool_free(fs); mlx5e_fs_tc_free(fs); mlx5e_fs_vlan_free(fs); kvfree(fs); } + +struct mlx5e_l2_table *mlx5e_fs_get_l2(struct mlx5e_flow_steering *fs) +{ + return &fs->l2; +} + +struct mlx5_flow_namespace *mlx5e_fs_get_ns(struct mlx5e_flow_steering *fs, bool egress) +{ + return egress ? fs->egress_ns : fs->ns; +} + +void mlx5e_fs_set_ns(struct mlx5e_flow_steering *fs, struct mlx5_flow_namespace *ns, bool egress) +{ + if (!egress) + fs->ns = ns; + else + fs->egress_ns = ns; +} + +struct mlx5_ttc_table *mlx5e_fs_get_ttc(struct mlx5e_flow_steering *fs, bool inner) +{ + return inner ? fs->inner_ttc : fs->ttc; +} + +void mlx5e_fs_set_ttc(struct mlx5e_flow_steering *fs, struct mlx5_ttc_table *ttc, bool inner) +{ + if (!inner) + fs->ttc = ttc; + else + fs->inner_ttc = ttc; +} + +#ifdef CONFIG_MLX5_EN_ARFS +struct mlx5e_arfs_tables *mlx5e_fs_get_arfs(struct mlx5e_flow_steering *fs) +{ + return fs->arfs; +} + +void mlx5e_fs_set_arfs(struct mlx5e_flow_steering *fs, struct mlx5e_arfs_tables *arfs) +{ + fs->arfs = arfs; +} +#endif + +struct mlx5e_ptp_fs *mlx5e_fs_get_ptp(struct mlx5e_flow_steering *fs) +{ + return fs->ptp_fs; +} + +void mlx5e_fs_set_ptp(struct mlx5e_flow_steering *fs, struct mlx5e_ptp_fs *ptp_fs) +{ + fs->ptp_fs = ptp_fs; +} + +struct mlx5e_fs_any *mlx5e_fs_get_any(struct mlx5e_flow_steering *fs) +{ + return fs->any; +} + +void mlx5e_fs_set_any(struct mlx5e_flow_steering *fs, struct mlx5e_fs_any *any) +{ + fs->any = any; +} + +#ifdef CONFIG_MLX5_EN_TLS +struct mlx5e_accel_fs_tcp *mlx5e_fs_get_accel_tcp(struct mlx5e_flow_steering *fs) +{ + return fs->accel_tcp; +} + +void mlx5e_fs_set_accel_tcp(struct mlx5e_flow_steering *fs, struct mlx5e_accel_fs_tcp *accel_tcp) +{ + fs->accel_tcp = accel_tcp; +} +#endif + +void mlx5e_fs_set_state_destroy(struct mlx5e_flow_steering *fs, bool state_destroy) +{ + fs->state_destroy = state_destroy; +} + +void mlx5e_fs_set_vlan_strip_disable(struct mlx5e_flow_steering *fs, + bool vlan_strip_disable) +{ + fs->vlan_strip_disable = vlan_strip_disable; +} + +struct mlx5e_fs_udp *mlx5e_fs_get_udp(struct mlx5e_flow_steering *fs) +{ + return fs->udp; +} + +void mlx5e_fs_set_udp(struct mlx5e_flow_steering *fs, struct mlx5e_fs_udp *udp) +{ + fs->udp = udp; +} + +struct mlx5_core_dev *mlx5e_fs_get_mdev(struct mlx5e_flow_steering *fs) +{ + return fs->mdev; +} diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_fs_ethtool.c b/drivers/net/ethernet/mellanox/mlx5/core/en_fs_ethtool.c index 3e4bc7836ef4..aac32e505c14 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_fs_ethtool.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_fs_ethtool.c @@ -34,6 +34,22 @@ #include "en.h" #include "en/params.h" #include "en/xsk/pool.h" +#include "en/fs_ethtool.h" + +struct mlx5e_ethtool_table { + struct mlx5_flow_table *ft; + int num_rules; +}; + +#define ETHTOOL_NUM_L3_L4_FTS 7 +#define ETHTOOL_NUM_L2_FTS 4 + +struct mlx5e_ethtool_steering { + struct mlx5e_ethtool_table l3_l4_ft[ETHTOOL_NUM_L3_L4_FTS]; + struct mlx5e_ethtool_table l2_ft[ETHTOOL_NUM_L2_FTS]; + struct list_head rules; + int tot_num_rules; +}; static int flow_type_to_traffic_type(u32 flow_type); @@ -66,6 +82,7 @@ static struct mlx5e_ethtool_table *get_flow_table(struct mlx5e_priv *priv, struct ethtool_rx_flow_spec *fs, int num_tuples) { + struct mlx5e_ethtool_steering *ethtool = mlx5e_fs_get_ethtool(priv->fs); struct mlx5_flow_table_attr ft_attr = {}; struct mlx5e_ethtool_table *eth_ft; struct mlx5_flow_namespace *ns; @@ -81,18 +98,18 @@ static struct mlx5e_ethtool_table *get_flow_table(struct mlx5e_priv *priv, case UDP_V6_FLOW: max_tuples = ETHTOOL_NUM_L3_L4_FTS; prio = MLX5E_ETHTOOL_L3_L4_PRIO + (max_tuples - num_tuples); - eth_ft = &priv->fs->ethtool.l3_l4_ft[prio]; + eth_ft = ðtool->l3_l4_ft[prio]; break; case IP_USER_FLOW: case IPV6_USER_FLOW: max_tuples = ETHTOOL_NUM_L3_L4_FTS; prio = MLX5E_ETHTOOL_L3_L4_PRIO + (max_tuples - num_tuples); - eth_ft = &priv->fs->ethtool.l3_l4_ft[prio]; + eth_ft = ðtool->l3_l4_ft[prio]; break; case ETHER_FLOW: max_tuples = ETHTOOL_NUM_L2_FTS; prio = max_tuples - num_tuples; - eth_ft = &priv->fs->ethtool.l2_ft[prio]; + eth_ft = ðtool->l2_ft[prio]; prio += MLX5E_ETHTOOL_L2_PRIO; break; default: @@ -382,15 +399,16 @@ static int set_flow_attrs(u32 *match_c, u32 *match_v, static void add_rule_to_list(struct mlx5e_priv *priv, struct mlx5e_ethtool_rule *rule) { + struct mlx5e_ethtool_steering *ethtool = mlx5e_fs_get_ethtool(priv->fs); + struct list_head *head = ðtool->rules; struct mlx5e_ethtool_rule *iter; - struct list_head *head = &priv->fs->ethtool.rules; - list_for_each_entry(iter, &priv->fs->ethtool.rules, list) { + list_for_each_entry(iter, ðtool->rules, list) { if (iter->flow_spec.location > rule->flow_spec.location) break; head = &iter->list; } - priv->fs->ethtool.tot_num_rules++; + ethtool->tot_num_rules++; list_add(&rule->list, head); } @@ -433,15 +451,7 @@ static int flow_get_tirn(struct mlx5e_priv *priv, eth_rule->rss = rss; mlx5e_rss_refcnt_inc(eth_rule->rss); } else { - struct mlx5e_params *params = &priv->channels.params; - enum mlx5e_rq_group group; - u16 ix; - - mlx5e_qid_get_ch_and_group(params, fs->ring_cookie, &ix, &group); - - *tirn = group == MLX5E_RQ_GROUP_XSK ? - mlx5e_rx_res_get_tirn_xsk(priv->rx_res, ix) : - mlx5e_rx_res_get_tirn_direct(priv->rx_res, ix); + *tirn = mlx5e_rx_res_get_tirn_direct(priv->rx_res, fs->ring_cookie); } return 0; @@ -499,15 +509,16 @@ free: return err ? ERR_PTR(err) : rule; } -static void del_ethtool_rule(struct mlx5e_priv *priv, +static void del_ethtool_rule(struct mlx5e_flow_steering *fs, struct mlx5e_ethtool_rule *eth_rule) { + struct mlx5e_ethtool_steering *ethtool = mlx5e_fs_get_ethtool(fs); if (eth_rule->rule) mlx5_del_flow_rules(eth_rule->rule); if (eth_rule->rss) mlx5e_rss_refcnt_dec(eth_rule->rss); list_del(ð_rule->list); - priv->fs->ethtool.tot_num_rules--; + ethtool->tot_num_rules--; put_flow_table(eth_rule->eth_ft); kfree(eth_rule); } @@ -515,9 +526,10 @@ static void del_ethtool_rule(struct mlx5e_priv *priv, static struct mlx5e_ethtool_rule *find_ethtool_rule(struct mlx5e_priv *priv, int location) { + struct mlx5e_ethtool_steering *ethtool = mlx5e_fs_get_ethtool(priv->fs); struct mlx5e_ethtool_rule *iter; - list_for_each_entry(iter, &priv->fs->ethtool.rules, list) { + list_for_each_entry(iter, ðtool->rules, list) { if (iter->flow_spec.location == location) return iter; } @@ -531,7 +543,7 @@ static struct mlx5e_ethtool_rule *get_ethtool_rule(struct mlx5e_priv *priv, eth_rule = find_ethtool_rule(priv, location); if (eth_rule) - del_ethtool_rule(priv, eth_rule); + del_ethtool_rule(priv->fs, eth_rule); eth_rule = kzalloc(sizeof(*eth_rule), GFP_KERNEL); if (!eth_rule) @@ -662,8 +674,7 @@ static int validate_flow(struct mlx5e_priv *priv, return -ENOSPC; if (fs->ring_cookie != RX_CLS_FLOW_DISC) - if (!mlx5e_qid_validate(priv->profile, &priv->channels.params, - fs->ring_cookie)) + if (fs->ring_cookie >= priv->channels.params.num_channels) return -EINVAL; switch (flow_type_mask(fs->flow_type)) { @@ -754,7 +765,7 @@ mlx5e_ethtool_flow_replace(struct mlx5e_priv *priv, return 0; del_ethtool_rule: - del_ethtool_rule(priv, eth_rule); + del_ethtool_rule(priv->fs, eth_rule); return err; } @@ -774,7 +785,7 @@ mlx5e_ethtool_flow_remove(struct mlx5e_priv *priv, int location) goto out; } - del_ethtool_rule(priv, eth_rule); + del_ethtool_rule(priv->fs, eth_rule); out: return err; } @@ -783,12 +794,13 @@ static int mlx5e_ethtool_get_flow(struct mlx5e_priv *priv, struct ethtool_rxnfc *info, int location) { + struct mlx5e_ethtool_steering *ethtool = mlx5e_fs_get_ethtool(priv->fs); struct mlx5e_ethtool_rule *eth_rule; if (location < 0 || location >= MAX_NUM_OF_ETHTOOL_RULES) return -EINVAL; - list_for_each_entry(eth_rule, &priv->fs->ethtool.rules, list) { + list_for_each_entry(eth_rule, ðtool->rules, list) { int index; if (eth_rule->flow_spec.location != location) @@ -826,18 +838,34 @@ mlx5e_ethtool_get_all_flows(struct mlx5e_priv *priv, return err; } -void mlx5e_ethtool_cleanup_steering(struct mlx5e_priv *priv) +int mlx5e_ethtool_alloc(struct mlx5e_ethtool_steering **ethtool) { + *ethtool = kvzalloc(sizeof(**ethtool), GFP_KERNEL); + if (!*ethtool) + return -ENOMEM; + return 0; +} + +void mlx5e_ethtool_free(struct mlx5e_ethtool_steering *ethtool) +{ + kvfree(ethtool); +} + +void mlx5e_ethtool_cleanup_steering(struct mlx5e_flow_steering *fs) +{ + struct mlx5e_ethtool_steering *ethtool = mlx5e_fs_get_ethtool(fs); struct mlx5e_ethtool_rule *iter; struct mlx5e_ethtool_rule *temp; - list_for_each_entry_safe(iter, temp, &priv->fs->ethtool.rules, list) - del_ethtool_rule(priv, iter); + list_for_each_entry_safe(iter, temp, ðtool->rules, list) + del_ethtool_rule(fs, iter); } -void mlx5e_ethtool_init_steering(struct mlx5e_priv *priv) +void mlx5e_ethtool_init_steering(struct mlx5e_flow_steering *fs) { - INIT_LIST_HEAD(&priv->fs->ethtool.rules); + struct mlx5e_ethtool_steering *ethtool = mlx5e_fs_get_ethtool(fs); + + INIT_LIST_HEAD(ðtool->rules); } static int flow_type_to_traffic_type(u32 flow_type) @@ -959,11 +987,12 @@ int mlx5e_ethtool_set_rxnfc(struct mlx5e_priv *priv, struct ethtool_rxnfc *cmd) int mlx5e_ethtool_get_rxnfc(struct mlx5e_priv *priv, struct ethtool_rxnfc *info, u32 *rule_locs) { + struct mlx5e_ethtool_steering *ethtool = mlx5e_fs_get_ethtool(priv->fs); int err = 0; switch (info->cmd) { case ETHTOOL_GRXCLSRLCNT: - info->rule_cnt = priv->fs->ethtool.tot_num_rules; + info->rule_cnt = ethtool->tot_num_rules; break; case ETHTOOL_GRXCLSRULE: err = mlx5e_ethtool_get_flow(priv, info, info->fs.location); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index 02eb2f0fa2ae..364f04309149 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -45,6 +45,7 @@ #include "en_tc.h" #include "en_rep.h" #include "en_accel/ipsec.h" +#include "en_accel/macsec.h" #include "en_accel/en_accel.h" #include "en_accel/ktls.h" #include "lib/vxlan.h" @@ -67,22 +68,25 @@ #include "qos.h" #include "en/trap.h" -bool mlx5e_check_fragmented_striding_rq_cap(struct mlx5_core_dev *mdev) +bool mlx5e_check_fragmented_striding_rq_cap(struct mlx5_core_dev *mdev, u8 page_shift, + enum mlx5e_mpwrq_umr_mode umr_mode) { - bool striding_rq_umr, inline_umr; - u16 max_wqe_sz_cap; + u16 umr_wqebbs, max_wqebbs; + bool striding_rq_umr; striding_rq_umr = MLX5_CAP_GEN(mdev, striding_rq) && MLX5_CAP_GEN(mdev, umr_ptr_rlky) && MLX5_CAP_ETH(mdev, reg_umr_sq); - max_wqe_sz_cap = mlx5e_get_max_sq_wqebbs(mdev) * MLX5_SEND_WQE_BB; - inline_umr = max_wqe_sz_cap >= MLX5E_UMR_WQE_INLINE_SZ; if (!striding_rq_umr) return false; - if (!inline_umr) { - mlx5_core_warn(mdev, "Cannot support Striding RQ: UMR WQE size (%d) exceeds maximum supported (%d).\n", - (int)MLX5E_UMR_WQE_INLINE_SZ, max_wqe_sz_cap); + + umr_wqebbs = mlx5e_mpwrq_umr_wqebbs(mdev, page_shift, umr_mode); + max_wqebbs = mlx5e_get_max_sq_aligned_wqebbs(mdev); + /* Sanity check; should never happen, because mlx5e_mpwrq_umr_wqebbs is + * calculated from mlx5e_get_max_sq_aligned_wqebbs. + */ + if (WARN_ON(umr_wqebbs > max_wqebbs)) return false; - } + return true; } @@ -199,21 +203,35 @@ static void mlx5e_disable_blocking_events(struct mlx5e_priv *priv) mlx5_blocking_notifier_unregister(priv->mdev, &priv->blocking_events_nb); } +static u16 mlx5e_mpwrq_umr_octowords(u32 entries, enum mlx5e_mpwrq_umr_mode umr_mode) +{ + u8 umr_entry_size = mlx5e_mpwrq_umr_entry_size(umr_mode); + + WARN_ON_ONCE(entries * umr_entry_size % MLX5_OCTWORD); + + return entries * umr_entry_size / MLX5_OCTWORD; +} + static inline void mlx5e_build_umr_wqe(struct mlx5e_rq *rq, struct mlx5e_icosq *sq, struct mlx5e_umr_wqe *wqe) { struct mlx5_wqe_ctrl_seg *cseg = &wqe->ctrl; struct mlx5_wqe_umr_ctrl_seg *ucseg = &wqe->uctrl; - u8 ds_cnt = DIV_ROUND_UP(MLX5E_UMR_WQE_INLINE_SZ, MLX5_SEND_WQE_DS); + u16 octowords; + u8 ds_cnt; + + ds_cnt = DIV_ROUND_UP(mlx5e_mpwrq_umr_wqe_sz(rq->mdev, rq->mpwqe.page_shift, + rq->mpwqe.umr_mode), + MLX5_SEND_WQE_DS); cseg->qpn_ds = cpu_to_be32((sq->sqn << MLX5_WQE_CTRL_QPN_SHIFT) | ds_cnt); - cseg->umr_mkey = rq->mkey_be; + cseg->umr_mkey = rq->mpwqe.umr_mkey_be; ucseg->flags = MLX5_UMR_TRANSLATION_OFFSET_EN | MLX5_UMR_INLINE; - ucseg->xlt_octowords = - cpu_to_be16(MLX5_MTT_OCTW(MLX5_MPWRQ_PAGES_PER_WQE)); + octowords = mlx5e_mpwrq_umr_octowords(rq->mpwqe.pages_per_wqe, rq->mpwqe.umr_mode); + ucseg->xlt_octowords = cpu_to_be16(octowords); ucseg->mkey_mask = cpu_to_be64(MLX5_MKEY_MASK_FREE); } @@ -259,10 +277,12 @@ static void mlx5e_rq_shampo_hd_info_free(struct mlx5e_rq *rq) static int mlx5e_rq_alloc_mpwqe_info(struct mlx5e_rq *rq, int node) { int wq_sz = mlx5_wq_ll_get_size(&rq->mpwqe.wq); + size_t alloc_size; + + alloc_size = array_size(wq_sz, struct_size(rq->mpwqe.info, alloc_units, + rq->mpwqe.pages_per_wqe)); - rq->mpwqe.info = kvzalloc_node(array_size(wq_sz, - sizeof(*rq->mpwqe.info)), - GFP_KERNEL, node); + rq->mpwqe.info = kvzalloc_node(alloc_size, GFP_KERNEL, node); if (!rq->mpwqe.info) return -ENOMEM; @@ -271,18 +291,52 @@ static int mlx5e_rq_alloc_mpwqe_info(struct mlx5e_rq *rq, int node) return 0; } -static int mlx5e_create_umr_mtt_mkey(struct mlx5_core_dev *mdev, - u64 npages, u8 page_shift, u32 *umr_mkey, - dma_addr_t filler_addr) + +static u8 mlx5e_mpwrq_access_mode(enum mlx5e_mpwrq_umr_mode umr_mode) +{ + switch (umr_mode) { + case MLX5E_MPWRQ_UMR_MODE_ALIGNED: + return MLX5_MKC_ACCESS_MODE_MTT; + case MLX5E_MPWRQ_UMR_MODE_UNALIGNED: + return MLX5_MKC_ACCESS_MODE_KSM; + case MLX5E_MPWRQ_UMR_MODE_OVERSIZED: + return MLX5_MKC_ACCESS_MODE_KLMS; + case MLX5E_MPWRQ_UMR_MODE_TRIPLE: + return MLX5_MKC_ACCESS_MODE_KSM; + } + WARN_ONCE(1, "MPWRQ UMR mode %d is not known\n", umr_mode); + return 0; +} + +static int mlx5e_create_umr_mkey(struct mlx5_core_dev *mdev, + u32 npages, u8 page_shift, u32 *umr_mkey, + dma_addr_t filler_addr, + enum mlx5e_mpwrq_umr_mode umr_mode, + u32 xsk_chunk_size) { struct mlx5_mtt *mtt; + struct mlx5_ksm *ksm; + struct mlx5_klm *klm; + u32 octwords; int inlen; void *mkc; u32 *in; int err; int i; - inlen = MLX5_ST_SZ_BYTES(create_mkey_in) + sizeof(*mtt) * npages; + if ((umr_mode == MLX5E_MPWRQ_UMR_MODE_UNALIGNED || + umr_mode == MLX5E_MPWRQ_UMR_MODE_TRIPLE) && + !MLX5_CAP_GEN(mdev, fixed_buffer_size)) { + mlx5_core_warn(mdev, "Unaligned AF_XDP requires fixed_buffer_size capability\n"); + return -EINVAL; + } + + octwords = mlx5e_mpwrq_umr_octowords(npages, umr_mode); + + inlen = MLX5_FLEXIBLE_INLEN(mdev, MLX5_ST_SZ_BYTES(create_mkey_in), + MLX5_OCTWORD, octwords); + if (inlen < 0) + return inlen; in = kvzalloc(inlen, GFP_KERNEL); if (!in) @@ -294,16 +348,17 @@ static int mlx5e_create_umr_mtt_mkey(struct mlx5_core_dev *mdev, MLX5_SET(mkc, mkc, umr_en, 1); MLX5_SET(mkc, mkc, lw, 1); MLX5_SET(mkc, mkc, lr, 1); - MLX5_SET(mkc, mkc, access_mode_1_0, MLX5_MKC_ACCESS_MODE_MTT); + MLX5_SET(mkc, mkc, access_mode_1_0, mlx5e_mpwrq_access_mode(umr_mode)); mlx5e_mkey_set_relaxed_ordering(mdev, mkc); MLX5_SET(mkc, mkc, qpn, 0xffffff); MLX5_SET(mkc, mkc, pd, mdev->mlx5e_res.hw_objs.pdn); MLX5_SET64(mkc, mkc, len, npages << page_shift); - MLX5_SET(mkc, mkc, translations_octword_size, - MLX5_MTT_OCTW(npages)); - MLX5_SET(mkc, mkc, log_page_size, page_shift); - MLX5_SET(create_mkey_in, in, translations_octword_actual_size, - MLX5_MTT_OCTW(npages)); + MLX5_SET(mkc, mkc, translations_octword_size, octwords); + if (umr_mode == MLX5E_MPWRQ_UMR_MODE_TRIPLE) + MLX5_SET(mkc, mkc, log_page_size, page_shift - 2); + else if (umr_mode != MLX5E_MPWRQ_UMR_MODE_OVERSIZED) + MLX5_SET(mkc, mkc, log_page_size, page_shift); + MLX5_SET(create_mkey_in, in, translations_octword_actual_size, octwords); /* Initialize the mkey with all MTTs pointing to a default * page (filler_addr). When the channels are activated, UMR @@ -311,9 +366,47 @@ static int mlx5e_create_umr_mtt_mkey(struct mlx5_core_dev *mdev, * the RQ's pool, while the gaps (wqe_overflow) remain mapped * to the default page. */ - mtt = MLX5_ADDR_OF(create_mkey_in, in, klm_pas_mtt); - for (i = 0 ; i < npages ; i++) - mtt[i].ptag = cpu_to_be64(filler_addr); + switch (umr_mode) { + case MLX5E_MPWRQ_UMR_MODE_OVERSIZED: + klm = MLX5_ADDR_OF(create_mkey_in, in, klm_pas_mtt); + for (i = 0; i < npages; i++) { + klm[i << 1] = (struct mlx5_klm) { + .va = cpu_to_be64(filler_addr), + .bcount = cpu_to_be32(xsk_chunk_size), + .key = cpu_to_be32(mdev->mlx5e_res.hw_objs.mkey), + }; + klm[(i << 1) + 1] = (struct mlx5_klm) { + .va = cpu_to_be64(filler_addr), + .bcount = cpu_to_be32((1 << page_shift) - xsk_chunk_size), + .key = cpu_to_be32(mdev->mlx5e_res.hw_objs.mkey), + }; + } + break; + case MLX5E_MPWRQ_UMR_MODE_UNALIGNED: + ksm = MLX5_ADDR_OF(create_mkey_in, in, klm_pas_mtt); + for (i = 0; i < npages; i++) + ksm[i] = (struct mlx5_ksm) { + .key = cpu_to_be32(mdev->mlx5e_res.hw_objs.mkey), + .va = cpu_to_be64(filler_addr), + }; + break; + case MLX5E_MPWRQ_UMR_MODE_ALIGNED: + mtt = MLX5_ADDR_OF(create_mkey_in, in, klm_pas_mtt); + for (i = 0; i < npages; i++) + mtt[i] = (struct mlx5_mtt) { + .ptag = cpu_to_be64(filler_addr), + }; + break; + case MLX5E_MPWRQ_UMR_MODE_TRIPLE: + ksm = MLX5_ADDR_OF(create_mkey_in, in, klm_pas_mtt); + for (i = 0; i < npages * 4; i++) { + ksm[i] = (struct mlx5_ksm) { + .key = cpu_to_be32(mdev->mlx5e_res.hw_objs.mkey), + .va = cpu_to_be64(filler_addr), + }; + } + break; + } err = mlx5_core_create_mkey(mdev, umr_mkey, in, inlen); @@ -356,10 +449,27 @@ static int mlx5e_create_umr_klm_mkey(struct mlx5_core_dev *mdev, static int mlx5e_create_rq_umr_mkey(struct mlx5_core_dev *mdev, struct mlx5e_rq *rq) { - u64 num_mtts = MLX5E_REQUIRED_MTTS(mlx5_wq_ll_get_size(&rq->mpwqe.wq)); + u32 xsk_chunk_size = rq->xsk_pool ? rq->xsk_pool->chunk_size : 0; + u32 wq_size = mlx5_wq_ll_get_size(&rq->mpwqe.wq); + u32 num_entries, max_num_entries; + u32 umr_mkey; + int err; + + max_num_entries = mlx5e_mpwrq_max_num_entries(mdev, rq->mpwqe.umr_mode); + + /* Shouldn't overflow, the result is at most MLX5E_MAX_RQ_NUM_MTTS. */ + if (WARN_ON_ONCE(check_mul_overflow(wq_size, (u32)rq->mpwqe.mtts_per_wqe, + &num_entries) || + num_entries > max_num_entries)) + mlx5_core_err(mdev, "%s: multiplication overflow: %u * %u > %u\n", + __func__, wq_size, rq->mpwqe.mtts_per_wqe, + max_num_entries); - return mlx5e_create_umr_mtt_mkey(mdev, num_mtts, PAGE_SHIFT, - &rq->umr_mkey, rq->wqe_overflow.addr); + err = mlx5e_create_umr_mkey(mdev, num_entries, rq->mpwqe.page_shift, + &umr_mkey, rq->wqe_overflow.addr, + rq->mpwqe.umr_mode, xsk_chunk_size); + rq->mpwqe.umr_mkey_be = cpu_to_be32(umr_mkey); + return err; } static int mlx5e_create_rq_hd_umr_mkey(struct mlx5_core_dev *mdev, @@ -376,18 +486,20 @@ static int mlx5e_create_rq_hd_umr_mkey(struct mlx5_core_dev *mdev, &rq->mpwqe.shampo->mkey); } -static u64 mlx5e_get_mpwqe_offset(u16 wqe_ix) -{ - return MLX5E_REQUIRED_MTTS(wqe_ix) << PAGE_SHIFT; -} - static void mlx5e_init_frags_partition(struct mlx5e_rq *rq) { struct mlx5e_wqe_frag_info next_frag = {}; struct mlx5e_wqe_frag_info *prev = NULL; int i; - next_frag.di = &rq->wqe.di[0]; + if (rq->xsk_pool) { + /* Assumptions used by XSK batched allocator. */ + WARN_ON(rq->wqe.info.num_frags != 1); + WARN_ON(rq->wqe.info.log_num_frags != 0); + WARN_ON(rq->wqe.info.arr[0].frag_stride != PAGE_SIZE); + } + + next_frag.au = &rq->wqe.alloc_units[0]; for (i = 0; i < mlx5_wq_cyc_get_size(&rq->wqe.wq); i++) { struct mlx5e_rq_frag_info *frag_info = &rq->wqe.info.arr[0]; @@ -397,7 +509,7 @@ static void mlx5e_init_frags_partition(struct mlx5e_rq *rq) for (f = 0; f < rq->wqe.info.num_frags; f++, frag++) { if (next_frag.offset + frag_info[f].frag_stride > PAGE_SIZE) { - next_frag.di++; + next_frag.au++; next_frag.offset = 0; if (prev) prev->last_in_page = true; @@ -414,12 +526,13 @@ static void mlx5e_init_frags_partition(struct mlx5e_rq *rq) prev->last_in_page = true; } -int mlx5e_init_di_list(struct mlx5e_rq *rq, int wq_sz, int node) +static int mlx5e_init_au_list(struct mlx5e_rq *rq, int wq_sz, int node) { int len = wq_sz << rq->wqe.info.log_num_frags; - rq->wqe.di = kvzalloc_node(array_size(len, sizeof(*rq->wqe.di)), GFP_KERNEL, node); - if (!rq->wqe.di) + rq->wqe.alloc_units = kvzalloc_node(array_size(len, sizeof(*rq->wqe.alloc_units)), + GFP_KERNEL, node); + if (!rq->wqe.alloc_units) return -ENOMEM; mlx5e_init_frags_partition(rq); @@ -427,9 +540,9 @@ int mlx5e_init_di_list(struct mlx5e_rq *rq, int wq_sz, int node) return 0; } -void mlx5e_free_di_list(struct mlx5e_rq *rq) +static void mlx5e_free_au_list(struct mlx5e_rq *rq) { - kvfree(rq->wqe.di); + kvfree(rq->wqe.alloc_units); } static void mlx5e_rq_err_cqe_work(struct work_struct *recover_work) @@ -485,7 +598,7 @@ static int mlx5e_init_rxq_rq(struct mlx5e_channel *c, struct mlx5e_params *param if (err) return err; - return xdp_rxq_info_reg(&rq->xdp_rxq, rq->netdev, rq->ix, 0); + return xdp_rxq_info_reg(&rq->xdp_rxq, rq->netdev, rq->ix, c->napi.napi_id); } static int mlx5_rq_shampo_alloc(struct mlx5_core_dev *mdev, @@ -572,6 +685,8 @@ static int mlx5e_alloc_rq(struct mlx5e_params *params, rq->buff.headroom = mlx5e_get_rq_headroom(mdev, params, xsk); pool_size = 1 << params->log_rq_mtu_frames; + rq->mkey_be = cpu_to_be32(mdev->mlx5e_res.hw_objs.mkey); + switch (rq->wq_type) { case MLX5_WQ_TYPE_LINKED_LIST_STRIDING_RQ: err = mlx5_wq_ll_create(mdev, &rqp->wq, rqc_wq, &rq->mpwqe.wq, @@ -587,8 +702,20 @@ static int mlx5e_alloc_rq(struct mlx5e_params *params, wq_sz = mlx5_wq_ll_get_size(&rq->mpwqe.wq); - pool_size = MLX5_MPWRQ_PAGES_PER_WQE << - mlx5e_mpwqe_get_log_rq_size(params, xsk); + rq->mpwqe.page_shift = mlx5e_mpwrq_page_shift(mdev, xsk); + rq->mpwqe.umr_mode = mlx5e_mpwrq_umr_mode(mdev, xsk); + rq->mpwqe.pages_per_wqe = + mlx5e_mpwrq_pages_per_wqe(mdev, rq->mpwqe.page_shift, + rq->mpwqe.umr_mode); + rq->mpwqe.umr_wqebbs = + mlx5e_mpwrq_umr_wqebbs(mdev, rq->mpwqe.page_shift, + rq->mpwqe.umr_mode); + rq->mpwqe.mtts_per_wqe = + mlx5e_mpwrq_mtts_per_wqe(mdev, rq->mpwqe.page_shift, + rq->mpwqe.umr_mode); + + pool_size = rq->mpwqe.pages_per_wqe << + mlx5e_mpwqe_get_log_rq_size(mdev, params, xsk); rq->mpwqe.log_stride_sz = mlx5e_mpwqe_get_log_stride_size(mdev, params, xsk); rq->mpwqe.num_strides = @@ -600,7 +727,6 @@ static int mlx5e_alloc_rq(struct mlx5e_params *params, err = mlx5e_create_rq_umr_mkey(mdev, rq); if (err) goto err_rq_drop_page; - rq->mkey_be = cpu_to_be32(rq->umr_mkey); err = mlx5e_rq_alloc_mpwqe_info(rq, node); if (err) @@ -608,7 +734,7 @@ static int mlx5e_alloc_rq(struct mlx5e_params *params, err = mlx5_rq_shampo_alloc(mdev, params, rqp, rq, &pool_size, node); if (err) - goto err_free_by_rq_type; + goto err_free_mpwqe_info; break; default: /* MLX5_WQ_TYPE_CYCLIC */ @@ -633,11 +759,9 @@ static int mlx5e_alloc_rq(struct mlx5e_params *params, goto err_rq_wq_destroy; } - err = mlx5e_init_di_list(rq, wq_sz, node); + err = mlx5e_init_au_list(rq, wq_sz, node); if (err) goto err_rq_frags; - - rq->mkey_be = cpu_to_be32(mdev->mlx5e_res.hw_objs.mkey); } if (xsk) { @@ -662,14 +786,14 @@ static int mlx5e_alloc_rq(struct mlx5e_params *params, if (IS_ERR(rq->page_pool)) { err = PTR_ERR(rq->page_pool); rq->page_pool = NULL; - goto err_free_shampo; + goto err_free_by_rq_type; } if (xdp_rxq_info_is_reg(&rq->xdp_rxq)) err = xdp_rxq_info_reg_mem_model(&rq->xdp_rxq, MEM_TYPE_PAGE_POOL, rq->page_pool); } if (err) - goto err_free_shampo; + goto err_destroy_page_pool; for (i = 0; i < wq_sz; i++) { if (rq->wq_type == MLX5_WQ_TYPE_LINKED_LIST_STRIDING_RQ) { @@ -677,13 +801,14 @@ static int mlx5e_alloc_rq(struct mlx5e_params *params, mlx5_wq_ll_get_wqe(&rq->mpwqe.wq, i); u32 byte_count = rq->mpwqe.num_strides << rq->mpwqe.log_stride_sz; - u64 dma_offset = mlx5e_get_mpwqe_offset(i); + u64 dma_offset = mul_u32_u32(i, rq->mpwqe.mtts_per_wqe) << + rq->mpwqe.page_shift; u16 headroom = test_bit(MLX5E_RQ_STATE_SHAMPO, &rq->state) ? 0 : rq->buff.headroom; wqe->data[0].addr = cpu_to_be64(dma_offset + headroom); wqe->data[0].byte_count = cpu_to_be32(byte_count); - wqe->data[0].lkey = rq->mkey_be; + wqe->data[0].lkey = rq->mpwqe.umr_mkey_be; } else { struct mlx5e_rx_wqe_cyc *wqe = mlx5_wq_cyc_get_wqe(&rq->wqe.wq, i); @@ -721,19 +846,21 @@ static int mlx5e_alloc_rq(struct mlx5e_params *params, return 0; -err_free_shampo: - mlx5e_rq_free_shampo(rq); +err_destroy_page_pool: + page_pool_destroy(rq->page_pool); err_free_by_rq_type: switch (rq->wq_type) { case MLX5_WQ_TYPE_LINKED_LIST_STRIDING_RQ: + mlx5e_rq_free_shampo(rq); +err_free_mpwqe_info: kvfree(rq->mpwqe.info); err_rq_mkey: - mlx5_core_destroy_mkey(mdev, rq->umr_mkey); + mlx5_core_destroy_mkey(mdev, be32_to_cpu(rq->mpwqe.umr_mkey_be)); err_rq_drop_page: mlx5e_free_mpwqe_rq_drop_page(rq); break; default: /* MLX5_WQ_TYPE_CYCLIC */ - mlx5e_free_di_list(rq); + mlx5e_free_au_list(rq); err_rq_frags: kvfree(rq->wqe.frags); } @@ -761,24 +888,22 @@ static void mlx5e_free_rq(struct mlx5e_rq *rq) switch (rq->wq_type) { case MLX5_WQ_TYPE_LINKED_LIST_STRIDING_RQ: kvfree(rq->mpwqe.info); - mlx5_core_destroy_mkey(rq->mdev, rq->umr_mkey); + mlx5_core_destroy_mkey(rq->mdev, be32_to_cpu(rq->mpwqe.umr_mkey_be)); mlx5e_free_mpwqe_rq_drop_page(rq); mlx5e_rq_free_shampo(rq); break; default: /* MLX5_WQ_TYPE_CYCLIC */ kvfree(rq->wqe.frags); - mlx5e_free_di_list(rq); + mlx5e_free_au_list(rq); } for (i = rq->page_cache.head; i != rq->page_cache.tail; i = (i + 1) & (MLX5E_CACHE_SIZE - 1)) { - struct mlx5e_dma_info *dma_info = &rq->page_cache.page_cache[i]; - /* With AF_XDP, page_cache is not used, so this loop is not * entered, and it's safe to call mlx5e_page_release_dynamic * directly. */ - mlx5e_page_release_dynamic(rq, dma_info->page, false); + mlx5e_page_release_dynamic(rq, rq->page_cache.page_cache[i], false); } xdp_rxq_info_unreg(&rq->xdp_rxq); @@ -833,7 +958,7 @@ int mlx5e_create_rq(struct mlx5e_rq *rq, struct mlx5e_rq_param *param) return err; } -int mlx5e_modify_rq_state(struct mlx5e_rq *rq, int curr_state, int next_state) +static int mlx5e_modify_rq_state(struct mlx5e_rq *rq, int curr_state, int next_state) { struct mlx5_core_dev *mdev = rq->mdev; @@ -862,6 +987,32 @@ int mlx5e_modify_rq_state(struct mlx5e_rq *rq, int curr_state, int next_state) return err; } +static int mlx5e_rq_to_ready(struct mlx5e_rq *rq, int curr_state) +{ + struct net_device *dev = rq->netdev; + int err; + + err = mlx5e_modify_rq_state(rq, curr_state, MLX5_RQC_STATE_RST); + if (err) { + netdev_err(dev, "Failed to move rq 0x%x to reset\n", rq->rqn); + return err; + } + err = mlx5e_modify_rq_state(rq, MLX5_RQC_STATE_RST, MLX5_RQC_STATE_RDY); + if (err) { + netdev_err(dev, "Failed to move rq 0x%x to ready\n", rq->rqn); + return err; + } + + return 0; +} + +int mlx5e_flush_rq(struct mlx5e_rq *rq, int curr_state) +{ + mlx5e_free_rx_descs(rq); + + return mlx5e_rq_to_ready(rq, curr_state); +} + static int mlx5e_modify_rq_scatter_fcs(struct mlx5e_rq *rq, bool enable) { struct mlx5_core_dev *mdev = rq->mdev; @@ -1154,9 +1305,9 @@ static int mlx5e_alloc_xdpsq(struct mlx5e_channel *c, is_redirect ? &c->priv->channel_stats[c->ix]->xdpsq : &c->priv->channel_stats[c->ix]->rq_xdpsq; - sq->max_sq_wqebbs = mlx5e_get_max_sq_wqebbs(mdev); - sq->stop_room = MLX5E_STOP_ROOM(sq->max_sq_wqebbs); - sq->max_sq_mpw_wqebbs = mlx5e_get_sw_max_sq_mpw_wqebbs(sq->max_sq_wqebbs); + sq->stop_room = param->is_mpw ? mlx5e_stop_room_for_mpwqe(mdev) : + mlx5e_stop_room_for_max_wqe(mdev); + sq->max_sq_mpw_wqebbs = mlx5e_get_max_sq_aligned_wqebbs(mdev); param->wq.db_numa_node = cpu_to_node(c->cpu); err = mlx5_wq_cyc_create(mdev, ¶m->wq, sqc_wq, wq, &sq->wq_ctrl); @@ -1231,7 +1382,6 @@ static int mlx5e_alloc_icosq(struct mlx5e_channel *c, sq->channel = c; sq->uar_map = mdev->mlx5e_res.hw_objs.bfreg.map; sq->reserved_room = param->stop_room; - sq->max_sq_wqebbs = mlx5e_get_max_sq_wqebbs(mdev); param->wq.db_numa_node = cpu_to_node(c->cpu); err = mlx5_wq_cyc_create(mdev, ¶m->wq, sqc_wq, wq, &sq->wq_ctrl); @@ -1317,8 +1467,7 @@ static int mlx5e_alloc_txqsq(struct mlx5e_channel *c, sq->uar_map = mdev->mlx5e_res.hw_objs.bfreg.map; sq->min_inline_mode = params->tx_min_inline_mode; sq->hw_mtu = MLX5E_SW2HW_MTU(params, params->sw_mtu); - sq->max_sq_wqebbs = mlx5e_get_max_sq_wqebbs(mdev); - sq->max_sq_mpw_wqebbs = mlx5e_get_sw_max_sq_mpw_wqebbs(sq->max_sq_wqebbs); + sq->max_sq_mpw_wqebbs = mlx5e_get_max_sq_aligned_wqebbs(mdev); INIT_WORK(&sq->recover_work, mlx5e_tx_err_cqe_work); if (!MLX5_CAP_ETH(mdev, wqe_vlan_insert)) set_bit(MLX5E_SQ_STATE_VLAN_NEED_L2_INLINE, &sq->state); @@ -2280,7 +2429,7 @@ static int mlx5e_open_channel(struct mlx5e_priv *priv, int ix, c->aff_mask = irq_get_effective_affinity_mask(irq); c->lag_port = mlx5e_enumerate_lag_port(priv->mdev, ix); - netif_napi_add(netdev, &c->napi, mlx5e_napi_poll, 64); + netif_napi_add(netdev, &c->napi, mlx5e_napi_poll); err = mlx5e_open_queues(c, params, cparam); if (unlikely(err)) @@ -2318,10 +2467,11 @@ static void mlx5e_activate_channel(struct mlx5e_channel *c) mlx5e_activate_txqsq(&c->sq[tc]); mlx5e_activate_icosq(&c->icosq); mlx5e_activate_icosq(&c->async_icosq); - mlx5e_activate_rq(&c->rq); if (test_bit(MLX5E_CHANNEL_STATE_XSK, c->state)) mlx5e_activate_xsk(c); + else + mlx5e_activate_rq(&c->rq); mlx5e_trigger_napi_icosq(c); } @@ -2332,8 +2482,9 @@ static void mlx5e_deactivate_channel(struct mlx5e_channel *c) if (test_bit(MLX5E_CHANNEL_STATE_XSK, c->state)) mlx5e_deactivate_xsk(c); + else + mlx5e_deactivate_rq(&c->rq); - mlx5e_deactivate_rq(&c->rq); mlx5e_deactivate_icosq(&c->async_icosq); mlx5e_deactivate_icosq(&c->icosq); for (tc = 0; tc < c->num_tc; tc++) @@ -2425,8 +2576,6 @@ static void mlx5e_activate_channels(struct mlx5e_channels *chs) mlx5e_ptp_activate_channel(chs->ptp); } -#define MLX5E_RQ_WQES_TIMEOUT 20000 /* msecs */ - static int mlx5e_wait_channels_min_rx_wqes(struct mlx5e_channels *chs) { int err = 0; @@ -2434,8 +2583,12 @@ static int mlx5e_wait_channels_min_rx_wqes(struct mlx5e_channels *chs) for (i = 0; i < chs->num; i++) { int timeout = err ? 0 : MLX5E_RQ_WQES_TIMEOUT; + struct mlx5e_channel *c = chs->c[i]; - err |= mlx5e_wait_for_min_rx_wqes(&chs->c[i]->rq, timeout); + if (test_bit(MLX5E_CHANNEL_STATE_XSK, c->state)) + continue; + + err |= mlx5e_wait_for_min_rx_wqes(&c->rq, timeout); /* Don't wait on the XSK RQ, because the newer xdpsock sample * doesn't provide any Fill Ring entries at the setup stage. @@ -2600,7 +2753,7 @@ static int mlx5e_update_netdev_queues(struct mlx5e_priv *priv) struct netdev_tc_txq old_tc_to_txq[TC_MAX_QUEUE], *tc_to_txq; struct net_device *netdev = priv->netdev; int old_num_txqs, old_ntc; - int num_rxqs, nch, ntc; + int nch, ntc; int err; int i; @@ -2611,7 +2764,6 @@ static int mlx5e_update_netdev_queues(struct mlx5e_priv *priv) nch = priv->channels.params.num_channels; ntc = priv->channels.params.mqprio.num_tc; - num_rxqs = nch * priv->profile->rq_groups; tc_to_txq = priv->channels.params.mqprio.tc_to_txq; err = mlx5e_netdev_set_tcs(netdev, nch, ntc, tc_to_txq); @@ -2620,7 +2772,7 @@ static int mlx5e_update_netdev_queues(struct mlx5e_priv *priv) err = mlx5e_update_tx_netdev_queues(priv); if (err) goto err_tcs; - err = netif_set_real_num_rx_queues(netdev, num_rxqs); + err = netif_set_real_num_rx_queues(netdev, nch); if (err) { netdev_warn(netdev, "netif_set_real_num_rx_queues failed, %d\n", err); goto err_txqs; @@ -2738,7 +2890,7 @@ void mlx5e_activate_priv_channels(struct mlx5e_priv *priv) netif_tx_start_all_queues(priv->netdev); if (mlx5e_is_vport_rep(priv)) - mlx5e_add_sqs_fwd_rules(priv); + mlx5e_rep_activate_channels(priv); mlx5e_wait_channels_min_rx_wqes(&priv->channels); @@ -2752,7 +2904,7 @@ void mlx5e_deactivate_priv_channels(struct mlx5e_priv *priv) mlx5e_rx_res_channels_deactivate(priv->rx_res); if (mlx5e_is_vport_rep(priv)) - mlx5e_remove_sqs_fwd_rules(priv); + mlx5e_rep_deactivate_channels(priv); /* The results of ndo_select_queue are unreliable, while netdev config * is being changed (real_num_tx_queues, num_tc). Stop all queues to @@ -3547,7 +3699,8 @@ mlx5e_get_stats(struct net_device *dev, struct rtnl_link_stats64 *stats) stats->rx_length_errors = PPORT_802_3_GET(pstats, a_in_range_length_errors) + PPORT_802_3_GET(pstats, a_out_of_range_length_field) + - PPORT_802_3_GET(pstats, a_frame_too_long_errors); + PPORT_802_3_GET(pstats, a_frame_too_long_errors) + + VNIC_ENV_GET(&priv->stats.vnic, eth_wqe_too_small); stats->rx_crc_errors = PPORT_802_3_GET(pstats, a_frame_check_sequence_errors); stats->rx_frame_errors = PPORT_802_3_GET(pstats, a_alignment_errors); @@ -3669,9 +3822,11 @@ static int set_feature_cvlan_filter(struct net_device *netdev, bool enable) struct mlx5e_priv *priv = netdev_priv(netdev); if (enable) - mlx5e_enable_cvlan_filter(priv); + mlx5e_enable_cvlan_filter(priv->fs, + !!(priv->netdev->flags & IFF_PROMISC)); else - mlx5e_disable_cvlan_filter(priv); + mlx5e_disable_cvlan_filter(priv->fs, + !!(priv->netdev->flags & IFF_PROMISC)); return 0; } @@ -3780,7 +3935,7 @@ static int set_feature_rx_vlan(struct net_device *netdev, bool enable) mutex_lock(&priv->state_lock); - priv->fs->vlan_strip_disable = !enable; + mlx5e_fs_set_vlan_strip_disable(priv->fs, !enable); priv->channels.params.vlan_strip_disable = !enable; if (!test_bit(MLX5E_STATE_OPENED, &priv->state)) @@ -3788,7 +3943,7 @@ static int set_feature_rx_vlan(struct net_device *netdev, bool enable) err = mlx5e_modify_channels_vsd(&priv->channels, !enable); if (err) { - priv->fs->vlan_strip_disable = enable; + mlx5e_fs_set_vlan_strip_disable(priv->fs, enable); priv->channels.params.vlan_strip_disable = enable; } unlock: @@ -3826,9 +3981,9 @@ static int set_feature_arfs(struct net_device *netdev, bool enable) int err; if (enable) - err = mlx5e_arfs_enable(priv); + err = mlx5e_arfs_enable(priv->fs); else - err = mlx5e_arfs_disable(priv); + err = mlx5e_arfs_disable(priv->fs); return err; } @@ -3912,12 +4067,14 @@ static netdev_features_t mlx5e_fix_features(struct net_device *netdev, netdev_features_t features) { struct mlx5e_priv *priv = netdev_priv(netdev); + struct mlx5e_vlan_table *vlan; struct mlx5e_params *params; + vlan = mlx5e_fs_get_vlan(priv->fs); mutex_lock(&priv->state_lock); params = &priv->channels.params; - if (!priv->fs->vlan || - !bitmap_empty(mlx5e_vlan_get_active_svlans(priv->fs->vlan), VLAN_N_VID)) { + if (!vlan || + !bitmap_empty(mlx5e_vlan_get_active_svlans(vlan), VLAN_N_VID)) { /* HW strips the outer C-tag header, this is a problem * for S-tag traffic. */ @@ -4006,7 +4163,7 @@ static bool mlx5e_xsk_validate_mtu(struct net_device *netdev, * 2. Size of SKBs allocated on XDP_PASS <= PAGE_SIZE. */ max_mtu_frame = MLX5E_HW2SW_MTU(new_params, xsk.chunk_size - hr); - max_mtu_page = mlx5e_xdp_max_mtu(new_params, &xsk); + max_mtu_page = MLX5E_HW2SW_MTU(new_params, SKB_MAX_HEAD(0)); max_mtu = min(max_mtu_frame, max_mtu_page); netdev_err(netdev, "MTU %d is too big for an XSK running on channel %u. Try MTU <= %d\n", @@ -4018,14 +4175,16 @@ static bool mlx5e_xsk_validate_mtu(struct net_device *netdev, return true; } -static bool mlx5e_params_validate_xdp(struct net_device *netdev, struct mlx5e_params *params) +static bool mlx5e_params_validate_xdp(struct net_device *netdev, + struct mlx5_core_dev *mdev, + struct mlx5e_params *params) { bool is_linear; /* No XSK params: AF_XDP can't be enabled yet at the point of setting * the XDP program. */ - is_linear = mlx5e_rx_is_linear_skb(params, NULL); + is_linear = mlx5e_rx_is_linear_skb(mdev, params, NULL); if (!is_linear && params->rq_wq_type != MLX5_WQ_TYPE_CYCLIC) { netdev_warn(netdev, "XDP is not allowed with striding RQ and MTU(%d) > %d\n", @@ -4062,7 +4221,8 @@ int mlx5e_change_mtu(struct net_device *netdev, int new_mtu, if (err) goto out; - if (new_params.xdp_prog && !mlx5e_params_validate_xdp(netdev, &new_params)) { + if (new_params.xdp_prog && !mlx5e_params_validate_xdp(netdev, priv->mdev, + &new_params)) { err = -EINVAL; goto out; } @@ -4077,19 +4237,21 @@ int mlx5e_change_mtu(struct net_device *netdev, int new_mtu, if (params->packet_merge.type == MLX5E_PACKET_MERGE_LRO) reset = false; - if (params->rq_wq_type == MLX5_WQ_TYPE_LINKED_LIST_STRIDING_RQ) { + if (params->rq_wq_type == MLX5_WQ_TYPE_LINKED_LIST_STRIDING_RQ && + params->packet_merge.type != MLX5E_PACKET_MERGE_SHAMPO) { bool is_linear_old = mlx5e_rx_mpwqe_is_linear_skb(priv->mdev, params, NULL); bool is_linear_new = mlx5e_rx_mpwqe_is_linear_skb(priv->mdev, &new_params, NULL); - u8 ppw_old = mlx5e_mpwqe_log_pkts_per_wqe(params, NULL); - u8 ppw_new = mlx5e_mpwqe_log_pkts_per_wqe(&new_params, NULL); + u8 sz_old = mlx5e_mpwqe_get_log_rq_size(priv->mdev, params, NULL); + u8 sz_new = mlx5e_mpwqe_get_log_rq_size(priv->mdev, &new_params, NULL); /* Always reset in linear mode - hw_mtu is used in data path. * Check that the mode was non-linear and didn't change. * If XSK is active, XSK RQs are linear. + * Reset if the RQ size changed, even if it's non-linear. */ if (!is_linear_old && !is_linear_new && !priv->xsk.refcnt && - ppw_old == ppw_new) + sz_old == sz_new) reset = false; } @@ -4539,7 +4701,7 @@ static int mlx5e_xdp_allowed(struct mlx5e_priv *priv, struct bpf_prog *prog) new_params = priv->channels.params; new_params.xdp_prog = prog; - if (!mlx5e_params_validate_xdp(netdev, &new_params)) + if (!mlx5e_params_validate_xdp(netdev, priv->mdev, &new_params)) return -EINVAL; return 0; @@ -4577,8 +4739,20 @@ static int mlx5e_xdp_set(struct net_device *netdev, struct bpf_prog *prog) new_params = priv->channels.params; new_params.xdp_prog = prog; - if (reset) - mlx5e_set_rq_type(priv->mdev, &new_params); + + /* XDP affects striding RQ parameters. Block XDP if striding RQ won't be + * supported with the new parameters: if PAGE_SIZE is bigger than + * MLX5_MPWQE_LOG_STRIDE_SZ_MAX, striding RQ can't be used, even though + * the MTU is small enough for the linear mode, because XDP uses strides + * of PAGE_SIZE on regular RQs. + */ + if (reset && MLX5E_GET_PFLAG(&new_params, MLX5E_PFLAG_RX_STRIDING_RQ)) { + /* Checking for regular RQs here; XSK RQs were checked on XSK bind. */ + err = mlx5e_mpwrq_validate_regular(priv->mdev, &new_params); + if (err) + goto unlock; + } + old_prog = priv->channels.params.xdp_prog; err = mlx5e_safe_switch_params(priv, &new_params, NULL, NULL, reset); @@ -4898,7 +5072,8 @@ static void mlx5e_build_nic_netdev(struct net_device *netdev) if (!!MLX5_CAP_ETH(mdev, lro_cap) && !MLX5_CAP_ETH(mdev, tunnel_lro_vxlan) && !MLX5_CAP_ETH(mdev, tunnel_lro_gre) && - mlx5e_check_fragmented_striding_rq_cap(mdev)) + mlx5e_check_fragmented_striding_rq_cap(mdev, PAGE_SHIFT, + MLX5E_MPWRQ_UMR_MODE_ALIGNED)) netdev->vlan_features |= NETIF_F_LRO; netdev->hw_features = netdev->vlan_features; @@ -4986,6 +5161,7 @@ static void mlx5e_build_nic_netdev(struct net_device *netdev) netif_set_tso_max_size(netdev, GSO_MAX_SIZE); mlx5e_set_netdev_dev_addr(netdev); + mlx5e_macsec_build_netdev(priv); mlx5e_ipsec_build_netdev(priv); mlx5e_ktls_build_netdev(priv); } @@ -5087,7 +5263,7 @@ static int mlx5e_init_nic_rx(struct mlx5e_priv *priv) goto err_destroy_q_counters; } - features = MLX5E_RX_RES_FEATURE_XSK | MLX5E_RX_RES_FEATURE_PTP; + features = MLX5E_RX_RES_FEATURE_PTP; if (priv->channels.params.tunneled_offload_en) features |= MLX5E_RX_RES_FEATURE_INNER_FT; err = mlx5e_rx_res_init(priv->rx_res, priv->mdev, features, @@ -5097,7 +5273,8 @@ static int mlx5e_init_nic_rx(struct mlx5e_priv *priv) if (err) goto err_close_drop_rq; - err = mlx5e_create_flow_steering(priv); + err = mlx5e_create_flow_steering(priv->fs, priv->rx_res, priv->profile, + priv->netdev); if (err) { mlx5_core_warn(mdev, "create flow steering failed, %d\n", err); goto err_destroy_rx_res; @@ -5120,7 +5297,8 @@ static int mlx5e_init_nic_rx(struct mlx5e_priv *priv) err_tc_nic_cleanup: mlx5e_tc_nic_cleanup(priv); err_destroy_flow_steering: - mlx5e_destroy_flow_steering(priv); + mlx5e_destroy_flow_steering(priv->fs, !!(priv->netdev->hw_features & NETIF_F_NTUPLE), + priv->profile); err_destroy_rx_res: mlx5e_rx_res_destroy(priv->rx_res); err_close_drop_rq: @@ -5136,7 +5314,8 @@ static void mlx5e_cleanup_nic_rx(struct mlx5e_priv *priv) { mlx5e_accel_cleanup_rx(priv); mlx5e_tc_nic_cleanup(priv); - mlx5e_destroy_flow_steering(priv); + mlx5e_destroy_flow_steering(priv->fs, !!(priv->netdev->hw_features & NETIF_F_NTUPLE), + priv->profile); mlx5e_rx_res_destroy(priv->rx_res); mlx5e_close_drop_rq(&priv->drop_rq); mlx5e_destroy_q_counters(priv); @@ -5188,9 +5367,14 @@ static void mlx5e_nic_enable(struct mlx5e_priv *priv) { struct net_device *netdev = priv->netdev; struct mlx5_core_dev *mdev = priv->mdev; + int err; mlx5e_fs_init_l2_addr(priv->fs, netdev); + err = mlx5e_macsec_init(priv); + if (err) + mlx5_core_err(mdev, "MACsec initialization failed, %d\n", err); + /* Marking the link as currently not needed by the Driver */ if (!netif_running(netdev)) mlx5e_modify_admin_state(mdev, MLX5_PORT_DOWN); @@ -5248,6 +5432,7 @@ static void mlx5e_nic_disable(struct mlx5e_priv *priv) mlx5e_disable_async_events(priv); mlx5_lag_remove_netdev(mdev, priv->netdev); mlx5_vxlan_reset_to_default(mdev->vxlan); + mlx5e_macsec_cleanup(priv); } int mlx5e_update_nic_rx(struct mlx5e_priv *priv) @@ -5269,7 +5454,6 @@ static const struct mlx5e_profile mlx5e_nic_profile = { .update_carrier = mlx5e_update_carrier, .rx_handlers = &mlx5e_rx_handlers_nic, .max_tc = MLX5E_MAX_NUM_TC, - .rq_groups = MLX5E_NUM_RQ_GROUPS(XSK), .stats_grps = mlx5e_nic_stats_grps, .stats_grps_num = mlx5e_nic_stats_grps_num, .features = BIT(MLX5E_PROFILE_FEATURE_PTP_RX) | @@ -5302,8 +5486,7 @@ mlx5e_calc_max_nch(struct mlx5_core_dev *mdev, struct net_device *netdev, max_nch = mlx5e_profile_max_num_channels(mdev, profile); /* netdev rx queues */ - tmp = netdev->num_rx_queues / max_t(u8, profile->rq_groups, 1); - max_nch = min_t(unsigned int, max_nch, tmp); + max_nch = min_t(unsigned int, max_nch, netdev->num_rx_queues); /* netdev tx queues */ tmp = netdev->num_tx_queues; @@ -5447,11 +5630,7 @@ static unsigned int mlx5e_get_max_num_txqs(struct mlx5_core_dev *mdev, static unsigned int mlx5e_get_max_num_rxqs(struct mlx5_core_dev *mdev, const struct mlx5e_profile *profile) { - unsigned int nch; - - nch = mlx5e_profile_max_num_channels(mdev, profile); - - return nch * profile->rq_groups; + return mlx5e_profile_max_num_channels(mdev, profile); } struct net_device * @@ -5512,7 +5691,8 @@ int mlx5e_attach_netdev(struct mlx5e_priv *priv) clear_bit(MLX5E_STATE_DESTROYING, &priv->state); if (priv->fs) - priv->fs->state_destroy = !test_bit(MLX5E_STATE_DESTROYING, &priv->state); + mlx5e_fs_set_state_destroy(priv->fs, + !test_bit(MLX5E_STATE_DESTROYING, &priv->state)); /* max number of channels may have changed */ max_nch = mlx5e_calc_max_nch(priv->mdev, priv->netdev, profile); @@ -5573,7 +5753,8 @@ out: mlx5e_reset_channels(priv->netdev); set_bit(MLX5E_STATE_DESTROYING, &priv->state); if (priv->fs) - priv->fs->state_destroy = !test_bit(MLX5E_STATE_DESTROYING, &priv->state); + mlx5e_fs_set_state_destroy(priv->fs, + !test_bit(MLX5E_STATE_DESTROYING, &priv->state)); cancel_work_sync(&priv->update_stats_work); return err; } @@ -5584,7 +5765,8 @@ void mlx5e_detach_netdev(struct mlx5e_priv *priv) set_bit(MLX5E_STATE_DESTROYING, &priv->state); if (priv->fs) - priv->fs->state_destroy = !test_bit(MLX5E_STATE_DESTROYING, &priv->state); + mlx5e_fs_set_state_destroy(priv->fs, + !test_bit(MLX5E_STATE_DESTROYING, &priv->state)); if (profile->disable) profile->disable(priv); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c index 759f7d3c2cfd..794cd8dfe9c9 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c @@ -56,6 +56,7 @@ #include "en_accel/ipsec.h" #include "en/tc/int_port.h" #include "en/ptp.h" +#include "en/fs_ethtool.h" #define MLX5E_REP_PARAMS_DEF_LOG_SQ_SIZE \ max(0x7, MLX5E_PARAMS_MINIMUM_LOG_SQ_SIZE) @@ -69,7 +70,7 @@ static void mlx5e_rep_get_drvinfo(struct net_device *dev, struct mlx5e_priv *priv = netdev_priv(dev); struct mlx5_core_dev *mdev = priv->mdev; - strlcpy(drvinfo->driver, mlx5e_rep_driver_name, + strscpy(drvinfo->driver, mlx5e_rep_driver_name, sizeof(drvinfo->driver)); snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version), "%d.%d.%04d (%.16s)", @@ -397,7 +398,8 @@ out_err: return err; } -int mlx5e_add_sqs_fwd_rules(struct mlx5e_priv *priv) +static int +mlx5e_add_sqs_fwd_rules(struct mlx5e_priv *priv) { int sqs_per_channel = mlx5e_get_dcb_num_tc(&priv->channels.params); struct mlx5_eswitch *esw = priv->mdev->priv.eswitch; @@ -451,7 +453,8 @@ out: return err; } -void mlx5e_remove_sqs_fwd_rules(struct mlx5e_priv *priv) +static void +mlx5e_remove_sqs_fwd_rules(struct mlx5e_priv *priv) { struct mlx5_eswitch *esw = priv->mdev->priv.eswitch; struct mlx5e_rep_priv *rpriv = priv->ppriv; @@ -460,6 +463,49 @@ void mlx5e_remove_sqs_fwd_rules(struct mlx5e_priv *priv) mlx5e_sqs2vport_stop(esw, rep); } +static int +mlx5e_rep_add_meta_tunnel_rule(struct mlx5e_priv *priv) +{ + struct mlx5_eswitch *esw = priv->mdev->priv.eswitch; + struct mlx5e_rep_priv *rpriv = priv->ppriv; + struct mlx5_eswitch_rep *rep = rpriv->rep; + struct mlx5_flow_handle *flow_rule; + struct mlx5_flow_group *g; + + g = esw->fdb_table.offloads.send_to_vport_meta_grp; + if (!g) + return 0; + + flow_rule = mlx5_eswitch_add_send_to_vport_meta_rule(esw, rep->vport); + if (IS_ERR(flow_rule)) + return PTR_ERR(flow_rule); + + rpriv->send_to_vport_meta_rule = flow_rule; + + return 0; +} + +static void +mlx5e_rep_del_meta_tunnel_rule(struct mlx5e_priv *priv) +{ + struct mlx5e_rep_priv *rpriv = priv->ppriv; + + if (rpriv->send_to_vport_meta_rule) + mlx5_eswitch_del_send_to_vport_meta_rule(rpriv->send_to_vport_meta_rule); +} + +void mlx5e_rep_activate_channels(struct mlx5e_priv *priv) +{ + mlx5e_add_sqs_fwd_rules(priv); + mlx5e_rep_add_meta_tunnel_rule(priv); +} + +void mlx5e_rep_deactivate_channels(struct mlx5e_priv *priv) +{ + mlx5e_rep_del_meta_tunnel_rule(priv); + mlx5e_remove_sqs_fwd_rules(priv); +} + static int mlx5e_rep_open(struct net_device *dev) { struct mlx5e_priv *priv = netdev_priv(dev); @@ -747,19 +793,20 @@ static int mlx5e_create_rep_ttc_table(struct mlx5e_priv *priv) struct ttc_params ttc_params = {}; int err; - priv->fs->ns = mlx5_get_flow_namespace(priv->mdev, - MLX5_FLOW_NAMESPACE_KERNEL); + mlx5e_fs_set_ns(priv->fs, + mlx5_get_flow_namespace(priv->mdev, + MLX5_FLOW_NAMESPACE_KERNEL), false); /* The inner_ttc in the ttc params is intentionally not set */ - mlx5e_set_ttc_params(priv, &ttc_params, false); + mlx5e_set_ttc_params(priv->fs, priv->rx_res, &ttc_params, false); if (rep->vport != MLX5_VPORT_UPLINK) /* To give uplik rep TTC a lower level for chaining from root ft */ ttc_params.ft_attr.level = MLX5E_TTC_FT_LEVEL + 1; - priv->fs->ttc = mlx5_create_ttc_table(priv->mdev, &ttc_params); - if (IS_ERR(priv->fs->ttc)) { - err = PTR_ERR(priv->fs->ttc); + mlx5e_fs_set_ttc(priv->fs, mlx5_create_ttc_table(priv->mdev, &ttc_params), false); + if (IS_ERR(mlx5e_fs_get_ttc(priv->fs, false))) { + err = PTR_ERR(mlx5e_fs_get_ttc(priv->fs, false)); netdev_err(priv->netdev, "Failed to create rep ttc table, err=%d\n", err); return err; @@ -779,7 +826,7 @@ static int mlx5e_create_rep_root_ft(struct mlx5e_priv *priv) /* non uplik reps will skip any bypass tables and go directly to * their own ttc */ - rpriv->root_ft = mlx5_get_ttc_flow_table(priv->fs->ttc); + rpriv->root_ft = mlx5_get_ttc_flow_table(mlx5e_fs_get_ttc(priv->fs, false)); return 0; } @@ -887,14 +934,14 @@ static int mlx5e_init_rep_rx(struct mlx5e_priv *priv) if (err) goto err_destroy_root_ft; - mlx5e_ethtool_init_steering(priv); + mlx5e_ethtool_init_steering(priv->fs); return 0; err_destroy_root_ft: mlx5e_destroy_rep_root_ft(priv); err_destroy_ttc_table: - mlx5_destroy_ttc_table(priv->fs->ttc); + mlx5_destroy_ttc_table(mlx5e_fs_get_ttc(priv->fs, false)); err_destroy_rx_res: mlx5e_rx_res_destroy(priv->rx_res); err_close_drop_rq: @@ -908,10 +955,10 @@ err_free_fs: static void mlx5e_cleanup_rep_rx(struct mlx5e_priv *priv) { - mlx5e_ethtool_cleanup_steering(priv); + mlx5e_ethtool_cleanup_steering(priv->fs); rep_vport_rx_rule_destroy(priv); mlx5e_destroy_rep_root_ft(priv); - mlx5_destroy_ttc_table(priv->fs->ttc); + mlx5_destroy_ttc_table(mlx5e_fs_get_ttc(priv->fs, false)); mlx5e_rx_res_destroy(priv->rx_res); mlx5e_close_drop_rq(&priv->drop_rq); mlx5e_rx_res_free(priv->rx_res); @@ -1177,7 +1224,6 @@ static const struct mlx5e_profile mlx5e_rep_profile = { .update_stats = mlx5e_stats_update_ndo_stats, .rx_handlers = &mlx5e_rx_handlers_rep, .max_tc = 1, - .rq_groups = MLX5E_NUM_RQ_GROUPS(REGULAR), .stats_grps = mlx5e_rep_stats_grps, .stats_grps_num = mlx5e_rep_stats_grps_num, .max_nch_limit = mlx5e_rep_max_nch_limit, @@ -1197,8 +1243,6 @@ static const struct mlx5e_profile mlx5e_uplink_rep_profile = { .update_carrier = mlx5e_update_carrier, .rx_handlers = &mlx5e_rx_handlers_rep, .max_tc = MLX5E_MAX_NUM_TC, - /* XSK is needed so we can replace profile with NIC netdev */ - .rq_groups = MLX5E_NUM_RQ_GROUPS(XSK), .stats_grps = mlx5e_ul_rep_stats_grps, .stats_grps_num = mlx5e_ul_rep_stats_grps_num, }; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.h b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.h index dec183ccd4ac..b4e691760da9 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.h @@ -111,6 +111,7 @@ struct mlx5e_rep_priv { struct list_head vport_sqs_list; struct mlx5_rep_uplink_priv uplink_priv; /* valid for uplink rep */ struct rtnl_link_stats64 prev_vf_vport_stats; + struct mlx5_flow_handle *send_to_vport_meta_rule; struct rhashtable tc_ht; }; @@ -241,8 +242,8 @@ int mlx5e_rep_get_offload_stats(int attr_id, const struct net_device *dev, void *sp); bool mlx5e_is_uplink_rep(struct mlx5e_priv *priv); -int mlx5e_add_sqs_fwd_rules(struct mlx5e_priv *priv); -void mlx5e_remove_sqs_fwd_rules(struct mlx5e_priv *priv); +void mlx5e_rep_activate_channels(struct mlx5e_priv *priv); +void mlx5e_rep_deactivate_channels(struct mlx5e_priv *priv); void mlx5e_rep_queue_neigh_stats_work(struct mlx5e_priv *priv); @@ -256,8 +257,8 @@ static inline bool mlx5e_eswitch_rep(const struct net_device *netdev) #else /* CONFIG_MLX5_ESWITCH */ static inline bool mlx5e_is_uplink_rep(struct mlx5e_priv *priv) { return false; } -static inline int mlx5e_add_sqs_fwd_rules(struct mlx5e_priv *priv) { return 0; } -static inline void mlx5e_remove_sqs_fwd_rules(struct mlx5e_priv *priv) {} +static inline void mlx5e_rep_activate_channels(struct mlx5e_priv *priv) {} +static inline void mlx5e_rep_deactivate_channels(struct mlx5e_priv *priv) {} static inline int mlx5e_rep_init(void) { return 0; }; static inline void mlx5e_rep_cleanup(void) {}; static inline bool mlx5e_rep_has_offload_stats(const struct net_device *dev, diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c index 24de37b79f5a..58084650151f 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c @@ -41,6 +41,7 @@ #include <net/gro.h> #include <net/udp.h> #include <net/tcp.h> +#include <net/xdp_sock_drv.h> #include "en.h" #include "en/txrx.h" #include "en_tc.h" @@ -49,6 +50,7 @@ #include "en/rep/tc.h" #include "ipoib/ipoib.h" #include "en_accel/ipsec.h" +#include "en_accel/macsec.h" #include "en_accel/ipsec_rxtx.h" #include "en_accel/ktls_txrx.h" #include "en/xdp.h" @@ -237,69 +239,61 @@ static inline bool mlx5e_rx_cache_put(struct mlx5e_rq *rq, struct page *page) return false; } - cache->page_cache[cache->tail].page = page; - cache->page_cache[cache->tail].addr = page_pool_get_dma_addr(page); + cache->page_cache[cache->tail] = page; cache->tail = tail_next; return true; } -static inline bool mlx5e_rx_cache_get(struct mlx5e_rq *rq, - struct mlx5e_dma_info *dma_info) +static inline bool mlx5e_rx_cache_get(struct mlx5e_rq *rq, union mlx5e_alloc_unit *au) { struct mlx5e_page_cache *cache = &rq->page_cache; struct mlx5e_rq_stats *stats = rq->stats; + dma_addr_t addr; if (unlikely(cache->head == cache->tail)) { stats->cache_empty++; return false; } - if (page_ref_count(cache->page_cache[cache->head].page) != 1) { + if (page_ref_count(cache->page_cache[cache->head]) != 1) { stats->cache_busy++; return false; } - *dma_info = cache->page_cache[cache->head]; + au->page = cache->page_cache[cache->head]; cache->head = (cache->head + 1) & (MLX5E_CACHE_SIZE - 1); stats->cache_reuse++; - dma_sync_single_for_device(rq->pdev, dma_info->addr, - PAGE_SIZE, - DMA_FROM_DEVICE); + addr = page_pool_get_dma_addr(au->page); + /* Non-XSK always uses PAGE_SIZE. */ + dma_sync_single_for_device(rq->pdev, addr, PAGE_SIZE, DMA_FROM_DEVICE); return true; } -static inline int mlx5e_page_alloc_pool(struct mlx5e_rq *rq, - struct mlx5e_dma_info *dma_info) +static inline int mlx5e_page_alloc_pool(struct mlx5e_rq *rq, union mlx5e_alloc_unit *au) { - if (mlx5e_rx_cache_get(rq, dma_info)) + dma_addr_t addr; + + if (mlx5e_rx_cache_get(rq, au)) return 0; - dma_info->page = page_pool_dev_alloc_pages(rq->page_pool); - if (unlikely(!dma_info->page)) + au->page = page_pool_dev_alloc_pages(rq->page_pool); + if (unlikely(!au->page)) return -ENOMEM; - dma_info->addr = dma_map_page_attrs(rq->pdev, dma_info->page, 0, PAGE_SIZE, - rq->buff.map_dir, DMA_ATTR_SKIP_CPU_SYNC); - if (unlikely(dma_mapping_error(rq->pdev, dma_info->addr))) { - page_pool_recycle_direct(rq->page_pool, dma_info->page); - dma_info->page = NULL; + /* Non-XSK always uses PAGE_SIZE. */ + addr = dma_map_page_attrs(rq->pdev, au->page, 0, PAGE_SIZE, + rq->buff.map_dir, DMA_ATTR_SKIP_CPU_SYNC); + if (unlikely(dma_mapping_error(rq->pdev, addr))) { + page_pool_recycle_direct(rq->page_pool, au->page); + au->page = NULL; return -ENOMEM; } - page_pool_set_dma_addr(dma_info->page, dma_info->addr); + page_pool_set_dma_addr(au->page, addr); return 0; } -static inline int mlx5e_page_alloc(struct mlx5e_rq *rq, - struct mlx5e_dma_info *dma_info) -{ - if (rq->xsk_pool) - return mlx5e_xsk_page_alloc_pool(rq, dma_info); - else - return mlx5e_page_alloc_pool(rq, dma_info); -} - void mlx5e_page_dma_unmap(struct mlx5e_rq *rq, struct page *page) { dma_addr_t dma_addr = page_pool_get_dma_addr(page); @@ -324,32 +318,18 @@ void mlx5e_page_release_dynamic(struct mlx5e_rq *rq, struct page *page, bool rec } } -static inline void mlx5e_page_release(struct mlx5e_rq *rq, - struct mlx5e_dma_info *dma_info, - bool recycle) -{ - if (rq->xsk_pool) - /* The `recycle` parameter is ignored, and the page is always - * put into the Reuse Ring, because there is no way to return - * the page to the userspace when the interface goes down. - */ - xsk_buff_free(dma_info->xsk); - else - mlx5e_page_release_dynamic(rq, dma_info->page, recycle); -} - static inline int mlx5e_get_rx_frag(struct mlx5e_rq *rq, struct mlx5e_wqe_frag_info *frag) { int err = 0; if (!frag->offset) - /* On first frag (offset == 0), replenish page (dma_info actually). - * Other frags that point to the same dma_info (with a different + /* On first frag (offset == 0), replenish page (alloc_unit actually). + * Other frags that point to the same alloc_unit (with a different * offset) should just use the new one without replenishing again * by themselves. */ - err = mlx5e_page_alloc(rq, frag->di); + err = mlx5e_page_alloc_pool(rq, frag->au); return err; } @@ -359,7 +339,7 @@ static inline void mlx5e_put_rx_frag(struct mlx5e_rq *rq, bool recycle) { if (frag->last_in_page) - mlx5e_page_release(rq, frag->di, recycle); + mlx5e_page_release_dynamic(rq, frag->au->page, recycle); } static inline struct mlx5e_wqe_frag_info *get_frag(struct mlx5e_rq *rq, u16 ix) @@ -375,6 +355,7 @@ static int mlx5e_alloc_rx_wqe(struct mlx5e_rq *rq, struct mlx5e_rx_wqe_cyc *wqe, int i; for (i = 0; i < rq->wqe.info.num_frags; i++, frag++) { + dma_addr_t addr; u16 headroom; err = mlx5e_get_rx_frag(rq, frag); @@ -382,8 +363,8 @@ static int mlx5e_alloc_rx_wqe(struct mlx5e_rq *rq, struct mlx5e_rx_wqe_cyc *wqe, goto free_frags; headroom = i == 0 ? rq->buff.headroom : 0; - wqe->data[i].addr = cpu_to_be64(frag->di->addr + - frag->offset + headroom); + addr = page_pool_get_dma_addr(frag->au->page); + wqe->data[i].addr = cpu_to_be64(addr + frag->offset + headroom); } return 0; @@ -401,6 +382,15 @@ static inline void mlx5e_free_rx_wqe(struct mlx5e_rq *rq, { int i; + if (rq->xsk_pool) { + /* The `recycle` parameter is ignored, and the page is always + * put into the Reuse Ring, because there is no way to return + * the page to the userspace when the interface goes down. + */ + xsk_buff_free(wi->au->xsk); + return; + } + for (i = 0; i < rq->wqe.info.num_frags; i++, wi++) mlx5e_put_rx_frag(rq, wi, recycle); } @@ -412,84 +402,76 @@ static void mlx5e_dealloc_rx_wqe(struct mlx5e_rq *rq, u16 ix) mlx5e_free_rx_wqe(rq, wi, false); } -static int mlx5e_alloc_rx_wqes(struct mlx5e_rq *rq, u16 ix, u8 wqe_bulk) +static int mlx5e_alloc_rx_wqes(struct mlx5e_rq *rq, u16 ix, int wqe_bulk) { struct mlx5_wq_cyc *wq = &rq->wqe.wq; - int err; int i; - if (rq->xsk_pool) { - int pages_desired = wqe_bulk << rq->wqe.info.log_num_frags; - - /* Check in advance that we have enough frames, instead of - * allocating one-by-one, failing and moving frames to the - * Reuse Ring. - */ - if (unlikely(!xsk_buff_can_alloc(rq->xsk_pool, pages_desired))) - return -ENOMEM; - } - for (i = 0; i < wqe_bulk; i++) { - struct mlx5e_rx_wqe_cyc *wqe = mlx5_wq_cyc_get_wqe(wq, ix + i); - - err = mlx5e_alloc_rx_wqe(rq, wqe, ix + i); - if (unlikely(err)) - goto free_wqes; - } + int j = mlx5_wq_cyc_ctr2ix(wq, ix + i); + struct mlx5e_rx_wqe_cyc *wqe; - return 0; + wqe = mlx5_wq_cyc_get_wqe(wq, j); -free_wqes: - while (--i >= 0) - mlx5e_dealloc_rx_wqe(rq, ix + i); + if (unlikely(mlx5e_alloc_rx_wqe(rq, wqe, j))) + break; + } - return err; + return i; } static inline void mlx5e_add_skb_frag(struct mlx5e_rq *rq, struct sk_buff *skb, - struct mlx5e_dma_info *di, u32 frag_offset, u32 len, + union mlx5e_alloc_unit *au, u32 frag_offset, u32 len, unsigned int truesize) { - dma_sync_single_for_cpu(rq->pdev, - di->addr + frag_offset, - len, DMA_FROM_DEVICE); - page_ref_inc(di->page); + dma_addr_t addr = page_pool_get_dma_addr(au->page); + + dma_sync_single_for_cpu(rq->pdev, addr + frag_offset, len, DMA_FROM_DEVICE); + page_ref_inc(au->page); skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, - di->page, frag_offset, len, truesize); + au->page, frag_offset, len, truesize); } static inline void mlx5e_copy_skb_header(struct device *pdev, struct sk_buff *skb, - struct mlx5e_dma_info *dma_info, + struct page *page, dma_addr_t addr, int offset_from, int dma_offset, u32 headlen) { - const void *from = page_address(dma_info->page) + offset_from; + const void *from = page_address(page) + offset_from; /* Aligning len to sizeof(long) optimizes memcpy performance */ unsigned int len = ALIGN(headlen, sizeof(long)); - dma_sync_single_for_cpu(pdev, dma_info->addr + dma_offset, len, - DMA_FROM_DEVICE); + dma_sync_single_for_cpu(pdev, addr + dma_offset, len, DMA_FROM_DEVICE); skb_copy_to_linear_data(skb, from, len); } static void mlx5e_free_rx_mpwqe(struct mlx5e_rq *rq, struct mlx5e_mpw_info *wi, bool recycle) { + union mlx5e_alloc_unit *alloc_units = wi->alloc_units; bool no_xdp_xmit; - struct mlx5e_dma_info *dma_info = wi->umr.dma_info; int i; /* A common case for AF_XDP. */ - if (bitmap_full(wi->xdp_xmit_bitmap, MLX5_MPWRQ_PAGES_PER_WQE)) + if (bitmap_full(wi->xdp_xmit_bitmap, rq->mpwqe.pages_per_wqe)) return; - no_xdp_xmit = bitmap_empty(wi->xdp_xmit_bitmap, - MLX5_MPWRQ_PAGES_PER_WQE); + no_xdp_xmit = bitmap_empty(wi->xdp_xmit_bitmap, rq->mpwqe.pages_per_wqe); - for (i = 0; i < MLX5_MPWRQ_PAGES_PER_WQE; i++) - if (no_xdp_xmit || !test_bit(i, wi->xdp_xmit_bitmap)) - mlx5e_page_release(rq, &dma_info[i], recycle); + if (rq->xsk_pool) { + /* The `recycle` parameter is ignored, and the page is always + * put into the Reuse Ring, because there is no way to return + * the page to the userspace when the interface goes down. + */ + for (i = 0; i < rq->mpwqe.pages_per_wqe; i++) + if (no_xdp_xmit || !test_bit(i, wi->xdp_xmit_bitmap)) + xsk_buff_free(alloc_units[i].xsk); + } else { + for (i = 0; i < rq->mpwqe.pages_per_wqe; i++) + if (no_xdp_xmit || !test_bit(i, wi->xdp_xmit_bitmap)) + mlx5e_page_release_dynamic(rq, alloc_units[i].page, recycle); + } } static void mlx5e_post_rx_mpwqe(struct mlx5e_rq *rq, u8 n) @@ -574,11 +556,13 @@ static int mlx5e_build_shampo_hd_umr(struct mlx5e_rq *rq, header_offset = (index & (MLX5E_SHAMPO_WQ_HEADER_PER_PAGE - 1)) << MLX5E_SHAMPO_LOG_MAX_HEADER_ENTRY_SIZE; if (!(header_offset & (PAGE_SIZE - 1))) { - err = mlx5e_page_alloc(rq, dma_info); + union mlx5e_alloc_unit au; + + err = mlx5e_page_alloc_pool(rq, &au); if (unlikely(err)) goto err_unmap; - addr = dma_info->addr; - page = dma_info->page; + page = dma_info->page = au.page; + addr = dma_info->addr = page_pool_get_dma_addr(au.page); } else { dma_info->addr = addr + header_offset; dma_info->page = page; @@ -611,7 +595,7 @@ err_unmap: dma_info = &shampo->info[--index]; if (!(i & (MLX5E_SHAMPO_WQ_HEADER_PER_PAGE - 1))) { dma_info->addr = ALIGN_DOWN(dma_info->addr, PAGE_SIZE); - mlx5e_page_release(rq, dma_info, true); + mlx5e_page_release_dynamic(rq, dma_info->page, true); } } rq->stats->buff_alloc_err++; @@ -659,57 +643,55 @@ static int mlx5e_alloc_rx_hd_mpwqe(struct mlx5e_rq *rq) static int mlx5e_alloc_rx_mpwqe(struct mlx5e_rq *rq, u16 ix) { - struct mlx5e_mpw_info *wi = &rq->mpwqe.info[ix]; - struct mlx5e_dma_info *dma_info = &wi->umr.dma_info[0]; + struct mlx5e_mpw_info *wi = mlx5e_get_mpw_info(rq, ix); + union mlx5e_alloc_unit *au = &wi->alloc_units[0]; struct mlx5e_icosq *sq = rq->icosq; struct mlx5_wq_cyc *wq = &sq->wq; struct mlx5e_umr_wqe *umr_wqe; + u32 offset; /* 17-bit value with MTT. */ u16 pi; int err; int i; - /* Check in advance that we have enough frames, instead of allocating - * one-by-one, failing and moving frames to the Reuse Ring. - */ - if (rq->xsk_pool && - unlikely(!xsk_buff_can_alloc(rq->xsk_pool, MLX5_MPWRQ_PAGES_PER_WQE))) { - err = -ENOMEM; - goto err; - } - if (test_bit(MLX5E_RQ_STATE_SHAMPO, &rq->state)) { err = mlx5e_alloc_rx_hd_mpwqe(rq); if (unlikely(err)) goto err; } - pi = mlx5e_icosq_get_next_pi(sq, MLX5E_UMR_WQEBBS); + pi = mlx5e_icosq_get_next_pi(sq, rq->mpwqe.umr_wqebbs); umr_wqe = mlx5_wq_cyc_get_wqe(wq, pi); - memcpy(umr_wqe, &rq->mpwqe.umr_wqe, offsetof(struct mlx5e_umr_wqe, inline_mtts)); + memcpy(umr_wqe, &rq->mpwqe.umr_wqe, sizeof(struct mlx5e_umr_wqe)); + + for (i = 0; i < rq->mpwqe.pages_per_wqe; i++, au++) { + dma_addr_t addr; - for (i = 0; i < MLX5_MPWRQ_PAGES_PER_WQE; i++, dma_info++) { - err = mlx5e_page_alloc(rq, dma_info); + err = mlx5e_page_alloc_pool(rq, au); if (unlikely(err)) goto err_unmap; - umr_wqe->inline_mtts[i].ptag = cpu_to_be64(dma_info->addr | MLX5_EN_WR); + addr = page_pool_get_dma_addr(au->page); + umr_wqe->inline_mtts[i] = (struct mlx5_mtt) { + .ptag = cpu_to_be64(addr | MLX5_EN_WR), + }; } - bitmap_zero(wi->xdp_xmit_bitmap, MLX5_MPWRQ_PAGES_PER_WQE); + bitmap_zero(wi->xdp_xmit_bitmap, rq->mpwqe.pages_per_wqe); wi->consumed_strides = 0; umr_wqe->ctrl.opmod_idx_opcode = cpu_to_be32((sq->pc << MLX5_WQE_CTRL_WQE_INDEX_SHIFT) | MLX5_OPCODE_UMR); - umr_wqe->uctrl.xlt_offset = - cpu_to_be16(MLX5_ALIGNED_MTTS_OCTW(MLX5E_REQUIRED_MTTS(ix))); + + offset = (ix * rq->mpwqe.mtts_per_wqe) * sizeof(struct mlx5_mtt) / MLX5_OCTWORD; + umr_wqe->uctrl.xlt_offset = cpu_to_be16(offset); sq->db.wqe_info[pi] = (struct mlx5e_icosq_wqe_info) { .wqe_type = MLX5E_ICOSQ_WQE_UMR_RX, - .num_wqebbs = MLX5E_UMR_WQEBBS, + .num_wqebbs = rq->mpwqe.umr_wqebbs, .umr.rq = rq, }; - sq->pc += MLX5E_UMR_WQEBBS; + sq->pc += rq->mpwqe.umr_wqebbs; sq->doorbell_cseg = &umr_wqe->ctrl; @@ -717,8 +699,8 @@ static int mlx5e_alloc_rx_mpwqe(struct mlx5e_rq *rq, u16 ix) err_unmap: while (--i >= 0) { - dma_info--; - mlx5e_page_release(rq, dma_info, true); + au--; + mlx5e_page_release_dynamic(rq, au->page, true); } err: @@ -752,7 +734,7 @@ void mlx5e_shampo_dealloc_hd(struct mlx5e_rq *rq, u16 len, u16 start, bool close hd_info->addr = ALIGN_DOWN(hd_info->addr, PAGE_SIZE); if (hd_info->page != deleted_page) { deleted_page = hd_info->page; - mlx5e_page_release(rq, hd_info, false); + mlx5e_page_release_dynamic(rq, hd_info->page, false); } } @@ -767,7 +749,7 @@ void mlx5e_shampo_dealloc_hd(struct mlx5e_rq *rq, u16 len, u16 start, bool close static void mlx5e_dealloc_rx_mpwqe(struct mlx5e_rq *rq, u16 ix) { - struct mlx5e_mpw_info *wi = &rq->mpwqe.info[ix]; + struct mlx5e_mpw_info *wi = mlx5e_get_mpw_info(rq, ix); /* Don't recycle, this function is called on rq/netdev close */ mlx5e_free_rx_mpwqe(rq, wi, false); } @@ -775,38 +757,51 @@ static void mlx5e_dealloc_rx_mpwqe(struct mlx5e_rq *rq, u16 ix) INDIRECT_CALLABLE_SCOPE bool mlx5e_post_rx_wqes(struct mlx5e_rq *rq) { struct mlx5_wq_cyc *wq = &rq->wqe.wq; - u8 wqe_bulk; - int err; + int wqe_bulk, count; + bool busy = false; + u16 head; if (unlikely(!test_bit(MLX5E_RQ_STATE_ENABLED, &rq->state))) return false; - wqe_bulk = rq->wqe.info.wqe_bulk; - - if (mlx5_wq_cyc_missing(wq) < wqe_bulk) + if (mlx5_wq_cyc_missing(wq) < rq->wqe.info.wqe_bulk) return false; if (rq->page_pool) page_pool_nid_changed(rq->page_pool, numa_mem_id()); - do { - u16 head = mlx5_wq_cyc_get_head(wq); + wqe_bulk = mlx5_wq_cyc_missing(wq); + head = mlx5_wq_cyc_get_head(wq); - err = mlx5e_alloc_rx_wqes(rq, head, wqe_bulk); - if (unlikely(err)) { - rq->stats->buff_alloc_err++; - break; - } + /* Don't allow any newly allocated WQEs to share the same page with old + * WQEs that aren't completed yet. Stop earlier. + */ + wqe_bulk -= (head + wqe_bulk) & rq->wqe.info.wqe_index_mask; - mlx5_wq_cyc_push_n(wq, wqe_bulk); - } while (mlx5_wq_cyc_missing(wq) >= wqe_bulk); + if (!rq->xsk_pool) + count = mlx5e_alloc_rx_wqes(rq, head, wqe_bulk); + else if (likely(!rq->xsk_pool->dma_need_sync)) + count = mlx5e_xsk_alloc_rx_wqes_batched(rq, head, wqe_bulk); + else + /* If dma_need_sync is true, it's more efficient to call + * xsk_buff_alloc in a loop, rather than xsk_buff_alloc_batch, + * because the latter does the same check and returns only one + * frame. + */ + count = mlx5e_xsk_alloc_rx_wqes(rq, head, wqe_bulk); + + mlx5_wq_cyc_push_n(wq, count); + if (unlikely(count != wqe_bulk)) { + rq->stats->buff_alloc_err++; + busy = true; + } /* ensure wqes are visible to device before updating doorbell record */ dma_wmb(); mlx5_wq_cyc_update_db_record(wq); - return !!err; + return busy; } void mlx5e_free_icosq_descs(struct mlx5e_icosq *sq) @@ -974,7 +969,8 @@ INDIRECT_CALLABLE_SCOPE bool mlx5e_post_rx_mpwqes(struct mlx5e_rq *rq) head = rq->mpwqe.actual_wq_head; i = missing; do { - alloc_err = mlx5e_alloc_rx_mpwqe(rq, head); + alloc_err = rq->xsk_pool ? mlx5e_xsk_alloc_rx_mpwqe(rq, head) : + mlx5e_alloc_rx_mpwqe(rq, head); if (unlikely(alloc_err)) break; @@ -1421,6 +1417,9 @@ static inline void mlx5e_build_rx_skb(struct mlx5_cqe64 *cqe, if (unlikely(mlx5_ipsec_is_rx_flow(cqe))) mlx5e_ipsec_offload_handle_rx_skb(netdev, skb, cqe); + if (unlikely(mlx5e_macsec_is_rx_flow(cqe))) + mlx5e_macsec_offload_handle_rx_skb(netdev, skb, cqe); + if (lro_num_seg > 1) { mlx5e_lro_update_hdr(skb, cqe, cqe_bcnt); skb_shinfo(skb)->gso_size = DIV_ROUND_UP(cqe_bcnt, lro_num_seg); @@ -1524,19 +1523,21 @@ static struct sk_buff * mlx5e_skb_from_cqe_linear(struct mlx5e_rq *rq, struct mlx5e_wqe_frag_info *wi, u32 cqe_bcnt) { - struct mlx5e_dma_info *di = wi->di; + union mlx5e_alloc_unit *au = wi->au; u16 rx_headroom = rq->buff.headroom; struct bpf_prog *prog; struct sk_buff *skb; u32 metasize = 0; void *va, *data; + dma_addr_t addr; u32 frag_size; - va = page_address(di->page) + wi->offset; + va = page_address(au->page) + wi->offset; data = va + rx_headroom; frag_size = MLX5_SKB_FRAG_SZ(rx_headroom + cqe_bcnt); - dma_sync_single_range_for_cpu(rq->pdev, di->addr, wi->offset, + addr = page_pool_get_dma_addr(au->page); + dma_sync_single_range_for_cpu(rq->pdev, addr, wi->offset, frag_size, DMA_FROM_DEVICE); net_prefetch(data); @@ -1546,7 +1547,7 @@ mlx5e_skb_from_cqe_linear(struct mlx5e_rq *rq, struct mlx5e_wqe_frag_info *wi, net_prefetchw(va); /* xdp_frame data area */ mlx5e_fill_xdp_buff(rq, va, rx_headroom, cqe_bcnt, &xdp); - if (mlx5e_xdp_handle(rq, di->page, prog, &xdp)) + if (mlx5e_xdp_handle(rq, au->page, prog, &xdp)) return NULL; /* page/packet was consumed by XDP */ rx_headroom = xdp.data - xdp.data_hard_start; @@ -1559,7 +1560,7 @@ mlx5e_skb_from_cqe_linear(struct mlx5e_rq *rq, struct mlx5e_wqe_frag_info *wi, return NULL; /* queue up for recycling/reuse */ - page_ref_inc(di->page); + page_ref_inc(au->page); return skb; } @@ -1570,20 +1571,22 @@ mlx5e_skb_from_cqe_nonlinear(struct mlx5e_rq *rq, struct mlx5e_wqe_frag_info *wi { struct mlx5e_rq_frag_info *frag_info = &rq->wqe.info.arr[0]; struct mlx5e_wqe_frag_info *head_wi = wi; + union mlx5e_alloc_unit *au = wi->au; u16 rx_headroom = rq->buff.headroom; - struct mlx5e_dma_info *di = wi->di; struct skb_shared_info *sinfo; u32 frag_consumed_bytes; struct bpf_prog *prog; struct xdp_buff xdp; struct sk_buff *skb; + dma_addr_t addr; u32 truesize; void *va; - va = page_address(di->page) + wi->offset; + va = page_address(au->page) + wi->offset; frag_consumed_bytes = min_t(u32, frag_info->frag_size, cqe_bcnt); - dma_sync_single_range_for_cpu(rq->pdev, di->addr, wi->offset, + addr = page_pool_get_dma_addr(au->page); + dma_sync_single_range_for_cpu(rq->pdev, addr, wi->offset, rq->buff.frame0_sz, DMA_FROM_DEVICE); net_prefetchw(va); /* xdp_frame data area */ net_prefetch(va + rx_headroom); @@ -1599,11 +1602,12 @@ mlx5e_skb_from_cqe_nonlinear(struct mlx5e_rq *rq, struct mlx5e_wqe_frag_info *wi while (cqe_bcnt) { skb_frag_t *frag; - di = wi->di; + au = wi->au; frag_consumed_bytes = min_t(u32, frag_info->frag_size, cqe_bcnt); - dma_sync_single_for_cpu(rq->pdev, di->addr + wi->offset, + addr = page_pool_get_dma_addr(au->page); + dma_sync_single_for_cpu(rq->pdev, addr + wi->offset, frag_consumed_bytes, DMA_FROM_DEVICE); if (!xdp_buff_has_frags(&xdp)) { @@ -1616,11 +1620,11 @@ mlx5e_skb_from_cqe_nonlinear(struct mlx5e_rq *rq, struct mlx5e_wqe_frag_info *wi } frag = &sinfo->frags[sinfo->nr_frags++]; - __skb_frag_set_page(frag, di->page); + __skb_frag_set_page(frag, au->page); skb_frag_off_set(frag, wi->offset); skb_frag_size_set(frag, frag_consumed_bytes); - if (page_is_pfmemalloc(di->page)) + if (page_is_pfmemalloc(au->page)) xdp_buff_set_frag_pfmemalloc(&xdp); sinfo->xdp_frags_size += frag_consumed_bytes; @@ -1631,10 +1635,10 @@ mlx5e_skb_from_cqe_nonlinear(struct mlx5e_rq *rq, struct mlx5e_wqe_frag_info *wi wi++; } - di = head_wi->di; + au = head_wi->au; prog = rcu_dereference(rq->xdp_prog); - if (prog && mlx5e_xdp_handle(rq, di->page, prog, &xdp)) { + if (prog && mlx5e_xdp_handle(rq, au->page, prog, &xdp)) { if (test_bit(MLX5E_RQ_FLAG_XDP_XMIT, rq->flags)) { int i; @@ -1651,7 +1655,7 @@ mlx5e_skb_from_cqe_nonlinear(struct mlx5e_rq *rq, struct mlx5e_wqe_frag_info *wi if (unlikely(!skb)) return NULL; - page_ref_inc(di->page); + page_ref_inc(au->page); if (unlikely(xdp_buff_has_frags(&xdp))) { int i; @@ -1706,9 +1710,10 @@ static void mlx5e_handle_rx_cqe(struct mlx5e_rq *rq, struct mlx5_cqe64 *cqe) goto free_wqe; } - skb = INDIRECT_CALL_2(rq->wqe.skb_from_cqe, + skb = INDIRECT_CALL_3(rq->wqe.skb_from_cqe, mlx5e_skb_from_cqe_linear, mlx5e_skb_from_cqe_nonlinear, + mlx5e_xsk_skb_from_cqe_linear, rq, wi, cqe_bcnt); if (!skb) { /* probably for XDP */ @@ -1791,11 +1796,11 @@ static void mlx5e_handle_rx_cqe_mpwrq_rep(struct mlx5e_rq *rq, struct mlx5_cqe64 { u16 cstrides = mpwrq_get_cqe_consumed_strides(cqe); u16 wqe_id = be16_to_cpu(cqe->wqe_id); - struct mlx5e_mpw_info *wi = &rq->mpwqe.info[wqe_id]; + struct mlx5e_mpw_info *wi = mlx5e_get_mpw_info(rq, wqe_id); u16 stride_ix = mpwrq_get_cqe_stride_index(cqe); u32 wqe_offset = stride_ix << rq->mpwqe.log_stride_sz; - u32 head_offset = wqe_offset & (PAGE_SIZE - 1); - u32 page_idx = wqe_offset >> PAGE_SHIFT; + u32 head_offset = wqe_offset & ((1 << rq->mpwqe.page_shift) - 1); + u32 page_idx = wqe_offset >> rq->mpwqe.page_shift; struct mlx5e_rx_wqe_ll *wqe; struct mlx5_wq_ll *wq; struct sk_buff *skb; @@ -1846,12 +1851,13 @@ const struct mlx5e_rx_handlers mlx5e_rx_handlers_rep = { #endif static void -mlx5e_fill_skb_data(struct sk_buff *skb, struct mlx5e_rq *rq, struct mlx5e_dma_info *di, - u32 data_bcnt, u32 data_offset) +mlx5e_fill_skb_data(struct sk_buff *skb, struct mlx5e_rq *rq, + union mlx5e_alloc_unit *au, u32 data_bcnt, u32 data_offset) { net_prefetchw(skb->data); while (data_bcnt) { + /* Non-linear mode, hence non-XSK, which always uses PAGE_SIZE. */ u32 pg_consumed_bytes = min_t(u32, PAGE_SIZE - data_offset, data_bcnt); unsigned int truesize; @@ -1860,12 +1866,12 @@ mlx5e_fill_skb_data(struct sk_buff *skb, struct mlx5e_rq *rq, struct mlx5e_dma_i else truesize = ALIGN(pg_consumed_bytes, BIT(rq->mpwqe.log_stride_sz)); - mlx5e_add_skb_frag(rq, skb, di, data_offset, + mlx5e_add_skb_frag(rq, skb, au, data_offset, pg_consumed_bytes, truesize); data_bcnt -= pg_consumed_bytes; data_offset = 0; - di++; + au++; } } @@ -1873,12 +1879,13 @@ static struct sk_buff * mlx5e_skb_from_cqe_mpwrq_nonlinear(struct mlx5e_rq *rq, struct mlx5e_mpw_info *wi, u16 cqe_bcnt, u32 head_offset, u32 page_idx) { + union mlx5e_alloc_unit *au = &wi->alloc_units[page_idx]; u16 headlen = min_t(u16, MLX5E_RX_MAX_HEAD, cqe_bcnt); - struct mlx5e_dma_info *di = &wi->umr.dma_info[page_idx]; u32 frag_offset = head_offset + headlen; u32 byte_cnt = cqe_bcnt - headlen; - struct mlx5e_dma_info *head_di = di; + union mlx5e_alloc_unit *head_au = au; struct sk_buff *skb; + dma_addr_t addr; skb = napi_alloc_skb(rq->cq.napi, ALIGN(MLX5E_RX_MAX_HEAD, sizeof(long))); @@ -1889,14 +1896,17 @@ mlx5e_skb_from_cqe_mpwrq_nonlinear(struct mlx5e_rq *rq, struct mlx5e_mpw_info *w net_prefetchw(skb->data); + /* Non-linear mode, hence non-XSK, which always uses PAGE_SIZE. */ if (unlikely(frag_offset >= PAGE_SIZE)) { - di++; + au++; frag_offset -= PAGE_SIZE; } - mlx5e_fill_skb_data(skb, rq, di, byte_cnt, frag_offset); + mlx5e_fill_skb_data(skb, rq, au, byte_cnt, frag_offset); /* copy header */ - mlx5e_copy_skb_header(rq->pdev, skb, head_di, head_offset, head_offset, headlen); + addr = page_pool_get_dma_addr(head_au->page); + mlx5e_copy_skb_header(rq->pdev, skb, head_au->page, addr, + head_offset, head_offset, headlen); /* skb linear part was allocated with headlen and aligned to long */ skb->tail += headlen; skb->len += headlen; @@ -1908,12 +1918,13 @@ static struct sk_buff * mlx5e_skb_from_cqe_mpwrq_linear(struct mlx5e_rq *rq, struct mlx5e_mpw_info *wi, u16 cqe_bcnt, u32 head_offset, u32 page_idx) { - struct mlx5e_dma_info *di = &wi->umr.dma_info[page_idx]; + union mlx5e_alloc_unit *au = &wi->alloc_units[page_idx]; u16 rx_headroom = rq->buff.headroom; struct bpf_prog *prog; struct sk_buff *skb; u32 metasize = 0; void *va, *data; + dma_addr_t addr; u32 frag_size; /* Check packet size. Note LRO doesn't use linear SKB */ @@ -1922,11 +1933,12 @@ mlx5e_skb_from_cqe_mpwrq_linear(struct mlx5e_rq *rq, struct mlx5e_mpw_info *wi, return NULL; } - va = page_address(di->page) + head_offset; + va = page_address(au->page) + head_offset; data = va + rx_headroom; frag_size = MLX5_SKB_FRAG_SZ(rx_headroom + cqe_bcnt); - dma_sync_single_range_for_cpu(rq->pdev, di->addr, head_offset, + addr = page_pool_get_dma_addr(au->page); + dma_sync_single_range_for_cpu(rq->pdev, addr, head_offset, frag_size, DMA_FROM_DEVICE); net_prefetch(data); @@ -1936,7 +1948,7 @@ mlx5e_skb_from_cqe_mpwrq_linear(struct mlx5e_rq *rq, struct mlx5e_mpw_info *wi, net_prefetchw(va); /* xdp_frame data area */ mlx5e_fill_xdp_buff(rq, va, rx_headroom, cqe_bcnt, &xdp); - if (mlx5e_xdp_handle(rq, di->page, prog, &xdp)) { + if (mlx5e_xdp_handle(rq, au->page, prog, &xdp)) { if (__test_and_clear_bit(MLX5E_RQ_FLAG_XDP_XMIT, rq->flags)) __set_bit(page_idx, wi->xdp_xmit_bitmap); /* non-atomic */ return NULL; /* page/packet was consumed by XDP */ @@ -1952,7 +1964,7 @@ mlx5e_skb_from_cqe_mpwrq_linear(struct mlx5e_rq *rq, struct mlx5e_mpw_info *wi, return NULL; /* queue up for recycling/reuse */ - page_ref_inc(di->page); + page_ref_inc(au->page); return skb; } @@ -1997,7 +2009,7 @@ mlx5e_skb_from_cqe_shampo(struct mlx5e_rq *rq, struct mlx5e_mpw_info *wi, } prefetchw(skb->data); - mlx5e_copy_skb_header(rq->pdev, skb, head, + mlx5e_copy_skb_header(rq->pdev, skb, head->page, head->addr, head_offset + rx_headroom, rx_headroom, head_size); /* skb linear part was allocated with headlen and aligned to long */ @@ -2049,7 +2061,7 @@ mlx5e_free_rx_shampo_hd_entry(struct mlx5e_rq *rq, u16 header_index) if (((header_index + 1) & (MLX5E_SHAMPO_WQ_HEADER_PER_PAGE - 1)) == 0) { shampo->info[header_index].addr = ALIGN_DOWN(addr, PAGE_SIZE); - mlx5e_page_release(rq, &shampo->info[header_index], true); + mlx5e_page_release_dynamic(rq, shampo->info[header_index].page, true); } bitmap_clear(shampo->bitmap, header_index, 1); } @@ -2070,11 +2082,11 @@ static void mlx5e_handle_rx_cqe_mpwrq_shampo(struct mlx5e_rq *rq, struct mlx5_cq bool match = cqe->shampo.match; struct mlx5e_rq_stats *stats = rq->stats; struct mlx5e_rx_wqe_ll *wqe; - struct mlx5e_dma_info *di; + union mlx5e_alloc_unit *au; struct mlx5e_mpw_info *wi; struct mlx5_wq_ll *wq; - wi = &rq->mpwqe.info[wqe_id]; + wi = mlx5e_get_mpw_info(rq, wqe_id); wi->consumed_strides += cstrides; if (unlikely(MLX5E_RX_ERR_CQE(cqe))) { @@ -2120,8 +2132,8 @@ static void mlx5e_handle_rx_cqe_mpwrq_shampo(struct mlx5e_rq *rq, struct mlx5_cq } if (likely(head_size)) { - di = &wi->umr.dma_info[page_idx]; - mlx5e_fill_skb_data(*skb, rq, di, data_bcnt, data_offset); + au = &wi->alloc_units[page_idx]; + mlx5e_fill_skb_data(*skb, rq, au, data_bcnt, data_offset); } mlx5e_shampo_complete_rx_cqe(rq, cqe, cqe_bcnt, *skb); @@ -2143,11 +2155,11 @@ static void mlx5e_handle_rx_cqe_mpwrq(struct mlx5e_rq *rq, struct mlx5_cqe64 *cq { u16 cstrides = mpwrq_get_cqe_consumed_strides(cqe); u16 wqe_id = be16_to_cpu(cqe->wqe_id); - struct mlx5e_mpw_info *wi = &rq->mpwqe.info[wqe_id]; + struct mlx5e_mpw_info *wi = mlx5e_get_mpw_info(rq, wqe_id); u16 stride_ix = mpwrq_get_cqe_stride_index(cqe); u32 wqe_offset = stride_ix << rq->mpwqe.log_stride_sz; - u32 head_offset = wqe_offset & (PAGE_SIZE - 1); - u32 page_idx = wqe_offset >> PAGE_SHIFT; + u32 head_offset = wqe_offset & ((1 << rq->mpwqe.page_shift) - 1); + u32 page_idx = wqe_offset >> rq->mpwqe.page_shift; struct mlx5e_rx_wqe_ll *wqe; struct mlx5_wq_ll *wq; struct sk_buff *skb; @@ -2170,9 +2182,10 @@ static void mlx5e_handle_rx_cqe_mpwrq(struct mlx5e_rq *rq, struct mlx5_cqe64 *cq cqe_bcnt = mpwrq_get_cqe_byte_cnt(cqe); - skb = INDIRECT_CALL_2(rq->mpwqe.skb_from_cqe_mpwrq, + skb = INDIRECT_CALL_3(rq->mpwqe.skb_from_cqe_mpwrq, mlx5e_skb_from_cqe_mpwrq_linear, mlx5e_skb_from_cqe_mpwrq_nonlinear, + mlx5e_xsk_skb_from_cqe_mpwrq_linear, rq, wi, cqe_bcnt, head_offset, page_idx); if (!skb) goto mpwrq_cqe_out; @@ -2417,7 +2430,7 @@ int mlx5e_rq_set_handlers(struct mlx5e_rq *rq, struct mlx5e_params *params, bool default: /* MLX5_WQ_TYPE_CYCLIC */ rq->wqe.skb_from_cqe = xsk ? mlx5e_xsk_skb_from_cqe_linear : - mlx5e_rx_is_linear_skb(params, NULL) ? + mlx5e_rx_is_linear_skb(mdev, params, NULL) ? mlx5e_skb_from_cqe_linear : mlx5e_skb_from_cqe_nonlinear; rq->post_wqes = mlx5e_post_rx_wqes; @@ -2471,7 +2484,7 @@ free_wqe: void mlx5e_rq_set_trap_handlers(struct mlx5e_rq *rq, struct mlx5e_params *params) { - rq->wqe.skb_from_cqe = mlx5e_rx_is_linear_skb(params, NULL) ? + rq->wqe.skb_from_cqe = mlx5e_rx_is_linear_skb(rq->mdev, params, NULL) ? mlx5e_skb_from_cqe_linear : mlx5e_skb_from_cqe_nonlinear; rq->post_wqes = mlx5e_post_rx_wqes; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_stats.c b/drivers/net/ethernet/mellanox/mlx5/core/en_stats.c index 7409829d1201..03c1841970f1 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_stats.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_stats.c @@ -641,17 +641,26 @@ static const struct counter_desc vnic_env_stats_dev_oob_desc[] = { VNIC_ENV_OFF(vport_env.internal_rq_out_of_buffer) }, }; +static const struct counter_desc vnic_env_stats_drop_desc[] = { + { "rx_oversize_pkts_buffer", + VNIC_ENV_OFF(vport_env.eth_wqe_too_small) }, +}; + #define NUM_VNIC_ENV_STEER_COUNTERS(dev) \ (MLX5_CAP_GEN(dev, nic_receive_steering_discard) ? \ ARRAY_SIZE(vnic_env_stats_steer_desc) : 0) #define NUM_VNIC_ENV_DEV_OOB_COUNTERS(dev) \ (MLX5_CAP_GEN(dev, vnic_env_int_rq_oob) ? \ ARRAY_SIZE(vnic_env_stats_dev_oob_desc) : 0) +#define NUM_VNIC_ENV_DROP_COUNTERS(dev) \ + (MLX5_CAP_GEN(dev, eth_wqe_too_small) ? \ + ARRAY_SIZE(vnic_env_stats_drop_desc) : 0) static MLX5E_DECLARE_STATS_GRP_OP_NUM_STATS(vnic_env) { return NUM_VNIC_ENV_STEER_COUNTERS(priv->mdev) + - NUM_VNIC_ENV_DEV_OOB_COUNTERS(priv->mdev); + NUM_VNIC_ENV_DEV_OOB_COUNTERS(priv->mdev) + + NUM_VNIC_ENV_DROP_COUNTERS(priv->mdev); } static MLX5E_DECLARE_STATS_GRP_OP_FILL_STRS(vnic_env) @@ -665,6 +674,11 @@ static MLX5E_DECLARE_STATS_GRP_OP_FILL_STRS(vnic_env) for (i = 0; i < NUM_VNIC_ENV_DEV_OOB_COUNTERS(priv->mdev); i++) strcpy(data + (idx++) * ETH_GSTRING_LEN, vnic_env_stats_dev_oob_desc[i].format); + + for (i = 0; i < NUM_VNIC_ENV_DROP_COUNTERS(priv->mdev); i++) + strcpy(data + (idx++) * ETH_GSTRING_LEN, + vnic_env_stats_drop_desc[i].format); + return idx; } @@ -679,6 +693,11 @@ static MLX5E_DECLARE_STATS_GRP_OP_FILL_STATS(vnic_env) for (i = 0; i < NUM_VNIC_ENV_DEV_OOB_COUNTERS(priv->mdev); i++) data[idx++] = MLX5E_READ_CTR32_BE(priv->stats.vnic.query_vnic_env_out, vnic_env_stats_dev_oob_desc, i); + + for (i = 0; i < NUM_VNIC_ENV_DROP_COUNTERS(priv->mdev); i++) + data[idx++] = MLX5E_READ_CTR32_BE(priv->stats.vnic.query_vnic_env_out, + vnic_env_stats_drop_desc, i); + return idx; } @@ -2451,6 +2470,9 @@ mlx5e_stats_grp_t mlx5e_nic_stats_grps[] = { &MLX5E_STATS_GRP(per_port_buff_congest), &MLX5E_STATS_GRP(ptp), &MLX5E_STATS_GRP(qos), +#ifdef CONFIG_MLX5_EN_MACSEC + &MLX5E_STATS_GRP(macsec_hw), +#endif }; unsigned int mlx5e_nic_stats_grps_num(struct mlx5e_priv *priv) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_stats.h b/drivers/net/ethernet/mellanox/mlx5/core/en_stats.h index ed4fc940e4ef..9f781085be47 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_stats.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_stats.h @@ -273,6 +273,10 @@ struct mlx5e_qcounter_stats { u32 rx_if_down_packets; }; +#define VNIC_ENV_GET(vnic_env_stats, c) \ + MLX5_GET(query_vnic_env_out, (vnic_env_stats)->query_vnic_env_out, \ + vport_env.c) + struct mlx5e_vnic_env_stats { __be64 query_vnic_env_out[MLX5_ST_SZ_QW(query_vnic_env_out)]; }; @@ -486,5 +490,6 @@ extern MLX5E_DECLARE_STATS_GRP(channels); extern MLX5E_DECLARE_STATS_GRP(per_port_buff_congest); extern MLX5E_DECLARE_STATS_GRP(ipsec_sw); extern MLX5E_DECLARE_STATS_GRP(ptp); +extern MLX5E_DECLARE_STATS_GRP(macsec_hw); #endif /* __MLX5_EN_STATS_H__ */ diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c index f154bda668ad..70a7a61f9708 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c @@ -311,6 +311,7 @@ mlx5e_get_flow_meters(struct mlx5_core_dev *dev) static struct mlx5_tc_ct_priv * get_ct_priv(struct mlx5e_priv *priv) { + struct mlx5e_tc_table *tc = mlx5e_fs_get_tc(priv->fs); struct mlx5_eswitch *esw = priv->mdev->priv.eswitch; struct mlx5_rep_uplink_priv *uplink_priv; struct mlx5e_rep_priv *uplink_rpriv; @@ -322,7 +323,7 @@ get_ct_priv(struct mlx5e_priv *priv) return uplink_priv->ct_priv; } - return priv->fs->tc->ct; + return tc->ct; } static struct mlx5e_tc_psample * @@ -345,6 +346,7 @@ get_sample_priv(struct mlx5e_priv *priv) static struct mlx5e_post_act * get_post_action(struct mlx5e_priv *priv) { + struct mlx5e_tc_table *tc = mlx5e_fs_get_tc(priv->fs); struct mlx5_eswitch *esw = priv->mdev->priv.eswitch; struct mlx5_rep_uplink_priv *uplink_priv; struct mlx5e_rep_priv *uplink_rpriv; @@ -356,7 +358,7 @@ get_post_action(struct mlx5e_priv *priv) return uplink_priv->post_act; } - return priv->fs->tc->post_act; + return tc->post_act; } struct mlx5_flow_handle * @@ -607,11 +609,12 @@ int mlx5e_get_flow_namespace(struct mlx5e_tc_flow *flow) static struct mod_hdr_tbl * get_mod_hdr_table(struct mlx5e_priv *priv, struct mlx5e_tc_flow *flow) { + struct mlx5e_tc_table *tc = mlx5e_fs_get_tc(priv->fs); struct mlx5_eswitch *esw = priv->mdev->priv.eswitch; return mlx5e_get_flow_namespace(flow) == MLX5_FLOW_NAMESPACE_FDB ? &esw->offloads.mod_hdr : - &priv->fs->tc->mod_hdr; + &tc->mod_hdr; } static int mlx5e_attach_mod_hdr(struct mlx5e_priv *priv, @@ -810,6 +813,7 @@ static int mlx5e_hairpin_rss_init(struct mlx5e_hairpin *hp) { struct mlx5e_priv *priv = hp->func_priv; struct ttc_params ttc_params; + struct mlx5_ttc_table *ttc; int err; err = mlx5e_hairpin_create_indirect_rqt(hp); @@ -827,9 +831,10 @@ static int mlx5e_hairpin_rss_init(struct mlx5e_hairpin *hp) goto err_create_ttc_table; } + ttc = mlx5e_fs_get_ttc(priv->fs, false); netdev_dbg(priv->netdev, "add hairpin: using %d channels rss ttc table id %x\n", hp->num_channels, - mlx5_get_ttc_flow_table(priv->fs->ttc)->id); + mlx5_get_ttc_flow_table(ttc)->id); return 0; @@ -916,10 +921,11 @@ static inline u32 hash_hairpin_info(u16 peer_vhca_id, u8 prio) static struct mlx5e_hairpin_entry *mlx5e_hairpin_get(struct mlx5e_priv *priv, u16 peer_vhca_id, u8 prio) { + struct mlx5e_tc_table *tc = mlx5e_fs_get_tc(priv->fs); struct mlx5e_hairpin_entry *hpe; u32 hash_key = hash_hairpin_info(peer_vhca_id, prio); - hash_for_each_possible(priv->fs->tc->hairpin_tbl, hpe, + hash_for_each_possible(tc->hairpin_tbl, hpe, hairpin_hlist, hash_key) { if (hpe->peer_vhca_id == peer_vhca_id && hpe->prio == prio) { refcount_inc(&hpe->refcnt); @@ -933,11 +939,12 @@ static struct mlx5e_hairpin_entry *mlx5e_hairpin_get(struct mlx5e_priv *priv, static void mlx5e_hairpin_put(struct mlx5e_priv *priv, struct mlx5e_hairpin_entry *hpe) { + struct mlx5e_tc_table *tc = mlx5e_fs_get_tc(priv->fs); /* no more hairpin flows for us, release the hairpin pair */ - if (!refcount_dec_and_mutex_lock(&hpe->refcnt, &priv->fs->tc->hairpin_tbl_lock)) + if (!refcount_dec_and_mutex_lock(&hpe->refcnt, &tc->hairpin_tbl_lock)) return; hash_del(&hpe->hairpin_hlist); - mutex_unlock(&priv->fs->tc->hairpin_tbl_lock); + mutex_unlock(&tc->hairpin_tbl_lock); if (!IS_ERR_OR_NULL(hpe->hp)) { netdev_dbg(priv->netdev, "del hairpin: peer %s\n", @@ -993,6 +1000,7 @@ static int mlx5e_hairpin_flow_add(struct mlx5e_priv *priv, struct mlx5e_tc_flow_parse_attr *parse_attr, struct netlink_ext_ack *extack) { + struct mlx5e_tc_table *tc = mlx5e_fs_get_tc(priv->fs); int peer_ifindex = parse_attr->mirred_ifindex[0]; struct mlx5_hairpin_params params; struct mlx5_core_dev *peer_mdev; @@ -1021,10 +1029,10 @@ static int mlx5e_hairpin_flow_add(struct mlx5e_priv *priv, if (err) return err; - mutex_lock(&priv->fs->tc->hairpin_tbl_lock); + mutex_lock(&tc->hairpin_tbl_lock); hpe = mlx5e_hairpin_get(priv, peer_id, match_prio); if (hpe) { - mutex_unlock(&priv->fs->tc->hairpin_tbl_lock); + mutex_unlock(&tc->hairpin_tbl_lock); wait_for_completion(&hpe->res_ready); if (IS_ERR(hpe->hp)) { @@ -1036,7 +1044,7 @@ static int mlx5e_hairpin_flow_add(struct mlx5e_priv *priv, hpe = kzalloc(sizeof(*hpe), GFP_KERNEL); if (!hpe) { - mutex_unlock(&priv->fs->tc->hairpin_tbl_lock); + mutex_unlock(&tc->hairpin_tbl_lock); return -ENOMEM; } @@ -1048,9 +1056,9 @@ static int mlx5e_hairpin_flow_add(struct mlx5e_priv *priv, refcount_set(&hpe->refcnt, 1); init_completion(&hpe->res_ready); - hash_add(priv->fs->tc->hairpin_tbl, &hpe->hairpin_hlist, + hash_add(tc->hairpin_tbl, &hpe->hairpin_hlist, hash_hairpin_info(peer_id, match_prio)); - mutex_unlock(&priv->fs->tc->hairpin_tbl_lock); + mutex_unlock(&tc->hairpin_tbl_lock); params.log_data_size = 16; params.log_data_size = min_t(u8, params.log_data_size, @@ -1126,8 +1134,9 @@ mlx5e_add_offloaded_nic_rule(struct mlx5e_priv *priv, struct mlx5_flow_attr *attr) { struct mlx5_flow_context *flow_context = &spec->flow_context; + struct mlx5e_vlan_table *vlan = mlx5e_fs_get_vlan(priv->fs); + struct mlx5e_tc_table *tc = mlx5e_fs_get_tc(priv->fs); struct mlx5_nic_flow_attr *nic_attr = attr->nic_attr; - struct mlx5e_tc_table *tc = priv->fs->tc; struct mlx5_flow_destination dest[2] = {}; struct mlx5_fs_chains *nic_chains; struct mlx5_flow_act flow_act = { @@ -1163,7 +1172,7 @@ mlx5e_add_offloaded_nic_rule(struct mlx5e_priv *priv, if (IS_ERR(dest[dest_ix].ft)) return ERR_CAST(dest[dest_ix].ft); } else { - dest[dest_ix].ft = mlx5e_vlan_get_flowtable(priv->fs->vlan); + dest[dest_ix].ft = mlx5e_vlan_get_flowtable(vlan); } dest_ix++; } @@ -1191,7 +1200,7 @@ mlx5e_add_offloaded_nic_rule(struct mlx5e_priv *priv, mutex_unlock(&tc->t_lock); netdev_err(priv->netdev, "Failed to create tc offload table\n"); - rule = ERR_CAST(priv->fs->tc->t); + rule = ERR_CAST(tc->t); goto err_ft_get; } } @@ -1293,8 +1302,10 @@ void mlx5e_del_offloaded_nic_rule(struct mlx5e_priv *priv, struct mlx5_flow_handle *rule, struct mlx5_flow_attr *attr) { - struct mlx5_fs_chains *nic_chains = mlx5e_nic_chains(priv->fs->tc); + struct mlx5e_tc_table *tc = mlx5e_fs_get_tc(priv->fs); + struct mlx5_fs_chains *nic_chains; + nic_chains = mlx5e_nic_chains(tc); mlx5_del_flow_rules(rule); if (attr->chain || attr->prio) @@ -1309,8 +1320,8 @@ void mlx5e_del_offloaded_nic_rule(struct mlx5e_priv *priv, static void mlx5e_tc_del_nic_flow(struct mlx5e_priv *priv, struct mlx5e_tc_flow *flow) { + struct mlx5e_tc_table *tc = mlx5e_fs_get_tc(priv->fs); struct mlx5_flow_attr *attr = flow->attr; - struct mlx5e_tc_table *tc = priv->fs->tc; flow_flag_clear(flow, OFFLOADED); @@ -1322,13 +1333,13 @@ static void mlx5e_tc_del_nic_flow(struct mlx5e_priv *priv, /* Remove root table if no rules are left to avoid * extra steering hops. */ - mutex_lock(&priv->fs->tc->t_lock); + mutex_lock(&tc->t_lock); if (!mlx5e_tc_num_filters(priv, MLX5_TC_FLAG(NIC_OFFLOAD)) && !IS_ERR_OR_NULL(tc->t)) { mlx5_chains_put_table(mlx5e_nic_chains(tc), 0, 1, MLX5E_TC_FT_LEVEL); - priv->fs->tc->t = NULL; + tc->t = NULL; } - mutex_unlock(&priv->fs->tc->t_lock); + mutex_unlock(&tc->t_lock); if (attr->action & MLX5_FLOW_CONTEXT_ACTION_MOD_HDR) mlx5e_detach_mod_hdr(priv, flow); @@ -1494,8 +1505,11 @@ bool mlx5e_tc_is_vf_tunnel(struct net_device *out_dev, struct net_device *route_ route_priv = netdev_priv(route_dev); route_mdev = route_priv->mdev; - if (out_mdev->coredev_type != MLX5_COREDEV_PF || - route_mdev->coredev_type != MLX5_COREDEV_VF) + if (out_mdev->coredev_type != MLX5_COREDEV_PF) + return false; + + if (route_mdev->coredev_type != MLX5_COREDEV_VF && + route_mdev->coredev_type != MLX5_COREDEV_SF) return false; return mlx5e_same_hw_devs(out_priv, route_priv); @@ -4058,13 +4072,14 @@ static const struct rhashtable_params tc_ht_params = { static struct rhashtable *get_tc_ht(struct mlx5e_priv *priv, unsigned long flags) { + struct mlx5e_tc_table *tc = mlx5e_fs_get_tc(priv->fs); struct mlx5e_rep_priv *rpriv; if (flags & MLX5_TC_FLAG(ESW_OFFLOAD)) { rpriv = priv->ppriv; return &rpriv->tc_ht; } else /* NIC offload */ - return &priv->fs->tc->ht; + return &tc->ht; } static bool is_peer_flow_needed(struct mlx5e_tc_flow *flow) @@ -4448,7 +4463,7 @@ int mlx5e_configure_flower(struct net_device *dev, struct mlx5e_priv *priv, int err = 0; if (!mlx5_esw_hold(priv->mdev)) - return -EAGAIN; + return -EBUSY; mlx5_esw_get(priv->mdev); @@ -4772,6 +4787,7 @@ void mlx5e_tc_stats_matchall(struct mlx5e_priv *priv, static void mlx5e_tc_hairpin_update_dead_peer(struct mlx5e_priv *priv, struct mlx5e_priv *peer_priv) { + struct mlx5e_tc_table *tc = mlx5e_fs_get_tc(priv->fs); struct mlx5_core_dev *peer_mdev = peer_priv->mdev; struct mlx5e_hairpin_entry *hpe, *tmp; LIST_HEAD(init_wait_list); @@ -4783,11 +4799,11 @@ static void mlx5e_tc_hairpin_update_dead_peer(struct mlx5e_priv *priv, peer_vhca_id = MLX5_CAP_GEN(peer_mdev, vhca_id); - mutex_lock(&priv->fs->tc->hairpin_tbl_lock); - hash_for_each(priv->fs->tc->hairpin_tbl, bkt, hpe, hairpin_hlist) + mutex_lock(&tc->hairpin_tbl_lock); + hash_for_each(tc->hairpin_tbl, bkt, hpe, hairpin_hlist) if (refcount_inc_not_zero(&hpe->refcnt)) list_add(&hpe->dead_peer_wait_list, &init_wait_list); - mutex_unlock(&priv->fs->tc->hairpin_tbl_lock); + mutex_unlock(&tc->hairpin_tbl_lock); list_for_each_entry_safe(hpe, tmp, &init_wait_list, dead_peer_wait_list) { wait_for_completion(&hpe->res_ready); @@ -4841,7 +4857,8 @@ static int mlx5e_tc_nic_get_ft_size(struct mlx5_core_dev *dev) static int mlx5e_tc_nic_create_miss_table(struct mlx5e_priv *priv) { - struct mlx5_flow_table **ft = &priv->fs->tc->miss_t; + struct mlx5e_tc_table *tc = mlx5e_fs_get_tc(priv->fs); + struct mlx5_flow_table **ft = &tc->miss_t; struct mlx5_flow_table_attr ft_attr = {}; struct mlx5_flow_namespace *ns; int err = 0; @@ -4863,12 +4880,14 @@ static int mlx5e_tc_nic_create_miss_table(struct mlx5e_priv *priv) static void mlx5e_tc_nic_destroy_miss_table(struct mlx5e_priv *priv) { - mlx5_destroy_flow_table(priv->fs->tc->miss_t); + struct mlx5e_tc_table *tc = mlx5e_fs_get_tc(priv->fs); + + mlx5_destroy_flow_table(tc->miss_t); } int mlx5e_tc_nic_init(struct mlx5e_priv *priv) { - struct mlx5e_tc_table *tc = priv->fs->tc; + struct mlx5e_tc_table *tc = mlx5e_fs_get_tc(priv->fs); struct mlx5_core_dev *dev = priv->mdev; struct mapping_ctx *chains_mapping; struct mlx5_chains_attr attr = {}; @@ -4909,7 +4928,7 @@ int mlx5e_tc_nic_init(struct mlx5e_priv *priv) attr.ns = MLX5_FLOW_NAMESPACE_KERNEL; attr.max_ft_sz = mlx5e_tc_nic_get_ft_size(dev); attr.max_grp_num = MLX5E_TC_TABLE_NUM_GROUPS; - attr.default_ft = priv->fs->tc->miss_t; + attr.default_ft = tc->miss_t; attr.mapping = chains_mapping; tc->chains = mlx5_chains_create(dev, &attr); @@ -4958,7 +4977,7 @@ static void _mlx5e_tc_del_flow(void *ptr, void *arg) void mlx5e_tc_nic_cleanup(struct mlx5e_priv *priv) { - struct mlx5e_tc_table *tc = priv->fs->tc; + struct mlx5e_tc_table *tc = mlx5e_fs_get_tc(priv->fs); if (tc->netdevice_nb.notifier_call) unregister_netdevice_notifier_dev_net(priv->netdev, @@ -5163,13 +5182,13 @@ bool mlx5e_tc_update_skb(struct mlx5_cqe64 *cqe, #if IS_ENABLED(CONFIG_NET_TC_SKB_EXT) u32 chain = 0, chain_tag, reg_b, zone_restore_id; struct mlx5e_priv *priv = netdev_priv(skb->dev); - struct mlx5e_tc_table *tc = priv->fs->tc; struct mlx5_mapped_obj mapped_obj; struct tc_skb_ext *tc_skb_ext; + struct mlx5e_tc_table *tc; int err; reg_b = be32_to_cpu(cqe->ft_metadata); - + tc = mlx5e_fs_get_tc(priv->fs); chain_tag = reg_b & MLX5E_TC_TABLE_CHAIN_TAG_MASK; err = mapping_find(tc->mapping, chain_tag, &mapped_obj); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.h b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.h index 6ce1ab6b86b7..48241317a535 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.h @@ -54,6 +54,7 @@ ESW_FLOW_ATTR_SZ :\ NIC_FLOW_ATTR_SZ) +struct mlx5_fs_chains *mlx5e_nic_chains(struct mlx5e_tc_table *tc); int mlx5e_tc_num_filters(struct mlx5e_priv *priv, unsigned long flags); struct mlx5e_tc_update_priv { diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c index 27f791feb517..bf2232a2a836 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c @@ -39,6 +39,7 @@ #include "ipoib/ipoib.h" #include "en_accel/en_accel.h" #include "en_accel/ipsec_rxtx.h" +#include "en_accel/macsec.h" #include "en/ptp.h" #include <net/ipv6.h> @@ -485,7 +486,7 @@ err_drop: static bool mlx5e_tx_skb_supports_mpwqe(struct sk_buff *skb, struct mlx5e_tx_attr *attr) { return !skb_is_nonlinear(skb) && !skb_vlan_tag_present(skb) && !attr->ihs && - !attr->insz; + !attr->insz && !mlx5e_macsec_skb_is_offload(skb); } static bool mlx5e_tx_mpwqe_same_eseg(struct mlx5e_txqsq *sq, struct mlx5_wqe_eth_seg *eseg) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c index 833be29170a1..9a458a5d9853 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c @@ -31,6 +31,7 @@ */ #include <linux/irq.h> +#include <net/xdp_sock_drv.h> #include "en.h" #include "en/txrx.h" #include "en/xdp.h" @@ -86,26 +87,36 @@ void mlx5e_trigger_irq(struct mlx5e_icosq *sq) static bool mlx5e_napi_xsk_post(struct mlx5e_xdpsq *xsksq, struct mlx5e_rq *xskrq) { + bool need_wakeup = xsk_uses_need_wakeup(xskrq->xsk_pool); bool busy_xsk = false, xsk_rx_alloc_err; - /* Handle the race between the application querying need_wakeup and the - * driver setting it: - * 1. Update need_wakeup both before and after the TX. If it goes to - * "yes", it can only happen with the first update. - * 2. If the application queried need_wakeup before we set it, the - * packets will be transmitted anyway, even w/o a wakeup. - * 3. Give a chance to clear need_wakeup after new packets were queued - * for TX. + /* If SQ is empty, there are no TX completions to trigger NAPI, so set + * need_wakeup. Do it before queuing packets for TX to avoid race + * condition with userspace. */ - mlx5e_xsk_update_tx_wakeup(xsksq); + if (need_wakeup && xsksq->pc == xsksq->cc) + xsk_set_tx_need_wakeup(xsksq->xsk_pool); busy_xsk |= mlx5e_xsk_tx(xsksq, MLX5E_TX_XSK_POLL_BUDGET); - mlx5e_xsk_update_tx_wakeup(xsksq); + /* If we queued some packets for TX, no need for wakeup anymore. */ + if (need_wakeup && xsksq->pc != xsksq->cc) + xsk_clear_tx_need_wakeup(xsksq->xsk_pool); + /* If WQ is empty, RX won't trigger NAPI, so set need_wakeup. Do it + * before refilling to avoid race condition with userspace. + */ + if (need_wakeup && !mlx5e_rqwq_get_cur_sz(xskrq)) + xsk_set_rx_need_wakeup(xskrq->xsk_pool); xsk_rx_alloc_err = INDIRECT_CALL_2(xskrq->post_wqes, mlx5e_post_rx_mpwqes, mlx5e_post_rx_wqes, xskrq); - busy_xsk |= mlx5e_xsk_update_rx_wakeup(xskrq, xsk_rx_alloc_err); + /* Ask for wakeup if WQ is not full after refill. */ + if (!need_wakeup) + busy_xsk |= xsk_rx_alloc_err; + else if (xsk_rx_alloc_err) + xsk_set_rx_need_wakeup(xskrq->xsk_pool); + else + xsk_clear_rx_need_wakeup(xskrq->xsk_pool); return busy_xsk; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eq.c b/drivers/net/ethernet/mellanox/mlx5/core/eq.c index 229728c80233..a0242dc15741 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eq.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eq.c @@ -575,6 +575,9 @@ static void gather_async_events_mask(struct mlx5_core_dev *dev, u64 mask[4]) if (MLX5_CAP_GEN_MAX(dev, vhca_state)) async_event_mask |= (1ull << MLX5_EVENT_TYPE_VHCA_STATE_CHANGE); + if (MLX5_CAP_MACSEC(dev, log_max_macsec_offload)) + async_event_mask |= (1ull << MLX5_EVENT_TYPE_OBJECT_CHANGE); + mask[0] = async_event_mask; if (MLX5_CAP_GEN(dev, event_cap)) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/esw/indir_table.c b/drivers/net/ethernet/mellanox/mlx5/core/esw/indir_table.c index 0abef71cb839..c9a91158e99c 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/esw/indir_table.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/esw/indir_table.c @@ -78,12 +78,16 @@ mlx5_esw_indir_table_needed(struct mlx5_eswitch *esw, struct mlx5_core_dev *dest_mdev) { struct mlx5_esw_flow_attr *esw_attr = attr->esw_attr; + bool vf_sf_vport; + + vf_sf_vport = mlx5_eswitch_is_vf_vport(esw, vport_num) || + mlx5_esw_is_sf_vport(esw, vport_num); /* Use indirect table for all IP traffic from UL to VF with vport * destination when source rewrite flag is set. */ return esw_attr->in_rep->vport == MLX5_VPORT_UPLINK && - mlx5_eswitch_is_vf_vport(esw, vport_num) && + vf_sf_vport && esw->dev == dest_mdev && attr->ip_version && attr->flags & MLX5_ATTR_FLAG_SRC_REWRITE; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/esw/qos.c b/drivers/net/ethernet/mellanox/mlx5/core/esw/qos.c index 694c54066955..4f8a24d84a86 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/esw/qos.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/esw/qos.c @@ -924,12 +924,16 @@ int mlx5_esw_qos_vport_update_group(struct mlx5_eswitch *esw, struct mlx5_esw_rate_group *group, struct netlink_ext_ack *extack) { - int err; + int err = 0; mutex_lock(&esw->state_lock); + if (!vport->qos.enabled && !group) + goto unlock; + err = esw_qos_vport_enable(esw, vport, 0, 0, extack); if (!err) err = esw_qos_vport_update_group(esw, vport, group, extack); +unlock: mutex_unlock(&esw->state_lock); return err; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c index 6aa58044b949..c59107fa9e6d 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c @@ -1360,7 +1360,6 @@ void mlx5_eswitch_disable_sriov(struct mlx5_eswitch *esw, bool clear_vf) if (esw->mode == MLX5_ESWITCH_OFFLOADS) { struct devlink *devlink = priv_to_devlink(esw->dev); - esw_offloads_del_send_to_vport_meta_rules(esw); devl_rate_nodes_destroy(devlink); } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h index 87ce5a208cb5..f68dc2d0dbe6 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h @@ -244,6 +244,8 @@ struct mlx5_esw_offload { struct mlx5_flow_table *ft_offloads; struct mlx5_flow_group *vport_rx_group; + struct mlx5_flow_group *vport_rx_drop_group; + struct mlx5_flow_handle *vport_rx_drop_rule; struct xarray vport_reps; struct list_head peer_flows; struct mutex peer_mutex; @@ -344,7 +346,10 @@ void esw_offloads_disable(struct mlx5_eswitch *esw); int esw_offloads_enable(struct mlx5_eswitch *esw); void esw_offloads_cleanup_reps(struct mlx5_eswitch *esw); int esw_offloads_init_reps(struct mlx5_eswitch *esw); -void esw_offloads_del_send_to_vport_meta_rules(struct mlx5_eswitch *esw); + +struct mlx5_flow_handle * +mlx5_eswitch_add_send_to_vport_meta_rule(struct mlx5_eswitch *esw, u16 vport_num); +void mlx5_eswitch_del_send_to_vport_meta_rule(struct mlx5_flow_handle *rule); bool mlx5_esw_vport_match_metadata_supported(const struct mlx5_eswitch *esw); int mlx5_esw_offloads_vport_metadata_set(struct mlx5_eswitch *esw, bool enable); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c index a9f4c652f859..4e50df3139c6 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c @@ -70,6 +70,8 @@ #define MLX5_ESW_VPORT_TBL_SIZE 128 #define MLX5_ESW_VPORT_TBL_NUM_GROUPS 4 +#define MLX5_ESW_FT_OFFLOADS_DROP_RULE (1) + static const struct esw_vport_tbl_namespace mlx5_esw_vport_tbl_mirror_ns = { .max_fte = MLX5_ESW_VPORT_TBL_SIZE, .max_num_groups = MLX5_ESW_VPORT_TBL_NUM_GROUPS, @@ -481,25 +483,27 @@ esw_setup_dests(struct mlx5_flow_destination *dest, !(attr->flags & MLX5_ATTR_FLAG_SLOW_PATH)) { esw_setup_sampler_dest(dest, flow_act, attr->sample_attr.sampler_id, *i); (*i)++; - } else if (attr->dest_ft) { - esw_setup_ft_dest(dest, flow_act, esw, attr, spec, *i); - (*i)++; } else if (attr->flags & MLX5_ATTR_FLAG_SLOW_PATH) { esw_setup_slow_path_dest(dest, flow_act, esw, *i); (*i)++; } else if (attr->flags & MLX5_ATTR_FLAG_ACCEPT) { esw_setup_accept_dest(dest, flow_act, chains, *i); (*i)++; - } else if (attr->dest_chain) { - err = esw_setup_chain_dest(dest, flow_act, chains, attr->dest_chain, - 1, 0, *i); - (*i)++; } else if (esw_is_indir_table(esw, attr)) { err = esw_setup_indir_table(dest, flow_act, esw, attr, spec, true, i); } else if (esw_is_chain_src_port_rewrite(esw, esw_attr)) { err = esw_setup_chain_src_port_rewrite(dest, flow_act, esw, chains, attr, i); } else { *i = esw_setup_vport_dests(dest, flow_act, esw, esw_attr, *i); + + if (attr->dest_ft) { + err = esw_setup_ft_dest(dest, flow_act, esw, attr, spec, *i); + (*i)++; + } else if (attr->dest_chain) { + err = esw_setup_chain_dest(dest, flow_act, chains, attr->dest_chain, + 1, 0, *i); + (*i)++; + } } return err; @@ -1058,52 +1062,23 @@ void mlx5_eswitch_del_send_to_vport_rule(struct mlx5_flow_handle *rule) mlx5_del_flow_rules(rule); } -static void mlx5_eswitch_del_send_to_vport_meta_rules(struct mlx5_eswitch *esw) +void mlx5_eswitch_del_send_to_vport_meta_rule(struct mlx5_flow_handle *rule) { - struct mlx5_flow_handle **flows = esw->fdb_table.offloads.send_to_vport_meta_rules; - int i = 0, num_vfs = esw->esw_funcs.num_vfs; - - if (!num_vfs || !flows) - return; - - for (i = 0; i < num_vfs; i++) - mlx5_del_flow_rules(flows[i]); - - kvfree(flows); - /* If changing eswitch mode from switchdev to legacy, but num_vfs is not 0, - * meta rules could be freed again. So set it to NULL. - */ - esw->fdb_table.offloads.send_to_vport_meta_rules = NULL; + if (rule) + mlx5_del_flow_rules(rule); } -void esw_offloads_del_send_to_vport_meta_rules(struct mlx5_eswitch *esw) -{ - mlx5_eswitch_del_send_to_vport_meta_rules(esw); -} - -static int -mlx5_eswitch_add_send_to_vport_meta_rules(struct mlx5_eswitch *esw) +struct mlx5_flow_handle * +mlx5_eswitch_add_send_to_vport_meta_rule(struct mlx5_eswitch *esw, u16 vport_num) { struct mlx5_flow_destination dest = {}; struct mlx5_flow_act flow_act = {0}; - int num_vfs, rule_idx = 0, err = 0; struct mlx5_flow_handle *flow_rule; - struct mlx5_flow_handle **flows; struct mlx5_flow_spec *spec; - struct mlx5_vport *vport; - unsigned long i; - u16 vport_num; - - num_vfs = esw->esw_funcs.num_vfs; - flows = kvcalloc(num_vfs, sizeof(*flows), GFP_KERNEL); - if (!flows) - return -ENOMEM; spec = kvzalloc(sizeof(*spec), GFP_KERNEL); - if (!spec) { - err = -ENOMEM; - goto alloc_err; - } + if (!spec) + return ERR_PTR(-ENOMEM); MLX5_SET(fte_match_param, spec->match_criteria, misc_parameters_2.metadata_reg_c_0, mlx5_eswitch_get_vport_metadata_mask()); @@ -1116,34 +1091,18 @@ mlx5_eswitch_add_send_to_vport_meta_rules(struct mlx5_eswitch *esw) dest.type = MLX5_FLOW_DESTINATION_TYPE_VPORT; flow_act.action = MLX5_FLOW_CONTEXT_ACTION_FWD_DEST; - mlx5_esw_for_each_vf_vport(esw, i, vport, num_vfs) { - vport_num = vport->vport; - MLX5_SET(fte_match_param, spec->match_value, misc_parameters_2.metadata_reg_c_0, - mlx5_eswitch_get_vport_metadata_for_match(esw, vport_num)); - dest.vport.num = vport_num; - - flow_rule = mlx5_add_flow_rules(esw->fdb_table.offloads.slow_fdb, - spec, &flow_act, &dest, 1); - if (IS_ERR(flow_rule)) { - err = PTR_ERR(flow_rule); - esw_warn(esw->dev, "FDB: Failed to add send to vport meta rule idx %d, err %ld\n", - rule_idx, PTR_ERR(flow_rule)); - goto rule_err; - } - flows[rule_idx++] = flow_rule; - } + MLX5_SET(fte_match_param, spec->match_value, misc_parameters_2.metadata_reg_c_0, + mlx5_eswitch_get_vport_metadata_for_match(esw, vport_num)); + dest.vport.num = vport_num; - esw->fdb_table.offloads.send_to_vport_meta_rules = flows; - kvfree(spec); - return 0; + flow_rule = mlx5_add_flow_rules(esw->fdb_table.offloads.slow_fdb, + spec, &flow_act, &dest, 1); + if (IS_ERR(flow_rule)) + esw_warn(esw->dev, "FDB: Failed to add send to vport meta rule vport %d, err %ld\n", + vport_num, PTR_ERR(flow_rule)); -rule_err: - while (--rule_idx >= 0) - mlx5_del_flow_rules(flows[rule_idx]); kvfree(spec); -alloc_err: - kvfree(flows); - return err; + return flow_rule; } static bool mlx5_eswitch_reg_c1_loopback_supported(struct mlx5_eswitch *esw) @@ -1668,18 +1627,200 @@ esw_chains_destroy(struct mlx5_eswitch *esw, struct mlx5_fs_chains *chains) #endif +static int +esw_create_send_to_vport_group(struct mlx5_eswitch *esw, + struct mlx5_flow_table *fdb, + u32 *flow_group_in, + int *ix) +{ + int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in); + struct mlx5_flow_group *g; + void *match_criteria; + int count, err = 0; + + memset(flow_group_in, 0, inlen); + + MLX5_SET(create_flow_group_in, flow_group_in, match_criteria_enable, + MLX5_MATCH_MISC_PARAMETERS); + + match_criteria = MLX5_ADDR_OF(create_flow_group_in, flow_group_in, match_criteria); + + MLX5_SET_TO_ONES(fte_match_param, match_criteria, misc_parameters.source_sqn); + MLX5_SET_TO_ONES(fte_match_param, match_criteria, misc_parameters.source_port); + if (MLX5_CAP_ESW(esw->dev, merged_eswitch)) { + MLX5_SET_TO_ONES(fte_match_param, match_criteria, + misc_parameters.source_eswitch_owner_vhca_id); + MLX5_SET(create_flow_group_in, flow_group_in, + source_eswitch_owner_vhca_id_valid, 1); + } + + /* See comment at table_size calculation */ + count = MLX5_MAX_PORTS * (esw->total_vports * MAX_SQ_NVPORTS + MAX_PF_SQ); + MLX5_SET(create_flow_group_in, flow_group_in, start_flow_index, 0); + MLX5_SET(create_flow_group_in, flow_group_in, end_flow_index, *ix + count - 1); + *ix += count; + + g = mlx5_create_flow_group(fdb, flow_group_in); + if (IS_ERR(g)) { + err = PTR_ERR(g); + esw_warn(esw->dev, "Failed to create send-to-vport flow group err(%d)\n", err); + goto out; + } + esw->fdb_table.offloads.send_to_vport_grp = g; + +out: + return err; +} + +static int +esw_create_meta_send_to_vport_group(struct mlx5_eswitch *esw, + struct mlx5_flow_table *fdb, + u32 *flow_group_in, + int *ix) +{ + int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in); + struct mlx5_flow_group *g; + void *match_criteria; + int err = 0; + + if (!esw_src_port_rewrite_supported(esw)) + return 0; + + memset(flow_group_in, 0, inlen); + + MLX5_SET(create_flow_group_in, flow_group_in, match_criteria_enable, + MLX5_MATCH_MISC_PARAMETERS_2); + + match_criteria = MLX5_ADDR_OF(create_flow_group_in, flow_group_in, match_criteria); + + MLX5_SET(fte_match_param, match_criteria, + misc_parameters_2.metadata_reg_c_0, + mlx5_eswitch_get_vport_metadata_mask()); + MLX5_SET(fte_match_param, match_criteria, + misc_parameters_2.metadata_reg_c_1, ESW_TUN_MASK); + + MLX5_SET(create_flow_group_in, flow_group_in, start_flow_index, *ix); + MLX5_SET(create_flow_group_in, flow_group_in, + end_flow_index, *ix + esw->total_vports - 1); + *ix += esw->total_vports; + + g = mlx5_create_flow_group(fdb, flow_group_in); + if (IS_ERR(g)) { + err = PTR_ERR(g); + esw_warn(esw->dev, + "Failed to create send-to-vport meta flow group err(%d)\n", err); + goto send_vport_meta_err; + } + esw->fdb_table.offloads.send_to_vport_meta_grp = g; + + return 0; + +send_vport_meta_err: + return err; +} + +static int +esw_create_peer_esw_miss_group(struct mlx5_eswitch *esw, + struct mlx5_flow_table *fdb, + u32 *flow_group_in, + int *ix) +{ + int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in); + struct mlx5_flow_group *g; + void *match_criteria; + int err = 0; + + if (!MLX5_CAP_ESW(esw->dev, merged_eswitch)) + return 0; + + memset(flow_group_in, 0, inlen); + + esw_set_flow_group_source_port(esw, flow_group_in); + + if (!mlx5_eswitch_vport_match_metadata_enabled(esw)) { + match_criteria = MLX5_ADDR_OF(create_flow_group_in, + flow_group_in, + match_criteria); + + MLX5_SET_TO_ONES(fte_match_param, match_criteria, + misc_parameters.source_eswitch_owner_vhca_id); + + MLX5_SET(create_flow_group_in, flow_group_in, + source_eswitch_owner_vhca_id_valid, 1); + } + + MLX5_SET(create_flow_group_in, flow_group_in, start_flow_index, *ix); + MLX5_SET(create_flow_group_in, flow_group_in, end_flow_index, + *ix + esw->total_vports - 1); + *ix += esw->total_vports; + + g = mlx5_create_flow_group(fdb, flow_group_in); + if (IS_ERR(g)) { + err = PTR_ERR(g); + esw_warn(esw->dev, "Failed to create peer miss flow group err(%d)\n", err); + goto out; + } + esw->fdb_table.offloads.peer_miss_grp = g; + +out: + return err; +} + +static int +esw_create_miss_group(struct mlx5_eswitch *esw, + struct mlx5_flow_table *fdb, + u32 *flow_group_in, + int *ix) +{ + int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in); + struct mlx5_flow_group *g; + void *match_criteria; + int err = 0; + u8 *dmac; + + memset(flow_group_in, 0, inlen); + + MLX5_SET(create_flow_group_in, flow_group_in, match_criteria_enable, + MLX5_MATCH_OUTER_HEADERS); + match_criteria = MLX5_ADDR_OF(create_flow_group_in, flow_group_in, + match_criteria); + dmac = MLX5_ADDR_OF(fte_match_param, match_criteria, + outer_headers.dmac_47_16); + dmac[0] = 0x01; + + MLX5_SET(create_flow_group_in, flow_group_in, start_flow_index, *ix); + MLX5_SET(create_flow_group_in, flow_group_in, end_flow_index, + *ix + MLX5_ESW_MISS_FLOWS); + + g = mlx5_create_flow_group(fdb, flow_group_in); + if (IS_ERR(g)) { + err = PTR_ERR(g); + esw_warn(esw->dev, "Failed to create miss flow group err(%d)\n", err); + goto miss_err; + } + esw->fdb_table.offloads.miss_grp = g; + + err = esw_add_fdb_miss_rule(esw); + if (err) + goto miss_rule_err; + + return 0; + +miss_rule_err: + mlx5_destroy_flow_group(esw->fdb_table.offloads.miss_grp); +miss_err: + return err; +} + static int esw_create_offloads_fdb_tables(struct mlx5_eswitch *esw) { int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in); struct mlx5_flow_table_attr ft_attr = {}; - int num_vfs, table_size, ix, err = 0; struct mlx5_core_dev *dev = esw->dev; struct mlx5_flow_namespace *root_ns; struct mlx5_flow_table *fdb = NULL; + int table_size, ix = 0, err = 0; u32 flags = 0, *flow_group_in; - struct mlx5_flow_group *g; - void *match_criteria; - u8 *dmac; esw_debug(esw->dev, "Create offloads FDB Tables\n"); @@ -1713,7 +1854,7 @@ static int esw_create_offloads_fdb_tables(struct mlx5_eswitch *esw) * total vports of the peer (currently is also uses esw->total_vports). */ table_size = MLX5_MAX_PORTS * (esw->total_vports * MAX_SQ_NVPORTS + MAX_PF_SQ) + - MLX5_ESW_MISS_FLOWS + esw->total_vports + esw->esw_funcs.num_vfs; + esw->total_vports * 2 + MLX5_ESW_MISS_FLOWS; /* create the slow path fdb with encap set, so further table instances * can be created at run time while VFs are probed if the FW allows that. @@ -1754,139 +1895,29 @@ static int esw_create_offloads_fdb_tables(struct mlx5_eswitch *esw) goto fdb_chains_err; } - /* create send-to-vport group */ - MLX5_SET(create_flow_group_in, flow_group_in, match_criteria_enable, - MLX5_MATCH_MISC_PARAMETERS); - - match_criteria = MLX5_ADDR_OF(create_flow_group_in, flow_group_in, match_criteria); - - MLX5_SET_TO_ONES(fte_match_param, match_criteria, misc_parameters.source_sqn); - MLX5_SET_TO_ONES(fte_match_param, match_criteria, misc_parameters.source_port); - if (MLX5_CAP_ESW(esw->dev, merged_eswitch)) { - MLX5_SET_TO_ONES(fte_match_param, match_criteria, - misc_parameters.source_eswitch_owner_vhca_id); - MLX5_SET(create_flow_group_in, flow_group_in, - source_eswitch_owner_vhca_id_valid, 1); - } - - /* See comment above table_size calculation */ - ix = MLX5_MAX_PORTS * (esw->total_vports * MAX_SQ_NVPORTS + MAX_PF_SQ); - MLX5_SET(create_flow_group_in, flow_group_in, start_flow_index, 0); - MLX5_SET(create_flow_group_in, flow_group_in, end_flow_index, ix - 1); - - g = mlx5_create_flow_group(fdb, flow_group_in); - if (IS_ERR(g)) { - err = PTR_ERR(g); - esw_warn(dev, "Failed to create send-to-vport flow group err(%d)\n", err); + err = esw_create_send_to_vport_group(esw, fdb, flow_group_in, &ix); + if (err) goto send_vport_err; - } - esw->fdb_table.offloads.send_to_vport_grp = g; - - if (esw_src_port_rewrite_supported(esw)) { - /* meta send to vport */ - memset(flow_group_in, 0, inlen); - MLX5_SET(create_flow_group_in, flow_group_in, match_criteria_enable, - MLX5_MATCH_MISC_PARAMETERS_2); - - match_criteria = MLX5_ADDR_OF(create_flow_group_in, flow_group_in, match_criteria); - - MLX5_SET(fte_match_param, match_criteria, - misc_parameters_2.metadata_reg_c_0, - mlx5_eswitch_get_vport_metadata_mask()); - MLX5_SET(fte_match_param, match_criteria, - misc_parameters_2.metadata_reg_c_1, ESW_TUN_MASK); - - num_vfs = esw->esw_funcs.num_vfs; - if (num_vfs) { - MLX5_SET(create_flow_group_in, flow_group_in, start_flow_index, ix); - MLX5_SET(create_flow_group_in, flow_group_in, - end_flow_index, ix + num_vfs - 1); - ix += num_vfs; - - g = mlx5_create_flow_group(fdb, flow_group_in); - if (IS_ERR(g)) { - err = PTR_ERR(g); - esw_warn(dev, "Failed to create send-to-vport meta flow group err(%d)\n", - err); - goto send_vport_meta_err; - } - esw->fdb_table.offloads.send_to_vport_meta_grp = g; - - err = mlx5_eswitch_add_send_to_vport_meta_rules(esw); - if (err) - goto meta_rule_err; - } - } - - if (MLX5_CAP_ESW(esw->dev, merged_eswitch)) { - /* create peer esw miss group */ - memset(flow_group_in, 0, inlen); - - esw_set_flow_group_source_port(esw, flow_group_in); - - if (!mlx5_eswitch_vport_match_metadata_enabled(esw)) { - match_criteria = MLX5_ADDR_OF(create_flow_group_in, - flow_group_in, - match_criteria); - - MLX5_SET_TO_ONES(fte_match_param, match_criteria, - misc_parameters.source_eswitch_owner_vhca_id); - - MLX5_SET(create_flow_group_in, flow_group_in, - source_eswitch_owner_vhca_id_valid, 1); - } - - MLX5_SET(create_flow_group_in, flow_group_in, start_flow_index, ix); - MLX5_SET(create_flow_group_in, flow_group_in, end_flow_index, - ix + esw->total_vports - 1); - ix += esw->total_vports; - - g = mlx5_create_flow_group(fdb, flow_group_in); - if (IS_ERR(g)) { - err = PTR_ERR(g); - esw_warn(dev, "Failed to create peer miss flow group err(%d)\n", err); - goto peer_miss_err; - } - esw->fdb_table.offloads.peer_miss_grp = g; - } - /* create miss group */ - memset(flow_group_in, 0, inlen); - MLX5_SET(create_flow_group_in, flow_group_in, match_criteria_enable, - MLX5_MATCH_OUTER_HEADERS); - match_criteria = MLX5_ADDR_OF(create_flow_group_in, flow_group_in, - match_criteria); - dmac = MLX5_ADDR_OF(fte_match_param, match_criteria, - outer_headers.dmac_47_16); - dmac[0] = 0x01; - - MLX5_SET(create_flow_group_in, flow_group_in, start_flow_index, ix); - MLX5_SET(create_flow_group_in, flow_group_in, end_flow_index, - ix + MLX5_ESW_MISS_FLOWS); + err = esw_create_meta_send_to_vport_group(esw, fdb, flow_group_in, &ix); + if (err) + goto send_vport_meta_err; - g = mlx5_create_flow_group(fdb, flow_group_in); - if (IS_ERR(g)) { - err = PTR_ERR(g); - esw_warn(dev, "Failed to create miss flow group err(%d)\n", err); - goto miss_err; - } - esw->fdb_table.offloads.miss_grp = g; + err = esw_create_peer_esw_miss_group(esw, fdb, flow_group_in, &ix); + if (err) + goto peer_miss_err; - err = esw_add_fdb_miss_rule(esw); + err = esw_create_miss_group(esw, fdb, flow_group_in, &ix); if (err) - goto miss_rule_err; + goto miss_err; kvfree(flow_group_in); return 0; -miss_rule_err: - mlx5_destroy_flow_group(esw->fdb_table.offloads.miss_grp); miss_err: if (MLX5_CAP_ESW(esw->dev, merged_eswitch)) mlx5_destroy_flow_group(esw->fdb_table.offloads.peer_miss_grp); peer_miss_err: - mlx5_eswitch_del_send_to_vport_meta_rules(esw); -meta_rule_err: if (esw->fdb_table.offloads.send_to_vport_meta_grp) mlx5_destroy_flow_group(esw->fdb_table.offloads.send_to_vport_meta_grp); send_vport_meta_err: @@ -1913,7 +1944,6 @@ static void esw_destroy_offloads_fdb_tables(struct mlx5_eswitch *esw) esw_debug(esw->dev, "Destroy offloads FDB Tables\n"); mlx5_del_flow_rules(esw->fdb_table.offloads.miss_rule_multi); mlx5_del_flow_rules(esw->fdb_table.offloads.miss_rule_uni); - mlx5_eswitch_del_send_to_vport_meta_rules(esw); mlx5_destroy_flow_group(esw->fdb_table.offloads.send_to_vport_grp); if (esw->fdb_table.offloads.send_to_vport_meta_grp) mlx5_destroy_flow_group(esw->fdb_table.offloads.send_to_vport_meta_grp); @@ -1931,7 +1961,7 @@ static void esw_destroy_offloads_fdb_tables(struct mlx5_eswitch *esw) atomic64_set(&esw->user_count, 0); } -static int esw_get_offloads_ft_size(struct mlx5_eswitch *esw) +static int esw_get_nr_ft_offloads_steering_src_ports(struct mlx5_eswitch *esw) { int nvports; @@ -1956,7 +1986,8 @@ static int esw_create_offloads_table(struct mlx5_eswitch *esw) return -EOPNOTSUPP; } - ft_attr.max_fte = esw_get_offloads_ft_size(esw); + ft_attr.max_fte = esw_get_nr_ft_offloads_steering_src_ports(esw) + + MLX5_ESW_FT_OFFLOADS_DROP_RULE; ft_attr.prio = 1; ft_offloads = mlx5_create_flow_table(ns, &ft_attr); @@ -1985,7 +2016,7 @@ static int esw_create_vport_rx_group(struct mlx5_eswitch *esw) int nvports; int err = 0; - nvports = esw_get_offloads_ft_size(esw); + nvports = esw_get_nr_ft_offloads_steering_src_ports(esw); flow_group_in = kvzalloc(inlen, GFP_KERNEL); if (!flow_group_in) return -ENOMEM; @@ -2015,6 +2046,52 @@ static void esw_destroy_vport_rx_group(struct mlx5_eswitch *esw) mlx5_destroy_flow_group(esw->offloads.vport_rx_group); } +static int esw_create_vport_rx_drop_rule_index(struct mlx5_eswitch *esw) +{ + /* ft_offloads table is enlarged by MLX5_ESW_FT_OFFLOADS_DROP_RULE (1) + * for the drop rule, which is placed at the end of the table. + * So return the total of vport and int_port as rule index. + */ + return esw_get_nr_ft_offloads_steering_src_ports(esw); +} + +static int esw_create_vport_rx_drop_group(struct mlx5_eswitch *esw) +{ + int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in); + struct mlx5_flow_group *g; + u32 *flow_group_in; + int flow_index; + int err = 0; + + flow_index = esw_create_vport_rx_drop_rule_index(esw); + + flow_group_in = kvzalloc(inlen, GFP_KERNEL); + if (!flow_group_in) + return -ENOMEM; + + MLX5_SET(create_flow_group_in, flow_group_in, start_flow_index, flow_index); + MLX5_SET(create_flow_group_in, flow_group_in, end_flow_index, flow_index); + + g = mlx5_create_flow_group(esw->offloads.ft_offloads, flow_group_in); + + if (IS_ERR(g)) { + err = PTR_ERR(g); + mlx5_core_warn(esw->dev, "Failed to create vport rx drop group err %d\n", err); + goto out; + } + + esw->offloads.vport_rx_drop_group = g; +out: + kvfree(flow_group_in); + return err; +} + +static void esw_destroy_vport_rx_drop_group(struct mlx5_eswitch *esw) +{ + if (esw->offloads.vport_rx_drop_group) + mlx5_destroy_flow_group(esw->offloads.vport_rx_drop_group); +} + struct mlx5_flow_handle * mlx5_eswitch_create_vport_rx_rule(struct mlx5_eswitch *esw, u16 vport, struct mlx5_flow_destination *dest) @@ -2063,6 +2140,32 @@ out: return flow_rule; } +static int esw_create_vport_rx_drop_rule(struct mlx5_eswitch *esw) +{ + struct mlx5_flow_act flow_act = {}; + struct mlx5_flow_handle *flow_rule; + + flow_act.action = MLX5_FLOW_CONTEXT_ACTION_DROP; + flow_rule = mlx5_add_flow_rules(esw->offloads.ft_offloads, NULL, + &flow_act, NULL, 0); + if (IS_ERR(flow_rule)) { + esw_warn(esw->dev, + "fs offloads: Failed to add vport rx drop rule err %ld\n", + PTR_ERR(flow_rule)); + return PTR_ERR(flow_rule); + } + + esw->offloads.vport_rx_drop_rule = flow_rule; + + return 0; +} + +static void esw_destroy_vport_rx_drop_rule(struct mlx5_eswitch *esw) +{ + if (esw->offloads.vport_rx_drop_rule) + mlx5_del_flow_rules(esw->offloads.vport_rx_drop_rule); +} + static int mlx5_eswitch_inline_mode_get(struct mlx5_eswitch *esw, u8 *mode) { u8 prev_mlx5_mode, mlx5_mode = MLX5_INLINE_MODE_L2; @@ -3063,8 +3166,20 @@ static int esw_offloads_steering_init(struct mlx5_eswitch *esw) if (err) goto create_fg_err; + err = esw_create_vport_rx_drop_group(esw); + if (err) + goto create_rx_drop_fg_err; + + err = esw_create_vport_rx_drop_rule(esw); + if (err) + goto create_rx_drop_rule_err; + return 0; +create_rx_drop_rule_err: + esw_destroy_vport_rx_drop_group(esw); +create_rx_drop_fg_err: + esw_destroy_vport_rx_group(esw); create_fg_err: esw_destroy_offloads_fdb_tables(esw); create_fdb_err: @@ -3082,6 +3197,8 @@ create_indir_err: static void esw_offloads_steering_cleanup(struct mlx5_eswitch *esw) { + esw_destroy_vport_rx_drop_rule(esw); + esw_destroy_vport_rx_drop_group(esw); esw_destroy_vport_rx_group(esw); esw_destroy_offloads_fdb_tables(esw); esw_destroy_restore_table(esw); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/events.c b/drivers/net/ethernet/mellanox/mlx5/core/events.c index a1ac3a654962..9459e56ee90a 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/events.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/events.c @@ -36,6 +36,7 @@ static struct mlx5_nb events_nbs_ref[] = { /* Events to be forwarded (as is) to mlx5 core interfaces (mlx5e/mlx5_ib) */ {.nb.notifier_call = forward_event, .event_type = MLX5_EVENT_TYPE_PORT_CHANGE }, {.nb.notifier_call = forward_event, .event_type = MLX5_EVENT_TYPE_GENERAL_EVENT }, + {.nb.notifier_call = forward_event, .event_type = MLX5_EVENT_TYPE_OBJECT_CHANGE }, /* QP/WQ resource events to forward */ {.nb.notifier_call = forward_event, .event_type = MLX5_EVENT_TYPE_DCT_DRAINED }, {.nb.notifier_call = forward_event, .event_type = MLX5_EVENT_TYPE_PATH_MIG }, @@ -132,6 +133,8 @@ static const char *eqe_type_str(u8 type) return "MLX5_EVENT_TYPE_MONITOR_COUNTER"; case MLX5_EVENT_TYPE_DEVICE_TRACER: return "MLX5_EVENT_TYPE_DEVICE_TRACER"; + case MLX5_EVENT_TYPE_OBJECT_CHANGE: + return "MLX5_EVENT_TYPE_OBJECT_CHANGE"; default: return "Unrecognized event"; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c b/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c index e735e19461ba..32d4c967469c 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c @@ -577,7 +577,10 @@ static int mlx5_cmd_set_fte(struct mlx5_core_dev *dev, MLX5_SET(flow_context, in_flow_context, modify_header_id, fte->action.modify_hdr->id); - MLX5_SET(flow_context, in_flow_context, ipsec_obj_id, fte->action.ipsec_obj_id); + MLX5_SET(flow_context, in_flow_context, encrypt_decrypt_type, + fte->action.crypto.type); + MLX5_SET(flow_context, in_flow_context, encrypt_decrypt_obj_id, + fte->action.crypto.obj_id); vlan = MLX5_ADDR_OF(flow_context, in_flow_context, push_vlan); @@ -919,13 +922,15 @@ static int mlx5_cmd_modify_header_alloc(struct mlx5_flow_root_namespace *ns, max_actions = MLX5_CAP_ESW_FLOWTABLE_FDB(dev, max_modify_header_actions); table_type = FS_FT_FDB; break; + case MLX5_FLOW_NAMESPACE_KERNEL_RX_MACSEC: case MLX5_FLOW_NAMESPACE_KERNEL: case MLX5_FLOW_NAMESPACE_BYPASS: max_actions = MLX5_CAP_FLOWTABLE_NIC_RX(dev, max_modify_header_actions); table_type = FS_FT_NIC_RX; break; case MLX5_FLOW_NAMESPACE_EGRESS: - case MLX5_FLOW_NAMESPACE_EGRESS_KERNEL: + case MLX5_FLOW_NAMESPACE_EGRESS_IPSEC: + case MLX5_FLOW_NAMESPACE_EGRESS_MACSEC: max_actions = MLX5_CAP_FLOWTABLE_NIC_TX(dev, max_modify_header_actions); table_type = FS_FT_NIC_TX; break; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c index e3960cdf5131..d53749248fa0 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c @@ -104,6 +104,10 @@ #define BY_PASS_MIN_LEVEL (ETHTOOL_MIN_LEVEL + MLX5_BY_PASS_NUM_PRIOS +\ LEFTOVERS_NUM_PRIOS) +#define KERNEL_RX_MACSEC_NUM_PRIOS 1 +#define KERNEL_RX_MACSEC_NUM_LEVELS 2 +#define KERNEL_RX_MACSEC_MIN_LEVEL (BY_PASS_MIN_LEVEL + KERNEL_RX_MACSEC_NUM_PRIOS) + #define ETHTOOL_PRIO_NUM_LEVELS 1 #define ETHTOOL_NUM_PRIOS 11 #define ETHTOOL_MIN_LEVEL (KERNEL_MIN_LEVEL + ETHTOOL_NUM_PRIOS) @@ -126,11 +130,15 @@ #define LAG_PRIO_NUM_LEVELS 1 #define LAG_NUM_PRIOS 1 -#define LAG_MIN_LEVEL (OFFLOADS_MIN_LEVEL + 1) +#define LAG_MIN_LEVEL (OFFLOADS_MIN_LEVEL + KERNEL_RX_MACSEC_MIN_LEVEL + 1) #define KERNEL_TX_IPSEC_NUM_PRIOS 1 #define KERNEL_TX_IPSEC_NUM_LEVELS 1 -#define KERNEL_TX_MIN_LEVEL (KERNEL_TX_IPSEC_NUM_LEVELS) +#define KERNEL_TX_IPSEC_MIN_LEVEL (KERNEL_TX_IPSEC_NUM_LEVELS) + +#define KERNEL_TX_MACSEC_NUM_PRIOS 1 +#define KERNEL_TX_MACSEC_NUM_LEVELS 2 +#define KERNEL_TX_MACSEC_MIN_LEVEL (KERNEL_TX_IPSEC_MIN_LEVEL + KERNEL_TX_MACSEC_NUM_PRIOS) struct node_caps { size_t arr_sz; @@ -149,12 +157,16 @@ static struct init_tree_node { enum mlx5_flow_table_miss_action def_miss_action; } root_fs = { .type = FS_TYPE_NAMESPACE, - .ar_size = 7, + .ar_size = 8, .children = (struct init_tree_node[]){ ADD_PRIO(0, BY_PASS_MIN_LEVEL, 0, FS_CHAINING_CAPS, ADD_NS(MLX5_FLOW_TABLE_MISS_ACTION_DEF, ADD_MULTIPLE_PRIO(MLX5_BY_PASS_NUM_PRIOS, BY_PASS_PRIO_NUM_LEVELS))), + ADD_PRIO(0, KERNEL_RX_MACSEC_MIN_LEVEL, 0, FS_CHAINING_CAPS, + ADD_NS(MLX5_FLOW_TABLE_MISS_ACTION_DEF, + ADD_MULTIPLE_PRIO(KERNEL_RX_MACSEC_NUM_PRIOS, + KERNEL_RX_MACSEC_NUM_LEVELS))), ADD_PRIO(0, LAG_MIN_LEVEL, 0, FS_CHAINING_CAPS, ADD_NS(MLX5_FLOW_TABLE_MISS_ACTION_DEF, ADD_MULTIPLE_PRIO(LAG_NUM_PRIOS, @@ -186,18 +198,23 @@ static struct init_tree_node { static struct init_tree_node egress_root_fs = { .type = FS_TYPE_NAMESPACE, - .ar_size = 2, + .ar_size = 3, .children = (struct init_tree_node[]) { ADD_PRIO(0, MLX5_BY_PASS_NUM_PRIOS, 0, FS_CHAINING_CAPS_EGRESS, ADD_NS(MLX5_FLOW_TABLE_MISS_ACTION_DEF, ADD_MULTIPLE_PRIO(MLX5_BY_PASS_NUM_PRIOS, BY_PASS_PRIO_NUM_LEVELS))), - ADD_PRIO(0, KERNEL_TX_MIN_LEVEL, 0, + ADD_PRIO(0, KERNEL_TX_IPSEC_MIN_LEVEL, 0, FS_CHAINING_CAPS_EGRESS, ADD_NS(MLX5_FLOW_TABLE_MISS_ACTION_DEF, ADD_MULTIPLE_PRIO(KERNEL_TX_IPSEC_NUM_PRIOS, KERNEL_TX_IPSEC_NUM_LEVELS))), + ADD_PRIO(0, KERNEL_TX_MACSEC_MIN_LEVEL, 0, + FS_CHAINING_CAPS_EGRESS, + ADD_NS(MLX5_FLOW_TABLE_MISS_ACTION_DEF, + ADD_MULTIPLE_PRIO(KERNEL_TX_MACSEC_NUM_PRIOS, + KERNEL_TX_MACSEC_NUM_LEVELS))), } }; @@ -2269,6 +2286,7 @@ static bool is_nic_rx_ns(enum mlx5_flow_namespace_type type) { switch (type) { case MLX5_FLOW_NAMESPACE_BYPASS: + case MLX5_FLOW_NAMESPACE_KERNEL_RX_MACSEC: case MLX5_FLOW_NAMESPACE_LAG: case MLX5_FLOW_NAMESPACE_OFFLOADS: case MLX5_FLOW_NAMESPACE_ETHTOOL: @@ -2315,7 +2333,8 @@ struct mlx5_flow_namespace *mlx5_get_flow_namespace(struct mlx5_core_dev *dev, prio = FDB_BYPASS_PATH; break; case MLX5_FLOW_NAMESPACE_EGRESS: - case MLX5_FLOW_NAMESPACE_EGRESS_KERNEL: + case MLX5_FLOW_NAMESPACE_EGRESS_IPSEC: + case MLX5_FLOW_NAMESPACE_EGRESS_MACSEC: root_ns = steering->egress_root_ns; prio = type - MLX5_FLOW_NAMESPACE_EGRESS; break; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fw.c b/drivers/net/ethernet/mellanox/mlx5/core/fw.c index 079fa44ada71..f34e758a2f1f 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fw.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/fw.c @@ -273,6 +273,19 @@ int mlx5_query_hca_caps(struct mlx5_core_dev *dev) return err; } + if (MLX5_CAP_GEN_64(dev, general_obj_types) & + MLX5_GENERAL_OBJ_TYPES_CAP_MACSEC_OFFLOAD) { + err = mlx5_core_get_caps(dev, MLX5_CAP_MACSEC); + if (err) + return err; + } + + if (MLX5_CAP_GEN(dev, adv_virtualization)) { + err = mlx5_core_get_caps(dev, MLX5_CAP_ADV_VIRTUALIZATION); + if (err) + return err; + } + return 0; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/health.c b/drivers/net/ethernet/mellanox/mlx5/core/health.c index 2cf2c9948446..86ed87d704f7 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/health.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/health.c @@ -601,7 +601,7 @@ static void mlx5_fw_reporter_err_work(struct work_struct *work) fw_reporter_ctx.miss_counter = health->miss_counter; if (fw_reporter_ctx.err_synd) { devlink_health_report(health->fw_reporter, - "FW syndrom reported", &fw_reporter_ctx); + "FW syndrome reported", &fw_reporter_ctx); return; } if (fw_reporter_ctx.miss_counter) @@ -702,11 +702,25 @@ static const struct devlink_health_reporter_ops mlx5_fw_fatal_reporter_ops = { .dump = mlx5_fw_fatal_reporter_dump, }; -#define MLX5_REPORTER_FW_GRACEFUL_PERIOD 1200000 +#define MLX5_FW_REPORTER_ECPF_GRACEFUL_PERIOD 180000 +#define MLX5_FW_REPORTER_PF_GRACEFUL_PERIOD 60000 +#define MLX5_FW_REPORTER_VF_GRACEFUL_PERIOD 30000 +#define MLX5_FW_REPORTER_DEFAULT_GRACEFUL_PERIOD MLX5_FW_REPORTER_VF_GRACEFUL_PERIOD + static void mlx5_fw_reporters_create(struct mlx5_core_dev *dev) { struct mlx5_core_health *health = &dev->priv.health; struct devlink *devlink = priv_to_devlink(dev); + u64 grace_period; + + if (mlx5_core_is_ecpf(dev)) { + grace_period = MLX5_FW_REPORTER_ECPF_GRACEFUL_PERIOD; + } else if (mlx5_core_is_pf(dev)) { + grace_period = MLX5_FW_REPORTER_PF_GRACEFUL_PERIOD; + } else { + /* VF or SF */ + grace_period = MLX5_FW_REPORTER_DEFAULT_GRACEFUL_PERIOD; + } health->fw_reporter = devlink_health_reporter_create(devlink, &mlx5_fw_reporter_ops, @@ -718,7 +732,7 @@ static void mlx5_fw_reporters_create(struct mlx5_core_dev *dev) health->fw_fatal_reporter = devlink_health_reporter_create(devlink, &mlx5_fw_fatal_reporter_ops, - MLX5_REPORTER_FW_GRACEFUL_PERIOD, + grace_period, dev); if (IS_ERR(health->fw_fatal_reporter)) mlx5_core_warn(dev, "Failed to create fw fatal reporter, err = %ld\n", @@ -843,9 +857,6 @@ void mlx5_start_health_poll(struct mlx5_core_dev *dev) health->timer.expires = jiffies + msecs_to_jiffies(poll_interval_ms); add_timer(&health->timer); - - if (mlx5_core_is_pf(dev) && MLX5_CAP_MCAM_REG(dev, mrtc)) - queue_delayed_work(health->wq, &health->update_fw_log_ts_work, 0); } void mlx5_stop_health_poll(struct mlx5_core_dev *dev, bool disable_health) @@ -862,6 +873,14 @@ void mlx5_stop_health_poll(struct mlx5_core_dev *dev, bool disable_health) del_timer_sync(&health->timer); } +void mlx5_start_health_fw_log_up(struct mlx5_core_dev *dev) +{ + struct mlx5_core_health *health = &dev->priv.health; + + if (mlx5_core_is_pf(dev) && MLX5_CAP_MCAM_REG(dev, mrtc)) + queue_delayed_work(health->wq, &health->update_fw_log_ts_work, 0); +} + void mlx5_drain_health_wq(struct mlx5_core_dev *dev) { struct mlx5_core_health *health = &dev->priv.health; @@ -875,13 +894,6 @@ void mlx5_drain_health_wq(struct mlx5_core_dev *dev) cancel_work_sync(&health->fatal_report_work); } -void mlx5_health_flush(struct mlx5_core_dev *dev) -{ - struct mlx5_core_health *health = &dev->priv.health; - - flush_workqueue(health->wq); -} - void mlx5_health_cleanup(struct mlx5_core_dev *dev) { struct mlx5_core_health *health = &dev->priv.health; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ethtool.c b/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ethtool.c index ac3757beaea2..c247cca154e9 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ethtool.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ethtool.c @@ -32,6 +32,7 @@ #include "en.h" #include "ipoib.h" +#include "en/fs_ethtool.h" static void mlx5i_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *drvinfo) @@ -39,7 +40,7 @@ static void mlx5i_get_drvinfo(struct net_device *dev, struct mlx5e_priv *priv = mlx5i_epriv(dev); mlx5e_ethtool_get_drvinfo(priv, drvinfo); - strlcpy(drvinfo->driver, KBUILD_MODNAME "[ib_ipoib]", + strscpy(drvinfo->driver, KBUILD_MODNAME "[ib_ipoib]", sizeof(drvinfo->driver)); } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib.c b/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib.c index c02b7b08fb4c..4e3a75496dd9 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib.c @@ -35,6 +35,7 @@ #include "en.h" #include "en/params.h" #include "ipoib.h" +#include "en/fs_ethtool.h" #define IB_DEFAULT_Q_KEY 0xb1b #define MLX5I_PARAMS_DEFAULT_LOG_RQ_SIZE 9 @@ -320,43 +321,47 @@ static void mlx5i_cleanup_tx(struct mlx5e_priv *priv) static int mlx5i_create_flow_steering(struct mlx5e_priv *priv) { + struct mlx5_flow_namespace *ns = + mlx5_get_flow_namespace(priv->mdev, MLX5_FLOW_NAMESPACE_KERNEL); int err; - priv->fs->ns = mlx5_get_flow_namespace(priv->mdev, - MLX5_FLOW_NAMESPACE_KERNEL); - if (!priv->fs->ns) + if (!ns) return -EINVAL; - err = mlx5e_arfs_create_tables(priv); + mlx5e_fs_set_ns(priv->fs, ns, false); + err = mlx5e_arfs_create_tables(priv->fs, priv->rx_res, + !!(priv->netdev->hw_features & NETIF_F_NTUPLE)); if (err) { netdev_err(priv->netdev, "Failed to create arfs tables, err=%d\n", err); priv->netdev->hw_features &= ~NETIF_F_NTUPLE; } - err = mlx5e_create_ttc_table(priv); + err = mlx5e_create_ttc_table(priv->fs, priv->rx_res); if (err) { netdev_err(priv->netdev, "Failed to create ttc table, err=%d\n", err); goto err_destroy_arfs_tables; } - mlx5e_ethtool_init_steering(priv); + mlx5e_ethtool_init_steering(priv->fs); return 0; err_destroy_arfs_tables: - mlx5e_arfs_destroy_tables(priv); + mlx5e_arfs_destroy_tables(priv->fs, + !!(priv->netdev->hw_features & NETIF_F_NTUPLE)); return err; } static void mlx5i_destroy_flow_steering(struct mlx5e_priv *priv) { - mlx5e_destroy_ttc_table(priv); - mlx5e_arfs_destroy_tables(priv); - mlx5e_ethtool_cleanup_steering(priv); + mlx5e_destroy_ttc_table(priv->fs); + mlx5e_arfs_destroy_tables(priv->fs, + !!(priv->netdev->hw_features & NETIF_F_NTUPLE)); + mlx5e_ethtool_cleanup_steering(priv->fs); } static int mlx5i_init_rx(struct mlx5e_priv *priv) @@ -458,7 +463,6 @@ static const struct mlx5e_profile mlx5i_nic_profile = { .update_carrier = NULL, /* no HW update in IB link */ .rx_handlers = &mlx5i_rx_handlers, .max_tc = MLX5I_MAX_NUM_TC, - .rq_groups = MLX5E_NUM_RQ_GROUPS(REGULAR), .stats_grps = mlx5i_stats_grps, .stats_grps_num = mlx5i_stats_grps_num, }; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib_vlan.c b/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib_vlan.c index 0b86e78dbc0e..0227a521d301 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib_vlan.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib_vlan.c @@ -349,7 +349,6 @@ static const struct mlx5e_profile mlx5i_pkey_nic_profile = { .update_stats = NULL, .rx_handlers = &mlx5i_rx_handlers, .max_tc = MLX5I_MAX_NUM_TC, - .rq_groups = MLX5E_NUM_RQ_GROUPS(REGULAR), }; const struct mlx5e_profile *mlx5i_pkey_get_profile(void) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c b/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c index 065102278cb8..a9f4ede4a9bf 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c @@ -65,6 +65,21 @@ static int get_port_sel_mode(enum mlx5_lag_mode mode, unsigned long flags) return MLX5_LAG_PORT_SELECT_MODE_QUEUE_AFFINITY; } +static u8 lag_active_port_bits(struct mlx5_lag *ldev) +{ + u8 enabled_ports[MLX5_MAX_PORTS] = {}; + u8 active_port = 0; + int num_enabled; + int idx; + + mlx5_infer_tx_enabled(&ldev->tracker, ldev->ports, enabled_ports, + &num_enabled); + for (idx = 0; idx < num_enabled; idx++) + active_port |= BIT_MASK(enabled_ports[idx]); + + return active_port; +} + static int mlx5_cmd_create_lag(struct mlx5_core_dev *dev, u8 *ports, int mode, unsigned long flags) { @@ -77,9 +92,21 @@ static int mlx5_cmd_create_lag(struct mlx5_core_dev *dev, u8 *ports, int mode, lag_ctx = MLX5_ADDR_OF(create_lag_in, in, ctx); MLX5_SET(create_lag_in, in, opcode, MLX5_CMD_OP_CREATE_LAG); MLX5_SET(lagc, lag_ctx, fdb_selection_mode, fdb_sel_mode); - if (port_sel_mode == MLX5_LAG_PORT_SELECT_MODE_QUEUE_AFFINITY) { + + switch (port_sel_mode) { + case MLX5_LAG_PORT_SELECT_MODE_QUEUE_AFFINITY: MLX5_SET(lagc, lag_ctx, tx_remap_affinity_1, ports[0]); MLX5_SET(lagc, lag_ctx, tx_remap_affinity_2, ports[1]); + break; + case MLX5_LAG_PORT_SELECT_MODE_PORT_SELECT_FT: + if (!MLX5_CAP_PORT_SELECTION(dev, port_select_flow_table_bypass)) + break; + + MLX5_SET(lagc, lag_ctx, active_port, + lag_active_port_bits(mlx5_lag_dev(dev))); + break; + default: + break; } MLX5_SET(lagc, lag_ctx, port_select_mode, port_sel_mode); @@ -386,12 +413,37 @@ static void mlx5_lag_drop_rule_setup(struct mlx5_lag *ldev, } } +static int mlx5_cmd_modify_active_port(struct mlx5_core_dev *dev, u8 ports) +{ + u32 in[MLX5_ST_SZ_DW(modify_lag_in)] = {}; + void *lag_ctx; + + lag_ctx = MLX5_ADDR_OF(modify_lag_in, in, ctx); + + MLX5_SET(modify_lag_in, in, opcode, MLX5_CMD_OP_MODIFY_LAG); + MLX5_SET(modify_lag_in, in, field_select, 0x2); + + MLX5_SET(lagc, lag_ctx, active_port, ports); + + return mlx5_cmd_exec_in(dev, modify_lag, in); +} + static int _mlx5_modify_lag(struct mlx5_lag *ldev, u8 *ports) { struct mlx5_core_dev *dev0 = ldev->pf[MLX5_LAG_P1].dev; + u8 active_ports; + int ret; + + if (test_bit(MLX5_LAG_MODE_FLAG_HASH_BASED, &ldev->mode_flags)) { + ret = mlx5_lag_port_sel_modify(ldev, ports); + if (ret || + !MLX5_CAP_PORT_SELECTION(dev0, port_select_flow_table_bypass)) + return ret; - if (test_bit(MLX5_LAG_MODE_FLAG_HASH_BASED, &ldev->mode_flags)) - return mlx5_lag_port_sel_modify(ldev, ports); + active_ports = lag_active_port_bits(ldev); + + return mlx5_cmd_modify_active_port(dev0, active_ports); + } return mlx5_cmd_modify_lag(dev0, ldev->ports, ports); } @@ -432,21 +484,22 @@ void mlx5_modify_lag(struct mlx5_lag *ldev, mlx5_lag_drop_rule_setup(ldev, tracker); } -#define MLX5_LAG_ROCE_HASH_PORTS_SUPPORTED 4 static int mlx5_lag_set_port_sel_mode_roce(struct mlx5_lag *ldev, unsigned long *flags) { - struct lag_func *dev0 = &ldev->pf[MLX5_LAG_P1]; + struct mlx5_core_dev *dev0 = ldev->pf[MLX5_LAG_P1].dev; - if (ldev->ports == MLX5_LAG_ROCE_HASH_PORTS_SUPPORTED) { - /* Four ports are support only in hash mode */ - if (!MLX5_CAP_PORT_SELECTION(dev0->dev, port_select_flow_table)) - return -EINVAL; - set_bit(MLX5_LAG_MODE_FLAG_HASH_BASED, flags); + if (!MLX5_CAP_PORT_SELECTION(dev0, port_select_flow_table)) { if (ldev->ports > 2) - ldev->buckets = MLX5_LAG_MAX_HASH_BUCKETS; + return -EINVAL; + return 0; } + if (ldev->ports > 2) + ldev->buckets = MLX5_LAG_MAX_HASH_BUCKETS; + + set_bit(MLX5_LAG_MODE_FLAG_HASH_BASED, flags); + return 0; } @@ -1275,6 +1328,22 @@ bool mlx5_lag_is_active(struct mlx5_core_dev *dev) } EXPORT_SYMBOL(mlx5_lag_is_active); +bool mlx5_lag_mode_is_hash(struct mlx5_core_dev *dev) +{ + struct mlx5_lag *ldev; + unsigned long flags; + bool res = 0; + + spin_lock_irqsave(&lag_lock, flags); + ldev = mlx5_lag_dev(dev); + if (ldev) + res = test_bit(MLX5_LAG_MODE_FLAG_HASH_BASED, &ldev->mode_flags); + spin_unlock_irqrestore(&lag_lock, flags); + + return res; +} +EXPORT_SYMBOL(mlx5_lag_mode_is_hash); + bool mlx5_lag_is_master(struct mlx5_core_dev *dev) { struct mlx5_lag *ldev; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lib/aso.h b/drivers/net/ethernet/mellanox/mlx5/core/lib/aso.h index b3bbf284fe71..d854e01d7fc5 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/lib/aso.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/lib/aso.h @@ -11,7 +11,9 @@ (DIV_ROUND_UP(sizeof(struct mlx5_aso_wqe), MLX5_SEND_WQE_BB)) #define MLX5_ASO_WQEBBS_DATA \ (DIV_ROUND_UP(sizeof(struct mlx5_aso_wqe_data), MLX5_SEND_WQE_BB)) +#define ASO_CTRL_READ_EN BIT(0) #define MLX5_WQE_CTRL_WQE_OPC_MOD_SHIFT 24 +#define MLX5_MACSEC_ASO_DS_CNT (DIV_ROUND_UP(sizeof(struct mlx5_aso_wqe), MLX5_SEND_WQE_DS)) struct mlx5_wqe_aso_ctrl_seg { __be32 va_h; @@ -70,6 +72,7 @@ enum { enum { MLX5_ACCESS_ASO_OPC_MOD_FLOW_METER = 0x2, + MLX5_ACCESS_ASO_OPC_MOD_MACSEC = 0x5, }; struct mlx5_aso; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lib/clock.c b/drivers/net/ethernet/mellanox/mlx5/core/lib/clock.c index 91e806c1aa21..d3a9ae80fd30 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/lib/clock.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/lib/clock.c @@ -65,6 +65,8 @@ enum { MLX5_MTPPS_FS_TIME_STAMP = BIT(0x4), MLX5_MTPPS_FS_OUT_PULSE_DURATION = BIT(0x5), MLX5_MTPPS_FS_ENH_OUT_PER_ADJ = BIT(0x7), + MLX5_MTPPS_FS_NPPS_PERIOD = BIT(0x9), + MLX5_MTPPS_FS_OUT_PULSE_DURATION_NS = BIT(0xa), }; static bool mlx5_real_time_mode(struct mlx5_core_dev *mdev) @@ -72,6 +74,13 @@ static bool mlx5_real_time_mode(struct mlx5_core_dev *mdev) return (mlx5_is_real_time_rq(mdev) || mlx5_is_real_time_sq(mdev)); } +static bool mlx5_npps_real_time_supported(struct mlx5_core_dev *mdev) +{ + return (mlx5_real_time_mode(mdev) && + MLX5_CAP_MCAM_FEATURE(mdev, npps_period) && + MLX5_CAP_MCAM_FEATURE(mdev, out_pulse_duration_ns)); +} + static bool mlx5_modify_mtutc_allowed(struct mlx5_core_dev *mdev) { return MLX5_CAP_MCAM_FEATURE(mdev, ptpcyc2realtime_modify); @@ -459,9 +468,95 @@ static u64 perout_conf_internal_timer(struct mlx5_core_dev *mdev, s64 sec) return find_target_cycles(mdev, target_ns); } -static u64 perout_conf_real_time(s64 sec) +static u64 perout_conf_real_time(s64 sec, u32 nsec) +{ + return (u64)nsec | (u64)sec << 32; +} + +static int perout_conf_1pps(struct mlx5_core_dev *mdev, struct ptp_clock_request *rq, + u64 *time_stamp, bool real_time) +{ + struct timespec64 ts; + s64 ns; + + ts.tv_nsec = rq->perout.period.nsec; + ts.tv_sec = rq->perout.period.sec; + ns = timespec64_to_ns(&ts); + + if ((ns >> 1) != 500000000LL) + return -EINVAL; + + *time_stamp = real_time ? perout_conf_real_time(rq->perout.start.sec, 0) : + perout_conf_internal_timer(mdev, rq->perout.start.sec); + + return 0; +} + +#define MLX5_MAX_PULSE_DURATION (BIT(__mlx5_bit_sz(mtpps_reg, out_pulse_duration_ns)) - 1) +static int mlx5_perout_conf_out_pulse_duration(struct mlx5_core_dev *mdev, + struct ptp_clock_request *rq, + u32 *out_pulse_duration_ns) { - return (u64)sec << 32; + struct mlx5_pps *pps_info = &mdev->clock.pps_info; + u32 out_pulse_duration; + struct timespec64 ts; + + if (rq->perout.flags & PTP_PEROUT_DUTY_CYCLE) { + ts.tv_sec = rq->perout.on.sec; + ts.tv_nsec = rq->perout.on.nsec; + out_pulse_duration = (u32)timespec64_to_ns(&ts); + } else { + /* out_pulse_duration_ns should be up to 50% of the + * pulse period as default + */ + ts.tv_sec = rq->perout.period.sec; + ts.tv_nsec = rq->perout.period.nsec; + out_pulse_duration = (u32)timespec64_to_ns(&ts) >> 1; + } + + if (out_pulse_duration < pps_info->min_out_pulse_duration_ns || + out_pulse_duration > MLX5_MAX_PULSE_DURATION) { + mlx5_core_err(mdev, "NPPS pulse duration %u is not in [%llu, %lu]\n", + out_pulse_duration, pps_info->min_out_pulse_duration_ns, + MLX5_MAX_PULSE_DURATION); + return -EINVAL; + } + *out_pulse_duration_ns = out_pulse_duration; + + return 0; +} + +static int perout_conf_npps_real_time(struct mlx5_core_dev *mdev, struct ptp_clock_request *rq, + u32 *field_select, u32 *out_pulse_duration_ns, + u64 *period, u64 *time_stamp) +{ + struct mlx5_pps *pps_info = &mdev->clock.pps_info; + struct ptp_clock_time *time = &rq->perout.start; + struct timespec64 ts; + + ts.tv_sec = rq->perout.period.sec; + ts.tv_nsec = rq->perout.period.nsec; + if (timespec64_to_ns(&ts) < pps_info->min_npps_period) { + mlx5_core_err(mdev, "NPPS period is lower than minimal npps period %llu\n", + pps_info->min_npps_period); + return -EINVAL; + } + *period = perout_conf_real_time(rq->perout.period.sec, rq->perout.period.nsec); + + if (mlx5_perout_conf_out_pulse_duration(mdev, rq, out_pulse_duration_ns)) + return -EINVAL; + + *time_stamp = perout_conf_real_time(time->sec, time->nsec); + *field_select |= MLX5_MTPPS_FS_NPPS_PERIOD | + MLX5_MTPPS_FS_OUT_PULSE_DURATION_NS; + + return 0; +} + +static bool mlx5_perout_verify_flags(struct mlx5_core_dev *mdev, unsigned int flags) +{ + return ((!mlx5_npps_real_time_supported(mdev) && flags) || + (mlx5_npps_real_time_supported(mdev) && flags & ~PTP_PEROUT_DUTY_CYCLE)); } static int mlx5_perout_configure(struct ptp_clock_info *ptp, @@ -474,20 +569,20 @@ static int mlx5_perout_configure(struct ptp_clock_info *ptp, container_of(clock, struct mlx5_core_dev, clock); bool rt_mode = mlx5_real_time_mode(mdev); u32 in[MLX5_ST_SZ_DW(mtpps_reg)] = {0}; - struct timespec64 ts; + u32 out_pulse_duration_ns = 0; u32 field_select = 0; + u64 npps_period = 0; u64 time_stamp = 0; u8 pin_mode = 0; u8 pattern = 0; int pin = -1; int err = 0; - s64 ns; if (!MLX5_PPS_CAP(mdev)) return -EOPNOTSUPP; /* Reject requests with unsupported flags */ - if (rq->perout.flags) + if (mlx5_perout_verify_flags(mdev, rq->perout.flags)) return -EOPNOTSUPP; if (rq->perout.index >= clock->ptp_info.n_pins) @@ -500,29 +595,25 @@ static int mlx5_perout_configure(struct ptp_clock_info *ptp, if (on) { bool rt_mode = mlx5_real_time_mode(mdev); - s64 sec = rq->perout.start.sec; - - if (rq->perout.start.nsec) - return -EINVAL; pin_mode = MLX5_PIN_MODE_OUT; pattern = MLX5_OUT_PATTERN_PERIODIC; - ts.tv_sec = rq->perout.period.sec; - ts.tv_nsec = rq->perout.period.nsec; - ns = timespec64_to_ns(&ts); - if ((ns >> 1) != 500000000LL) + if (rt_mode && rq->perout.start.sec > U32_MAX) return -EINVAL; - if (rt_mode && sec > U32_MAX) - return -EINVAL; - - time_stamp = rt_mode ? perout_conf_real_time(sec) : - perout_conf_internal_timer(mdev, sec); - field_select |= MLX5_MTPPS_FS_PIN_MODE | MLX5_MTPPS_FS_PATTERN | MLX5_MTPPS_FS_TIME_STAMP; + + if (mlx5_npps_real_time_supported(mdev)) + err = perout_conf_npps_real_time(mdev, rq, &field_select, + &out_pulse_duration_ns, &npps_period, + &time_stamp); + else + err = perout_conf_1pps(mdev, rq, &time_stamp, rt_mode); + if (err) + return err; } MLX5_SET(mtpps_reg, in, pin, pin); @@ -531,7 +622,8 @@ static int mlx5_perout_configure(struct ptp_clock_info *ptp, MLX5_SET(mtpps_reg, in, enable, on); MLX5_SET64(mtpps_reg, in, time_stamp, time_stamp); MLX5_SET(mtpps_reg, in, field_select, field_select); - + MLX5_SET64(mtpps_reg, in, npps_period, npps_period); + MLX5_SET(mtpps_reg, in, out_pulse_duration_ns, out_pulse_duration_ns); err = mlx5_set_mtpps(mdev, in, sizeof(in)); if (err) return err; @@ -687,6 +779,13 @@ static void mlx5_get_pps_caps(struct mlx5_core_dev *mdev) clock->ptp_info.n_per_out = MLX5_GET(mtpps_reg, out, cap_max_num_of_pps_out_pins); + if (MLX5_CAP_MCAM_FEATURE(mdev, npps_period)) + clock->pps_info.min_npps_period = 1 << MLX5_GET(mtpps_reg, out, + cap_log_min_npps_period); + if (MLX5_CAP_MCAM_FEATURE(mdev, out_pulse_duration_ns)) + clock->pps_info.min_out_pulse_duration_ns = 1 << MLX5_GET(mtpps_reg, out, + cap_log_min_out_pulse_duration_ns); + clock->pps_info.pin_caps[0] = MLX5_GET(mtpps_reg, out, cap_pin_0_mode); clock->pps_info.pin_caps[1] = MLX5_GET(mtpps_reg, out, cap_pin_1_mode); clock->pps_info.pin_caps[2] = MLX5_GET(mtpps_reg, out, cap_pin_2_mode); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lib/mlx5.h b/drivers/net/ethernet/mellanox/mlx5/core/lib/mlx5.h index 2f536c5d30b1..032adb21ad4b 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/lib/mlx5.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/lib/mlx5.h @@ -83,6 +83,7 @@ int mlx5_notifier_call_chain(struct mlx5_events *events, unsigned int event, voi enum { MLX5_ACCEL_OBJ_TLS_KEY = MLX5_GENERAL_OBJECT_TYPE_ENCRYPTION_KEY_TYPE_TLS, MLX5_ACCEL_OBJ_IPSEC_KEY = MLX5_GENERAL_OBJECT_TYPE_ENCRYPTION_KEY_TYPE_IPSEC, + MLX5_ACCEL_OBJ_MACSEC_KEY = MLX5_GENERAL_OBJECT_TYPE_ENCRYPTION_KEY_TYPE_MACSEC, }; int mlx5_create_encryption_key(struct mlx5_core_dev *mdev, diff --git a/drivers/net/ethernet/mellanox/mlx5/core/main.c b/drivers/net/ethernet/mellanox/mlx5/core/main.c index 89b2d9cea33f..0b459d841c3a 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/main.c @@ -671,6 +671,33 @@ static int handle_hca_cap_roce(struct mlx5_core_dev *dev, void *set_ctx) return err; } +static int handle_hca_cap_port_selection(struct mlx5_core_dev *dev, + void *set_ctx) +{ + void *set_hca_cap; + int err; + + if (!MLX5_CAP_GEN(dev, port_selection_cap)) + return 0; + + err = mlx5_core_get_caps(dev, MLX5_CAP_PORT_SELECTION); + if (err) + return err; + + if (MLX5_CAP_PORT_SELECTION(dev, port_select_flow_table_bypass) || + !MLX5_CAP_PORT_SELECTION_MAX(dev, port_select_flow_table_bypass)) + return 0; + + set_hca_cap = MLX5_ADDR_OF(set_hca_cap_in, set_ctx, capability); + memcpy(set_hca_cap, dev->caps.hca[MLX5_CAP_PORT_SELECTION]->cur, + MLX5_ST_SZ_BYTES(port_selection_cap)); + MLX5_SET(port_selection_cap, set_hca_cap, port_select_flow_table_bypass, 1); + + err = set_caps(dev, set_ctx, MLX5_SET_HCA_CAP_OP_MODE_PORT_SELECTION); + + return err; +} + static int set_hca_cap(struct mlx5_core_dev *dev) { int set_sz = MLX5_ST_SZ_BYTES(set_hca_cap_in); @@ -715,6 +742,13 @@ static int set_hca_cap(struct mlx5_core_dev *dev) goto out; } + memset(set_ctx, 0, set_sz); + err = handle_hca_cap_port_selection(dev, set_ctx); + if (err) { + mlx5_core_err(dev, "handle_hca_cap_port_selection failed\n"); + goto out; + } + out: kfree(set_ctx); return err; @@ -1058,7 +1092,7 @@ static void mlx5_cleanup_once(struct mlx5_core_dev *dev) mlx5_devcom_unregister_device(dev->priv.devcom); } -static int mlx5_function_setup(struct mlx5_core_dev *dev, u64 timeout) +static int mlx5_function_setup(struct mlx5_core_dev *dev, bool boot, u64 timeout) { int err; @@ -1096,10 +1130,12 @@ static int mlx5_function_setup(struct mlx5_core_dev *dev, u64 timeout) mlx5_cmd_set_state(dev, MLX5_CMDIF_STATE_UP); + mlx5_start_health_poll(dev); + err = mlx5_core_enable_hca(dev, 0); if (err) { mlx5_core_err(dev, "enable hca failed\n"); - goto err_cmd_cleanup; + goto stop_health_poll; } err = mlx5_core_set_issi(dev); @@ -1151,8 +1187,7 @@ static int mlx5_function_setup(struct mlx5_core_dev *dev, u64 timeout) mlx5_core_err(dev, "query hca failed\n"); goto reclaim_boot_pages; } - - mlx5_start_health_poll(dev); + mlx5_start_health_fw_log_up(dev); return 0; @@ -1160,6 +1195,8 @@ reclaim_boot_pages: mlx5_reclaim_startup_pages(dev); err_disable_hca: mlx5_core_disable_hca(dev, 0); +stop_health_poll: + mlx5_stop_health_poll(dev, boot); err_cmd_cleanup: mlx5_cmd_set_state(dev, MLX5_CMDIF_STATE_DOWN); mlx5_cmd_cleanup(dev); @@ -1171,7 +1208,6 @@ static int mlx5_function_teardown(struct mlx5_core_dev *dev, bool boot) { int err; - mlx5_stop_health_poll(dev, boot); err = mlx5_cmd_teardown_hca(dev); if (err) { mlx5_core_err(dev, "tear_down_hca failed, skip cleanup\n"); @@ -1179,6 +1215,7 @@ static int mlx5_function_teardown(struct mlx5_core_dev *dev, bool boot) } mlx5_reclaim_startup_pages(dev); mlx5_core_disable_hca(dev, 0); + mlx5_stop_health_poll(dev, boot); mlx5_cmd_set_state(dev, MLX5_CMDIF_STATE_DOWN); mlx5_cmd_cleanup(dev); @@ -1328,7 +1365,7 @@ int mlx5_init_one(struct mlx5_core_dev *dev) mutex_lock(&dev->intf_state_mutex); dev->state = MLX5_DEVICE_STATE_UP; - err = mlx5_function_setup(dev, mlx5_tout_ms(dev, FW_PRE_INIT_TIMEOUT)); + err = mlx5_function_setup(dev, true, mlx5_tout_ms(dev, FW_PRE_INIT_TIMEOUT)); if (err) goto err_function; @@ -1416,7 +1453,7 @@ int mlx5_load_one_devl_locked(struct mlx5_core_dev *dev, bool recovery) timeout = mlx5_tout_ms(dev, FW_PRE_INIT_ON_RECOVERY_TIMEOUT); else timeout = mlx5_tout_ms(dev, FW_PRE_INIT_TIMEOUT); - err = mlx5_function_setup(dev, timeout); + err = mlx5_function_setup(dev, false, timeout); if (err) goto err_function; @@ -1507,6 +1544,8 @@ static const int types[] = { MLX5_CAP_IPSEC, MLX5_CAP_PORT_SELECTION, MLX5_CAP_DEV_SHAMPO, + MLX5_CAP_MACSEC, + MLX5_CAP_ADV_VIRTUALIZATION, }; static void mlx5_hca_caps_free(struct mlx5_core_dev *dev) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h b/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h index ad61b86d5769..a806e3de7b7c 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h @@ -143,6 +143,36 @@ enum mlx5_semaphore_space_address { #define MLX5_DEFAULT_PROF 2 +static inline int mlx5_flexible_inlen(struct mlx5_core_dev *dev, size_t fixed, + size_t item_size, size_t num_items, + const char *func, int line) +{ + int inlen; + + if (fixed > INT_MAX || item_size > INT_MAX || num_items > INT_MAX) { + mlx5_core_err(dev, "%s: %s:%d: input values too big: %zu + %zu * %zu\n", + __func__, func, line, fixed, item_size, num_items); + return -ENOMEM; + } + + if (check_mul_overflow((int)item_size, (int)num_items, &inlen)) { + mlx5_core_err(dev, "%s: %s:%d: multiplication overflow: %zu + %zu * %zu\n", + __func__, func, line, fixed, item_size, num_items); + return -ENOMEM; + } + + if (check_add_overflow((int)fixed, inlen, &inlen)) { + mlx5_core_err(dev, "%s: %s:%d: addition overflow: %zu + %zu * %zu\n", + __func__, func, line, fixed, item_size, num_items); + return -ENOMEM; + } + + return inlen; +} + +#define MLX5_FLEXIBLE_INLEN(dev, fixed, item_size, num_items) \ + mlx5_flexible_inlen(dev, fixed, item_size, num_items, __func__, __LINE__) + int mlx5_query_hca_caps(struct mlx5_core_dev *dev); int mlx5_query_board_id(struct mlx5_core_dev *dev); int mlx5_cmd_init(struct mlx5_core_dev *dev); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/port.c b/drivers/net/ethernet/mellanox/mlx5/core/port.c index e1bd54574ea5..a1548e6bfb35 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/port.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/port.c @@ -493,29 +493,6 @@ int mlx5_query_port_vl_hw_cap(struct mlx5_core_dev *dev, } EXPORT_SYMBOL_GPL(mlx5_query_port_vl_hw_cap); -int mlx5_core_query_ib_ppcnt(struct mlx5_core_dev *dev, - u8 port_num, void *out, size_t sz) -{ - u32 *in; - int err; - - in = kvzalloc(sz, GFP_KERNEL); - if (!in) { - err = -ENOMEM; - return err; - } - - MLX5_SET(ppcnt_reg, in, local_port, port_num); - - MLX5_SET(ppcnt_reg, in, grp, MLX5_INFINIBAND_PORT_COUNTERS_GROUP); - err = mlx5_core_access_reg(dev, in, sz, out, - sz, MLX5_REG_PPCNT, 0, 0); - - kvfree(in); - return err; -} -EXPORT_SYMBOL_GPL(mlx5_core_query_ib_ppcnt); - static int mlx5_query_pfcc_reg(struct mlx5_core_dev *dev, u32 *out, u32 out_size) { diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_types.h b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_types.h index 062c7c74a1f3..1777a1e508e7 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_types.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_types.h @@ -1294,20 +1294,6 @@ struct mlx5dr_cmd_gid_attr { u32 roce_ver; }; -struct mlx5dr_cmd_qp_create_attr { - u32 page_id; - u32 pdn; - u32 cqn; - u32 pm_state; - u32 service_type; - u32 buff_umem_id; - u32 db_umem_id; - u32 sq_wqe_cnt; - u32 rq_wqe_cnt; - u32 rq_wqe_shift; - u8 isolate_vl_tc:1; -}; - int mlx5dr_cmd_query_gid(struct mlx5_core_dev *mdev, u8 vhca_port_num, u16 index, struct mlx5dr_cmd_gid_attr *attr); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/fs_dr.h b/drivers/net/ethernet/mellanox/mlx5/core/steering/fs_dr.h index 1fb185d6ac7f..d168622063d5 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/fs_dr.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/fs_dr.h @@ -14,10 +14,6 @@ struct mlx5_fs_dr_action { struct mlx5dr_action *dr_action; }; -struct mlx5_fs_dr_ns { - struct mlx5_dr_ns *dr_ns; -}; - struct mlx5_fs_dr_rule { struct mlx5dr_rule *dr_rule; /* Only actions created by fs_dr */ diff --git a/drivers/net/ethernet/mellanox/mlx5/core/wq.h b/drivers/net/ethernet/mellanox/mlx5/core/wq.h index e5c4dcd1425e..4d629e5ddbc7 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/wq.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/wq.h @@ -123,7 +123,7 @@ static inline void mlx5_wq_cyc_push(struct mlx5_wq_cyc *wq) wq->cur_sz++; } -static inline void mlx5_wq_cyc_push_n(struct mlx5_wq_cyc *wq, u8 n) +static inline void mlx5_wq_cyc_push_n(struct mlx5_wq_cyc *wq, u16 n) { wq->wqe_ctr += n; wq->cur_sz += n; diff --git a/drivers/net/ethernet/mellanox/mlxbf_gige/mlxbf_gige_main.c b/drivers/net/ethernet/mellanox/mlxbf_gige/mlxbf_gige_main.c index b03e1c66bac0..2292d63a279c 100644 --- a/drivers/net/ethernet/mellanox/mlxbf_gige/mlxbf_gige_main.c +++ b/drivers/net/ethernet/mellanox/mlxbf_gige/mlxbf_gige_main.c @@ -156,7 +156,7 @@ static int mlxbf_gige_open(struct net_device *netdev) phy_start(phydev); - netif_napi_add(netdev, &priv->napi, mlxbf_gige_poll, NAPI_POLL_WEIGHT); + netif_napi_add(netdev, &priv->napi, mlxbf_gige_poll); napi_enable(&priv->napi); netif_start_queue(netdev); diff --git a/drivers/net/ethernet/mellanox/mlxsw/cmd.h b/drivers/net/ethernet/mellanox/mlxsw/cmd.h index 60232fb8ccd7..09bef04b11d1 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/cmd.h +++ b/drivers/net/ethernet/mellanox/mlxsw/cmd.h @@ -703,6 +703,9 @@ MLXSW_ITEM32(cmd_mbox, config_profile, max_vepa_channels, 0x10, 0, 8); /* cmd_mbox_config_profile_max_lag * Maximum number of LAG IDs requested. + * Reserved when Spectrum-1/2/3, supported from Spectrum-4 and above. + * For Spectrum-4, firmware sets 128 for values between 1-128 and 256 for values + * between 129-256. */ MLXSW_ITEM32(cmd_mbox, config_profile, max_lag, 0x14, 0, 16); diff --git a/drivers/net/ethernet/mellanox/mlxsw/core.c b/drivers/net/ethernet/mellanox/mlxsw/core.c index 75553eb2c7f2..e2a985ec2c76 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core.c +++ b/drivers/net/ethernet/mellanox/mlxsw/core.c @@ -70,6 +70,8 @@ struct mlxsw_core { struct workqueue_struct *emad_wq; struct list_head rx_listener_list; struct list_head event_listener_list; + struct list_head irq_event_handler_list; + struct mutex irq_event_handler_lock; /* Locks access to handlers list */ struct { atomic64_t tid; struct list_head trans_list; @@ -184,6 +186,23 @@ unsigned int mlxsw_core_max_ports(const struct mlxsw_core *mlxsw_core) } EXPORT_SYMBOL(mlxsw_core_max_ports); +int mlxsw_core_max_lag(struct mlxsw_core *mlxsw_core, u16 *p_max_lag) +{ + struct mlxsw_driver *driver = mlxsw_core->driver; + + if (driver->profile->used_max_lag) { + *p_max_lag = driver->profile->max_lag; + return 0; + } + + if (!MLXSW_CORE_RES_VALID(mlxsw_core, MAX_LAG)) + return -EIO; + + *p_max_lag = MLXSW_CORE_RES_GET(mlxsw_core, MAX_LAG); + return 0; +} +EXPORT_SYMBOL(mlxsw_core_max_lag); + void *mlxsw_core_driver_priv(struct mlxsw_core *mlxsw_core) { return mlxsw_core->driver_priv; @@ -633,7 +652,7 @@ static void mlxsw_emad_process_string_tlv(const struct sk_buff *skb, return; string = mlxsw_emad_string_tlv_string_data(string_tlv); - strlcpy(trans->emad_err_string, string, + strscpy(trans->emad_err_string, string, MLXSW_EMAD_STRING_TLV_STRING_LEN); } @@ -1305,21 +1324,6 @@ mlxsw_devlink_sb_pool_set(struct devlink *devlink, extack); } -static int mlxsw_devlink_port_type_set(struct devlink_port *devlink_port, - enum devlink_port_type port_type) -{ - struct mlxsw_core *mlxsw_core = devlink_priv(devlink_port->devlink); - struct mlxsw_driver *mlxsw_driver = mlxsw_core->driver; - struct mlxsw_core_port *mlxsw_core_port = __dl_port(devlink_port); - - if (!mlxsw_driver->port_type_set) - return -EOPNOTSUPP; - - return mlxsw_driver->port_type_set(mlxsw_core, - mlxsw_core_port->local_port, - port_type); -} - static int mlxsw_devlink_sb_port_pool_get(struct devlink_port *devlink_port, unsigned int sb_index, u16 pool_index, u32 *p_threshold) @@ -1650,7 +1654,6 @@ static const struct devlink_ops mlxsw_devlink_ops = { BIT(DEVLINK_RELOAD_ACTION_FW_ACTIVATE), .reload_down = mlxsw_devlink_core_bus_device_reload_down, .reload_up = mlxsw_devlink_core_bus_device_reload_up, - .port_type_set = mlxsw_devlink_port_type_set, .port_split = mlxsw_devlink_port_split, .port_unsplit = mlxsw_devlink_port_unsplit, .sb_pool_get = mlxsw_devlink_sb_pool_get, @@ -2090,6 +2093,18 @@ static void mlxsw_core_health_fini(struct mlxsw_core *mlxsw_core) devlink_health_reporter_destroy(mlxsw_core->health.fw_fatal); } +static void mlxsw_core_irq_event_handler_init(struct mlxsw_core *mlxsw_core) +{ + INIT_LIST_HEAD(&mlxsw_core->irq_event_handler_list); + mutex_init(&mlxsw_core->irq_event_handler_lock); +} + +static void mlxsw_core_irq_event_handler_fini(struct mlxsw_core *mlxsw_core) +{ + mutex_destroy(&mlxsw_core->irq_event_handler_lock); + WARN_ON(!list_empty(&mlxsw_core->irq_event_handler_list)); +} + static int __mlxsw_core_bus_device_register(const struct mlxsw_bus_info *mlxsw_bus_info, const struct mlxsw_bus *mlxsw_bus, @@ -2101,6 +2116,7 @@ __mlxsw_core_bus_device_register(const struct mlxsw_bus_info *mlxsw_bus_info, struct mlxsw_core *mlxsw_core; struct mlxsw_driver *mlxsw_driver; size_t alloc_size; + u16 max_lag; int err; mlxsw_driver = mlxsw_core_driver_get(device_kind); @@ -2125,6 +2141,7 @@ __mlxsw_core_bus_device_register(const struct mlxsw_bus_info *mlxsw_bus_info, mlxsw_core->bus = mlxsw_bus; mlxsw_core->bus_priv = bus_priv; mlxsw_core->bus_info = mlxsw_bus_info; + mlxsw_core_irq_event_handler_init(mlxsw_core); err = mlxsw_bus->init(bus_priv, mlxsw_core, mlxsw_driver->profile, &mlxsw_core->res); @@ -2141,10 +2158,9 @@ __mlxsw_core_bus_device_register(const struct mlxsw_bus_info *mlxsw_bus_info, if (err) goto err_ports_init; - if (MLXSW_CORE_RES_VALID(mlxsw_core, MAX_LAG) && - MLXSW_CORE_RES_VALID(mlxsw_core, MAX_LAG_MEMBERS)) { - alloc_size = sizeof(*mlxsw_core->lag.mapping) * - MLXSW_CORE_RES_GET(mlxsw_core, MAX_LAG) * + err = mlxsw_core_max_lag(mlxsw_core, &max_lag); + if (!err && MLXSW_CORE_RES_VALID(mlxsw_core, MAX_LAG_MEMBERS)) { + alloc_size = sizeof(*mlxsw_core->lag.mapping) * max_lag * MLXSW_CORE_RES_GET(mlxsw_core, MAX_LAG_MEMBERS); mlxsw_core->lag.mapping = kzalloc(alloc_size, GFP_KERNEL); if (!mlxsw_core->lag.mapping) { @@ -2233,6 +2249,7 @@ err_ports_init: err_register_resources: mlxsw_bus->fini(bus_priv); err_bus_init: + mlxsw_core_irq_event_handler_fini(mlxsw_core); if (!reload) { devl_unlock(devlink); devlink_free(devlink); @@ -2302,6 +2319,7 @@ void mlxsw_core_bus_device_unregister(struct mlxsw_core *mlxsw_core, if (!reload) devl_resources_unregister(devlink); mlxsw_core->bus->fini(mlxsw_core->bus_priv); + mlxsw_core_irq_event_handler_fini(mlxsw_core); if (!reload) { devl_unlock(devlink); devlink_free(devlink); @@ -2772,6 +2790,57 @@ int mlxsw_reg_trans_bulk_wait(struct list_head *bulk_list) } EXPORT_SYMBOL(mlxsw_reg_trans_bulk_wait); +struct mlxsw_core_irq_event_handler_item { + struct list_head list; + void (*cb)(struct mlxsw_core *mlxsw_core); +}; + +int mlxsw_core_irq_event_handler_register(struct mlxsw_core *mlxsw_core, + mlxsw_irq_event_cb_t cb) +{ + struct mlxsw_core_irq_event_handler_item *item; + + item = kzalloc(sizeof(*item), GFP_KERNEL); + if (!item) + return -ENOMEM; + item->cb = cb; + mutex_lock(&mlxsw_core->irq_event_handler_lock); + list_add_tail(&item->list, &mlxsw_core->irq_event_handler_list); + mutex_unlock(&mlxsw_core->irq_event_handler_lock); + return 0; +} +EXPORT_SYMBOL(mlxsw_core_irq_event_handler_register); + +void mlxsw_core_irq_event_handler_unregister(struct mlxsw_core *mlxsw_core, + mlxsw_irq_event_cb_t cb) +{ + struct mlxsw_core_irq_event_handler_item *item, *tmp; + + mutex_lock(&mlxsw_core->irq_event_handler_lock); + list_for_each_entry_safe(item, tmp, + &mlxsw_core->irq_event_handler_list, list) { + if (item->cb == cb) { + list_del(&item->list); + kfree(item); + } + } + mutex_unlock(&mlxsw_core->irq_event_handler_lock); +} +EXPORT_SYMBOL(mlxsw_core_irq_event_handler_unregister); + +void mlxsw_core_irq_event_handlers_call(struct mlxsw_core *mlxsw_core) +{ + struct mlxsw_core_irq_event_handler_item *item; + + mutex_lock(&mlxsw_core->irq_event_handler_lock); + list_for_each_entry(item, &mlxsw_core->irq_event_handler_list, list) { + if (item->cb) + item->cb(mlxsw_core); + } + mutex_unlock(&mlxsw_core->irq_event_handler_lock); +} +EXPORT_SYMBOL(mlxsw_core_irq_event_handlers_call); + static int mlxsw_core_reg_access_cmd(struct mlxsw_core *mlxsw_core, const struct mlxsw_reg_info *reg, char *payload, @@ -3115,18 +3184,6 @@ void mlxsw_core_port_eth_set(struct mlxsw_core *mlxsw_core, u16 local_port, } EXPORT_SYMBOL(mlxsw_core_port_eth_set); -void mlxsw_core_port_ib_set(struct mlxsw_core *mlxsw_core, u16 local_port, - void *port_driver_priv) -{ - struct mlxsw_core_port *mlxsw_core_port = - &mlxsw_core->ports[local_port]; - struct devlink_port *devlink_port = &mlxsw_core_port->devlink_port; - - mlxsw_core_port->port_driver_priv = port_driver_priv; - devlink_port_type_ib_set(devlink_port, NULL); -} -EXPORT_SYMBOL(mlxsw_core_port_ib_set); - void mlxsw_core_port_clear(struct mlxsw_core *mlxsw_core, u16 local_port, void *port_driver_priv) { @@ -3139,18 +3196,6 @@ void mlxsw_core_port_clear(struct mlxsw_core *mlxsw_core, u16 local_port, } EXPORT_SYMBOL(mlxsw_core_port_clear); -enum devlink_port_type mlxsw_core_port_type_get(struct mlxsw_core *mlxsw_core, - u16 local_port) -{ - struct mlxsw_core_port *mlxsw_core_port = - &mlxsw_core->ports[local_port]; - struct devlink_port *devlink_port = &mlxsw_core_port->devlink_port; - - return devlink_port->type; -} -EXPORT_SYMBOL(mlxsw_core_port_type_get); - - struct devlink_port * mlxsw_core_port_devlink_port_get(struct mlxsw_core *mlxsw_core, u16 local_port) diff --git a/drivers/net/ethernet/mellanox/mlxsw/core.h b/drivers/net/ethernet/mellanox/mlxsw/core.h index 02d9cc2ef0c8..ca0c3d2bee6b 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core.h +++ b/drivers/net/ethernet/mellanox/mlxsw/core.h @@ -35,6 +35,8 @@ struct mlxsw_fw_rev; unsigned int mlxsw_core_max_ports(const struct mlxsw_core *mlxsw_core); +int mlxsw_core_max_lag(struct mlxsw_core *mlxsw_core, u16 *p_max_lag); + void *mlxsw_core_driver_priv(struct mlxsw_core *mlxsw_core); struct mlxsw_linecards *mlxsw_core_linecards(struct mlxsw_core *mlxsw_core); @@ -215,6 +217,14 @@ int mlxsw_reg_trans_write(struct mlxsw_core *mlxsw_core, mlxsw_reg_trans_cb_t *cb, unsigned long cb_priv); int mlxsw_reg_trans_bulk_wait(struct list_head *bulk_list); +typedef void mlxsw_irq_event_cb_t(struct mlxsw_core *mlxsw_core); + +int mlxsw_core_irq_event_handler_register(struct mlxsw_core *mlxsw_core, + mlxsw_irq_event_cb_t cb); +void mlxsw_core_irq_event_handler_unregister(struct mlxsw_core *mlxsw_core, + mlxsw_irq_event_cb_t cb); +void mlxsw_core_irq_event_handlers_call(struct mlxsw_core *mlxsw_core); + int mlxsw_reg_query(struct mlxsw_core *mlxsw_core, const struct mlxsw_reg_info *reg, char *payload); int mlxsw_reg_write(struct mlxsw_core *mlxsw_core, @@ -256,12 +266,8 @@ int mlxsw_core_cpu_port_init(struct mlxsw_core *mlxsw_core, void mlxsw_core_cpu_port_fini(struct mlxsw_core *mlxsw_core); void mlxsw_core_port_eth_set(struct mlxsw_core *mlxsw_core, u16 local_port, void *port_driver_priv, struct net_device *dev); -void mlxsw_core_port_ib_set(struct mlxsw_core *mlxsw_core, u16 local_port, - void *port_driver_priv); void mlxsw_core_port_clear(struct mlxsw_core *mlxsw_core, u16 local_port, void *port_driver_priv); -enum devlink_port_type mlxsw_core_port_type_get(struct mlxsw_core *mlxsw_core, - u16 local_port); struct devlink_port * mlxsw_core_port_devlink_port_get(struct mlxsw_core *mlxsw_core, u16 local_port); @@ -291,6 +297,7 @@ struct mlxsw_swid_config { struct mlxsw_config_profile { u16 used_max_vepa_channels:1, + used_max_lag:1, used_max_mid:1, used_max_pgt:1, used_max_system_port:1, @@ -306,6 +313,7 @@ struct mlxsw_config_profile { used_kvd_sizes:1, used_cqe_time_stamp_type:1; u8 max_vepa_channels; + u16 max_lag; u16 max_mid; u16 max_pgt; u16 max_system_port; @@ -341,8 +349,6 @@ struct mlxsw_driver { const struct mlxsw_bus_info *mlxsw_bus_info, struct netlink_ext_ack *extack); void (*fini)(struct mlxsw_core *mlxsw_core); - int (*port_type_set)(struct mlxsw_core *mlxsw_core, u16 local_port, - enum devlink_port_type new_type); int (*port_split)(struct mlxsw_core *mlxsw_core, u16 local_port, unsigned int count, struct netlink_ext_ack *extack); int (*port_unsplit)(struct mlxsw_core *mlxsw_core, u16 local_port, diff --git a/drivers/net/ethernet/mellanox/mlxsw/core_acl_flex_actions.c b/drivers/net/ethernet/mellanox/mlxsw/core_acl_flex_actions.c index 636db9a87457..9dfe7148199f 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core_acl_flex_actions.c +++ b/drivers/net/ethernet/mellanox/mlxsw/core_acl_flex_actions.c @@ -737,8 +737,9 @@ mlxsw_afa_cookie_create(struct mlxsw_afa *mlxsw_afa, if (!cookie) return ERR_PTR(-ENOMEM); refcount_set(&cookie->ref_count, 1); - memcpy(&cookie->fa_cookie, fa_cookie, - sizeof(*fa_cookie) + fa_cookie->cookie_len); + cookie->fa_cookie = *fa_cookie; + memcpy(cookie->fa_cookie.cookie, fa_cookie->cookie, + fa_cookie->cookie_len); err = rhashtable_insert_fast(&mlxsw_afa->cookie_ht, &cookie->ht_node, mlxsw_afa_cookie_ht_params); diff --git a/drivers/net/ethernet/mellanox/mlxsw/core_linecards.c b/drivers/net/ethernet/mellanox/mlxsw/core_linecards.c index ca59f0b946da..83d2dc91ba2c 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core_linecards.c +++ b/drivers/net/ethernet/mellanox/mlxsw/core_linecards.c @@ -785,6 +785,21 @@ static int mlxsw_linecard_status_get_and_process(struct mlxsw_core *mlxsw_core, return mlxsw_linecard_status_process(linecards, linecard, mddq_pl); } +static void mlxsw_linecards_irq_event_handler(struct mlxsw_core *mlxsw_core) +{ + struct mlxsw_linecards *linecards = mlxsw_core_linecards(mlxsw_core); + int i; + + /* Handle change of line card active state. */ + for (i = 0; i < linecards->count; i++) { + struct mlxsw_linecard *linecard = mlxsw_linecard_get(linecards, + i + 1); + + mlxsw_linecard_status_get_and_process(mlxsw_core, linecards, + linecard); + } +} + static const char * const mlxsw_linecard_status_event_type_name[] = { [MLXSW_LINECARD_STATUS_EVENT_TYPE_PROVISION] = "provision", [MLXSW_LINECARD_STATUS_EVENT_TYPE_UNPROVISION] = "unprovision", @@ -1238,7 +1253,6 @@ static int mlxsw_linecard_init(struct mlxsw_core *mlxsw_core, { struct devlink_linecard *devlink_linecard; struct mlxsw_linecard *linecard; - int err; linecard = mlxsw_linecard_get(linecards, slot_index); linecard->slot_index = slot_index; @@ -1248,17 +1262,45 @@ static int mlxsw_linecard_init(struct mlxsw_core *mlxsw_core, devlink_linecard = devlink_linecard_create(priv_to_devlink(mlxsw_core), slot_index, &mlxsw_linecard_ops, linecard); - if (IS_ERR(devlink_linecard)) { - err = PTR_ERR(devlink_linecard); - goto err_devlink_linecard_create; - } + if (IS_ERR(devlink_linecard)) + return PTR_ERR(devlink_linecard); + linecard->devlink_linecard = devlink_linecard; INIT_DELAYED_WORK(&linecard->status_event_to_dw, &mlxsw_linecard_status_event_to_work); + return 0; +} + +static void mlxsw_linecard_fini(struct mlxsw_core *mlxsw_core, + struct mlxsw_linecards *linecards, + u8 slot_index) +{ + struct mlxsw_linecard *linecard; + + linecard = mlxsw_linecard_get(linecards, slot_index); + cancel_delayed_work_sync(&linecard->status_event_to_dw); + /* Make sure all scheduled events are processed */ + mlxsw_core_flush_owq(); + if (linecard->active) + mlxsw_linecard_active_clear(linecard); + mlxsw_linecard_bdev_del(linecard); + devlink_linecard_destroy(linecard->devlink_linecard); + mutex_destroy(&linecard->lock); +} + +static int +mlxsw_linecard_event_delivery_init(struct mlxsw_core *mlxsw_core, + struct mlxsw_linecards *linecards, + u8 slot_index) +{ + struct mlxsw_linecard *linecard; + int err; + + linecard = mlxsw_linecard_get(linecards, slot_index); err = mlxsw_linecard_event_delivery_set(mlxsw_core, linecard, true); if (err) - goto err_event_delivery_set; + return err; err = mlxsw_linecard_status_get_and_process(mlxsw_core, linecards, linecard); @@ -1269,29 +1311,18 @@ static int mlxsw_linecard_init(struct mlxsw_core *mlxsw_core, err_status_get_and_process: mlxsw_linecard_event_delivery_set(mlxsw_core, linecard, false); -err_event_delivery_set: - devlink_linecard_destroy(linecard->devlink_linecard); -err_devlink_linecard_create: - mutex_destroy(&linecard->lock); return err; } -static void mlxsw_linecard_fini(struct mlxsw_core *mlxsw_core, - struct mlxsw_linecards *linecards, - u8 slot_index) +static void +mlxsw_linecard_event_delivery_fini(struct mlxsw_core *mlxsw_core, + struct mlxsw_linecards *linecards, + u8 slot_index) { struct mlxsw_linecard *linecard; linecard = mlxsw_linecard_get(linecards, slot_index); mlxsw_linecard_event_delivery_set(mlxsw_core, linecard, false); - cancel_delayed_work_sync(&linecard->status_event_to_dw); - /* Make sure all scheduled events are processed */ - mlxsw_core_flush_owq(); - if (linecard->active) - mlxsw_linecard_active_clear(linecard); - mlxsw_linecard_bdev_del(linecard); - devlink_linecard_destroy(linecard->devlink_linecard); - mutex_destroy(&linecard->lock); } /* LINECARDS INI BUNDLE FILE @@ -1505,6 +1536,11 @@ int mlxsw_linecards_init(struct mlxsw_core *mlxsw_core, if (err) goto err_traps_register; + err = mlxsw_core_irq_event_handler_register(mlxsw_core, + mlxsw_linecards_irq_event_handler); + if (err) + goto err_irq_event_handler_register; + mlxsw_core_linecards_set(mlxsw_core, linecards); for (i = 0; i < linecards->count; i++) { @@ -1513,11 +1549,25 @@ int mlxsw_linecards_init(struct mlxsw_core *mlxsw_core, goto err_linecard_init; } + for (i = 0; i < linecards->count; i++) { + err = mlxsw_linecard_event_delivery_init(mlxsw_core, linecards, + i + 1); + if (err) + goto err_linecard_event_delivery_init; + } + return 0; +err_linecard_event_delivery_init: + for (i--; i >= 0; i--) + mlxsw_linecard_event_delivery_fini(mlxsw_core, linecards, i + 1); + i = linecards->count; err_linecard_init: for (i--; i >= 0; i--) mlxsw_linecard_fini(mlxsw_core, linecards, i + 1); + mlxsw_core_irq_event_handler_unregister(mlxsw_core, + mlxsw_linecards_irq_event_handler); +err_irq_event_handler_register: mlxsw_core_traps_unregister(mlxsw_core, mlxsw_linecard_listener, ARRAY_SIZE(mlxsw_linecard_listener), mlxsw_core); @@ -1536,7 +1586,11 @@ void mlxsw_linecards_fini(struct mlxsw_core *mlxsw_core) if (!linecards) return; for (i = 0; i < linecards->count; i++) + mlxsw_linecard_event_delivery_fini(mlxsw_core, linecards, i + 1); + for (i = 0; i < linecards->count; i++) mlxsw_linecard_fini(mlxsw_core, linecards, i + 1); + mlxsw_core_irq_event_handler_unregister(mlxsw_core, + mlxsw_linecards_irq_event_handler); mlxsw_core_traps_unregister(mlxsw_core, mlxsw_linecard_listener, ARRAY_SIZE(mlxsw_linecard_listener), mlxsw_core); diff --git a/drivers/net/ethernet/mellanox/mlxsw/i2c.c b/drivers/net/ethernet/mellanox/mlxsw/i2c.c index ce843ea91464..716c73e4fd59 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/i2c.c +++ b/drivers/net/ethernet/mellanox/mlxsw/i2c.c @@ -9,6 +9,7 @@ #include <linux/mutex.h> #include <linux/module.h> #include <linux/mod_devicetable.h> +#include <linux/platform_data/mlxreg.h> #include <linux/slab.h> #include "cmd.h" @@ -51,6 +52,15 @@ #define MLXSW_I2C_TIMEOUT_MSECS 5000 #define MLXSW_I2C_MAX_DATA_SIZE 256 +/* Driver can be initialized by kernel platform driver or from the user + * space. In the first case IRQ line number is passed through the platform + * data, otherwise default IRQ line is to be used. Default IRQ is relevant + * only for specific I2C slave address, allowing 3.4 MHz I2C path to the chip + * (special hardware feature for I2C acceleration). + */ +#define MLXSW_I2C_DEFAULT_IRQ 17 +#define MLXSW_FAST_I2C_SLAVE 0x37 + /** * struct mlxsw_i2c - device private data: * @cmd: command attributes; @@ -63,6 +73,9 @@ * @core: switch core pointer; * @bus_info: bus info block; * @block_size: maximum block size allowed to pass to under layer; + * @pdata: device platform data; + * @irq_work: interrupts work item; + * @irq: IRQ line number; */ struct mlxsw_i2c { struct { @@ -76,6 +89,9 @@ struct mlxsw_i2c { struct mlxsw_core *core; struct mlxsw_bus_info bus_info; u16 block_size; + struct mlxreg_core_hotplug_platform_data *pdata; + struct work_struct irq_work; + int irq; }; #define MLXSW_I2C_READ_MSG(_client, _addr_buf, _buf, _len) { \ @@ -546,6 +562,67 @@ static void mlxsw_i2c_fini(void *bus_priv) mlxsw_i2c->core = NULL; } +static void mlxsw_i2c_work_handler(struct work_struct *work) +{ + struct mlxsw_i2c *mlxsw_i2c; + + mlxsw_i2c = container_of(work, struct mlxsw_i2c, irq_work); + mlxsw_core_irq_event_handlers_call(mlxsw_i2c->core); +} + +static irqreturn_t mlxsw_i2c_irq_handler(int irq, void *dev) +{ + struct mlxsw_i2c *mlxsw_i2c = dev; + + mlxsw_core_schedule_work(&mlxsw_i2c->irq_work); + + /* Interrupt handler shares IRQ line with 'main' interrupt handler. + * Return here IRQ_NONE, while main handler will return IRQ_HANDLED. + */ + return IRQ_NONE; +} + +static int mlxsw_i2c_irq_init(struct mlxsw_i2c *mlxsw_i2c, u8 addr) +{ + int err; + + /* Initialize interrupt handler if system hotplug driver is reachable, + * otherwise interrupt line is not enabled and interrupts will not be + * raised to CPU. Also request_irq() call will be not valid. + */ + if (!IS_REACHABLE(CONFIG_MLXREG_HOTPLUG)) + return 0; + + /* Set default interrupt line. */ + if (mlxsw_i2c->pdata && mlxsw_i2c->pdata->irq) + mlxsw_i2c->irq = mlxsw_i2c->pdata->irq; + else if (addr == MLXSW_FAST_I2C_SLAVE) + mlxsw_i2c->irq = MLXSW_I2C_DEFAULT_IRQ; + + if (!mlxsw_i2c->irq) + return 0; + + INIT_WORK(&mlxsw_i2c->irq_work, mlxsw_i2c_work_handler); + err = request_irq(mlxsw_i2c->irq, mlxsw_i2c_irq_handler, + IRQF_TRIGGER_FALLING | IRQF_SHARED, "mlxsw-i2c", + mlxsw_i2c); + if (err) { + dev_err(mlxsw_i2c->bus_info.dev, "Failed to request irq: %d\n", + err); + return err; + } + + return 0; +} + +static void mlxsw_i2c_irq_fini(struct mlxsw_i2c *mlxsw_i2c) +{ + if (!IS_REACHABLE(CONFIG_MLXREG_HOTPLUG) || !mlxsw_i2c->irq) + return; + cancel_work_sync(&mlxsw_i2c->irq_work); + free_irq(mlxsw_i2c->irq, mlxsw_i2c); +} + static const struct mlxsw_bus mlxsw_i2c_bus = { .kind = "i2c", .init = mlxsw_i2c_init, @@ -638,17 +715,24 @@ static int mlxsw_i2c_probe(struct i2c_client *client, mlxsw_i2c->bus_info.dev = &client->dev; mlxsw_i2c->bus_info.low_frequency = true; mlxsw_i2c->dev = &client->dev; + mlxsw_i2c->pdata = client->dev.platform_data; + + err = mlxsw_i2c_irq_init(mlxsw_i2c, client->addr); + if (err) + goto errout; err = mlxsw_core_bus_device_register(&mlxsw_i2c->bus_info, &mlxsw_i2c_bus, mlxsw_i2c, false, NULL, NULL); if (err) { dev_err(&client->dev, "Fail to register core bus\n"); - return err; + goto err_bus_device_register; } return 0; +err_bus_device_register: + mlxsw_i2c_irq_fini(mlxsw_i2c); errout: mutex_destroy(&mlxsw_i2c->cmd.lock); i2c_set_clientdata(client, NULL); @@ -661,6 +745,7 @@ static int mlxsw_i2c_remove(struct i2c_client *client) struct mlxsw_i2c *mlxsw_i2c = i2c_get_clientdata(client); mlxsw_core_bus_device_unregister(mlxsw_i2c->core, false); + mlxsw_i2c_irq_fini(mlxsw_i2c); mutex_destroy(&mlxsw_i2c->cmd.lock); return 0; diff --git a/drivers/net/ethernet/mellanox/mlxsw/minimal.c b/drivers/net/ethernet/mellanox/mlxsw/minimal.c index bb1cd4bae82e..55b3c42bb007 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/minimal.c +++ b/drivers/net/ethernet/mellanox/mlxsw/minimal.c @@ -26,20 +26,29 @@ static const struct mlxsw_fw_rev mlxsw_m_fw_rev = { struct mlxsw_m_port; +struct mlxsw_m_line_card { + bool active; + int module_to_port[]; +}; + struct mlxsw_m { struct mlxsw_m_port **ports; - int *module_to_port; struct mlxsw_core *core; const struct mlxsw_bus_info *bus_info; u8 base_mac[ETH_ALEN]; u8 max_ports; + u8 max_modules_per_slot; /* Maximum number of modules per-slot. */ + u8 num_of_slots; /* Including the main board. */ + struct mlxsw_m_line_card **line_cards; }; struct mlxsw_m_port { struct net_device *dev; struct mlxsw_m *mlxsw_m; u16 local_port; + u8 slot_index; u8 module; + u8 module_offset; }; static int mlxsw_m_base_mac_get(struct mlxsw_m *mlxsw_m) @@ -94,14 +103,14 @@ static void mlxsw_m_module_get_drvinfo(struct net_device *dev, struct mlxsw_m_port *mlxsw_m_port = netdev_priv(dev); struct mlxsw_m *mlxsw_m = mlxsw_m_port->mlxsw_m; - strlcpy(drvinfo->driver, mlxsw_m->bus_info->device_kind, + strscpy(drvinfo->driver, mlxsw_m->bus_info->device_kind, sizeof(drvinfo->driver)); snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version), "%d.%d.%d", mlxsw_m->bus_info->fw_rev.major, mlxsw_m->bus_info->fw_rev.minor, mlxsw_m->bus_info->fw_rev.subminor); - strlcpy(drvinfo->bus_info, mlxsw_m->bus_info->device_name, + strscpy(drvinfo->bus_info, mlxsw_m->bus_info->device_name, sizeof(drvinfo->bus_info)); } @@ -111,8 +120,9 @@ static int mlxsw_m_get_module_info(struct net_device *netdev, struct mlxsw_m_port *mlxsw_m_port = netdev_priv(netdev); struct mlxsw_core *core = mlxsw_m_port->mlxsw_m->core; - return mlxsw_env_get_module_info(netdev, core, 0, mlxsw_m_port->module, - modinfo); + return mlxsw_env_get_module_info(netdev, core, + mlxsw_m_port->slot_index, + mlxsw_m_port->module, modinfo); } static int @@ -122,7 +132,8 @@ mlxsw_m_get_module_eeprom(struct net_device *netdev, struct ethtool_eeprom *ee, struct mlxsw_m_port *mlxsw_m_port = netdev_priv(netdev); struct mlxsw_core *core = mlxsw_m_port->mlxsw_m->core; - return mlxsw_env_get_module_eeprom(netdev, core, 0, + return mlxsw_env_get_module_eeprom(netdev, core, + mlxsw_m_port->slot_index, mlxsw_m_port->module, ee, data); } @@ -134,7 +145,8 @@ mlxsw_m_get_module_eeprom_by_page(struct net_device *netdev, struct mlxsw_m_port *mlxsw_m_port = netdev_priv(netdev); struct mlxsw_core *core = mlxsw_m_port->mlxsw_m->core; - return mlxsw_env_get_module_eeprom_by_page(core, 0, + return mlxsw_env_get_module_eeprom_by_page(core, + mlxsw_m_port->slot_index, mlxsw_m_port->module, page, extack); } @@ -144,7 +156,8 @@ static int mlxsw_m_reset(struct net_device *netdev, u32 *flags) struct mlxsw_m_port *mlxsw_m_port = netdev_priv(netdev); struct mlxsw_core *core = mlxsw_m_port->mlxsw_m->core; - return mlxsw_env_reset_module(netdev, core, 0, mlxsw_m_port->module, + return mlxsw_env_reset_module(netdev, core, mlxsw_m_port->slot_index, + mlxsw_m_port->module, flags); } @@ -156,7 +169,8 @@ mlxsw_m_get_module_power_mode(struct net_device *netdev, struct mlxsw_m_port *mlxsw_m_port = netdev_priv(netdev); struct mlxsw_core *core = mlxsw_m_port->mlxsw_m->core; - return mlxsw_env_get_module_power_mode(core, 0, mlxsw_m_port->module, + return mlxsw_env_get_module_power_mode(core, mlxsw_m_port->slot_index, + mlxsw_m_port->module, params, extack); } @@ -168,7 +182,8 @@ mlxsw_m_set_module_power_mode(struct net_device *netdev, struct mlxsw_m_port *mlxsw_m_port = netdev_priv(netdev); struct mlxsw_core *core = mlxsw_m_port->mlxsw_m->core; - return mlxsw_env_set_module_power_mode(core, 0, mlxsw_m_port->module, + return mlxsw_env_set_module_power_mode(core, mlxsw_m_port->slot_index, + mlxsw_m_port->module, params->policy, extack); } @@ -184,7 +199,7 @@ static const struct ethtool_ops mlxsw_m_port_ethtool_ops = { static int mlxsw_m_port_module_info_get(struct mlxsw_m *mlxsw_m, u16 local_port, - u8 *p_module, u8 *p_width) + u8 *p_module, u8 *p_width, u8 *p_slot_index) { char pmlp_pl[MLXSW_REG_PMLP_LEN]; int err; @@ -195,6 +210,7 @@ mlxsw_m_port_module_info_get(struct mlxsw_m *mlxsw_m, u16 local_port, return err; *p_module = mlxsw_reg_pmlp_module_get(pmlp_pl, 0); *p_width = mlxsw_reg_pmlp_width_get(pmlp_pl); + *p_slot_index = mlxsw_reg_pmlp_slot_index_get(pmlp_pl, 0); return 0; } @@ -212,18 +228,25 @@ mlxsw_m_port_dev_addr_get(struct mlxsw_m_port *mlxsw_m_port) if (err) return err; mlxsw_reg_ppad_mac_memcpy_from(ppad_pl, addr); - eth_hw_addr_gen(mlxsw_m_port->dev, addr, mlxsw_m_port->module + 1); + eth_hw_addr_gen(mlxsw_m_port->dev, addr, mlxsw_m_port->module + 1 + + mlxsw_m_port->module_offset); return 0; } +static bool mlxsw_m_port_created(struct mlxsw_m *mlxsw_m, u16 local_port) +{ + return mlxsw_m->ports[local_port]; +} + static int -mlxsw_m_port_create(struct mlxsw_m *mlxsw_m, u16 local_port, u8 module) +mlxsw_m_port_create(struct mlxsw_m *mlxsw_m, u16 local_port, u8 slot_index, + u8 module) { struct mlxsw_m_port *mlxsw_m_port; struct net_device *dev; int err; - err = mlxsw_core_port_init(mlxsw_m->core, local_port, 0, + err = mlxsw_core_port_init(mlxsw_m->core, local_port, slot_index, module + 1, false, 0, false, 0, mlxsw_m->base_mac, sizeof(mlxsw_m->base_mac)); @@ -246,6 +269,15 @@ mlxsw_m_port_create(struct mlxsw_m *mlxsw_m, u16 local_port, u8 module) mlxsw_m_port->mlxsw_m = mlxsw_m; mlxsw_m_port->local_port = local_port; mlxsw_m_port->module = module; + mlxsw_m_port->slot_index = slot_index; + /* Add module offset for line card. Offset for main board iz zero. + * For line card in slot #n offset is calculated as (#n - 1) + * multiplied by maximum modules number, which could be found on a line + * card. + */ + mlxsw_m_port->module_offset = mlxsw_m_port->slot_index ? + (mlxsw_m_port->slot_index - 1) * + mlxsw_m->max_modules_per_slot : 0; dev->netdev_ops = &mlxsw_m_port_netdev_ops; dev->ethtool_ops = &mlxsw_m_port_ethtool_ops; @@ -291,19 +323,29 @@ static void mlxsw_m_port_remove(struct mlxsw_m *mlxsw_m, u16 local_port) mlxsw_core_port_fini(mlxsw_m->core, local_port); } +static int* +mlxsw_m_port_mapping_get(struct mlxsw_m *mlxsw_m, u8 slot_index, u8 module) +{ + return &mlxsw_m->line_cards[slot_index]->module_to_port[module]; +} + static int mlxsw_m_port_module_map(struct mlxsw_m *mlxsw_m, u16 local_port, u8 *last_module) { unsigned int max_ports = mlxsw_core_max_ports(mlxsw_m->core); - u8 module, width; + u8 module, width, slot_index; + int *module_to_port; int err; /* Fill out to local port mapping array */ err = mlxsw_m_port_module_info_get(mlxsw_m, local_port, &module, - &width); + &width, &slot_index); if (err) return err; + /* Skip if line card has been already configured */ + if (mlxsw_m->line_cards[slot_index]->active) + return 0; if (!width) return 0; /* Skip, if port belongs to the cluster */ @@ -313,91 +355,220 @@ static int mlxsw_m_port_module_map(struct mlxsw_m *mlxsw_m, u16 local_port, if (WARN_ON_ONCE(module >= max_ports)) return -EINVAL; - mlxsw_env_module_port_map(mlxsw_m->core, 0, module); - mlxsw_m->module_to_port[module] = ++mlxsw_m->max_ports; + mlxsw_env_module_port_map(mlxsw_m->core, slot_index, module); + module_to_port = mlxsw_m_port_mapping_get(mlxsw_m, slot_index, module); + *module_to_port = local_port; return 0; } -static void mlxsw_m_port_module_unmap(struct mlxsw_m *mlxsw_m, u8 module) +static void +mlxsw_m_port_module_unmap(struct mlxsw_m *mlxsw_m, u8 slot_index, u8 module) { - mlxsw_m->module_to_port[module] = -1; - mlxsw_env_module_port_unmap(mlxsw_m->core, 0, module); + int *module_to_port = mlxsw_m_port_mapping_get(mlxsw_m, slot_index, + module); + *module_to_port = -1; + mlxsw_env_module_port_unmap(mlxsw_m->core, slot_index, module); } -static int mlxsw_m_ports_create(struct mlxsw_m *mlxsw_m) +static int mlxsw_m_linecards_init(struct mlxsw_m *mlxsw_m) { unsigned int max_ports = mlxsw_core_max_ports(mlxsw_m->core); - u8 last_module = max_ports; - int i; - int err; + char mgpir_pl[MLXSW_REG_MGPIR_LEN]; + u8 num_of_modules; + int i, j, err; + + mlxsw_reg_mgpir_pack(mgpir_pl, 0); + err = mlxsw_reg_query(mlxsw_m->core, MLXSW_REG(mgpir), mgpir_pl); + if (err) + return err; + + mlxsw_reg_mgpir_unpack(mgpir_pl, NULL, NULL, NULL, &num_of_modules, + &mlxsw_m->num_of_slots); + /* If the system is modular, get the maximum number of modules per-slot. + * Otherwise, get the maximum number of modules on the main board. + */ + if (mlxsw_m->num_of_slots) + mlxsw_m->max_modules_per_slot = + mlxsw_reg_mgpir_max_modules_per_slot_get(mgpir_pl); + else + mlxsw_m->max_modules_per_slot = num_of_modules; + /* Add slot for main board. */ + mlxsw_m->num_of_slots += 1; mlxsw_m->ports = kcalloc(max_ports, sizeof(*mlxsw_m->ports), GFP_KERNEL); if (!mlxsw_m->ports) return -ENOMEM; - mlxsw_m->module_to_port = kmalloc_array(max_ports, sizeof(int), - GFP_KERNEL); - if (!mlxsw_m->module_to_port) { + mlxsw_m->line_cards = kcalloc(mlxsw_m->num_of_slots, + sizeof(*mlxsw_m->line_cards), + GFP_KERNEL); + if (!mlxsw_m->line_cards) { err = -ENOMEM; - goto err_module_to_port_alloc; + goto err_kcalloc; } - /* Invalidate the entries of module to local port mapping array */ - for (i = 0; i < max_ports; i++) - mlxsw_m->module_to_port[i] = -1; + for (i = 0; i < mlxsw_m->num_of_slots; i++) { + mlxsw_m->line_cards[i] = + kzalloc(struct_size(mlxsw_m->line_cards[i], + module_to_port, + mlxsw_m->max_modules_per_slot), + GFP_KERNEL); + if (!mlxsw_m->line_cards[i]) { + err = -ENOMEM; + goto err_kmalloc_array; + } - /* Fill out module to local port mapping array */ - for (i = 1; i < max_ports; i++) { - err = mlxsw_m_port_module_map(mlxsw_m, i, &last_module); - if (err) - goto err_module_to_port_map; + /* Invalidate the entries of module to local port mapping array. */ + for (j = 0; j < mlxsw_m->max_modules_per_slot; j++) + mlxsw_m->line_cards[i]->module_to_port[j] = -1; } - /* Create port objects for each valid entry */ - for (i = 0; i < mlxsw_m->max_ports; i++) { - if (mlxsw_m->module_to_port[i] > 0) { - err = mlxsw_m_port_create(mlxsw_m, - mlxsw_m->module_to_port[i], - i); + return 0; + +err_kmalloc_array: + for (i--; i >= 0; i--) + kfree(mlxsw_m->line_cards[i]); +err_kcalloc: + kfree(mlxsw_m->ports); + return err; +} + +static void mlxsw_m_linecards_fini(struct mlxsw_m *mlxsw_m) +{ + int i = mlxsw_m->num_of_slots; + + for (i--; i >= 0; i--) + kfree(mlxsw_m->line_cards[i]); + kfree(mlxsw_m->line_cards); + kfree(mlxsw_m->ports); +} + +static void +mlxsw_m_linecard_port_module_unmap(struct mlxsw_m *mlxsw_m, u8 slot_index) +{ + int i; + + for (i = mlxsw_m->max_modules_per_slot - 1; i >= 0; i--) { + int *module_to_port; + + module_to_port = mlxsw_m_port_mapping_get(mlxsw_m, slot_index, i); + if (*module_to_port > 0) + mlxsw_m_port_module_unmap(mlxsw_m, slot_index, i); + } +} + +static int +mlxsw_m_linecard_ports_create(struct mlxsw_m *mlxsw_m, u8 slot_index) +{ + int *module_to_port; + int i, err; + + for (i = 0; i < mlxsw_m->max_modules_per_slot; i++) { + module_to_port = mlxsw_m_port_mapping_get(mlxsw_m, slot_index, i); + if (*module_to_port > 0) { + err = mlxsw_m_port_create(mlxsw_m, *module_to_port, + slot_index, i); if (err) - goto err_module_to_port_create; + goto err_port_create; + /* Mark slot as active */ + if (!mlxsw_m->line_cards[slot_index]->active) + mlxsw_m->line_cards[slot_index]->active = true; } } - return 0; -err_module_to_port_create: +err_port_create: for (i--; i >= 0; i--) { - if (mlxsw_m->module_to_port[i] > 0) - mlxsw_m_port_remove(mlxsw_m, - mlxsw_m->module_to_port[i]); + module_to_port = mlxsw_m_port_mapping_get(mlxsw_m, slot_index, i); + if (*module_to_port > 0 && + mlxsw_m_port_created(mlxsw_m, *module_to_port)) { + mlxsw_m_port_remove(mlxsw_m, *module_to_port); + /* Mark slot as inactive */ + if (mlxsw_m->line_cards[slot_index]->active) + mlxsw_m->line_cards[slot_index]->active = false; + } } - i = max_ports; -err_module_to_port_map: - for (i--; i > 0; i--) - mlxsw_m_port_module_unmap(mlxsw_m, i); - kfree(mlxsw_m->module_to_port); -err_module_to_port_alloc: - kfree(mlxsw_m->ports); return err; } -static void mlxsw_m_ports_remove(struct mlxsw_m *mlxsw_m) +static void +mlxsw_m_linecard_ports_remove(struct mlxsw_m *mlxsw_m, u8 slot_index) { int i; - for (i = 0; i < mlxsw_m->max_ports; i++) { - if (mlxsw_m->module_to_port[i] > 0) { - mlxsw_m_port_remove(mlxsw_m, - mlxsw_m->module_to_port[i]); - mlxsw_m_port_module_unmap(mlxsw_m, i); + for (i = 0; i < mlxsw_m->max_modules_per_slot; i++) { + int *module_to_port = mlxsw_m_port_mapping_get(mlxsw_m, + slot_index, i); + + if (*module_to_port > 0 && + mlxsw_m_port_created(mlxsw_m, *module_to_port)) { + mlxsw_m_port_remove(mlxsw_m, *module_to_port); + mlxsw_m_port_module_unmap(mlxsw_m, slot_index, i); } } +} - kfree(mlxsw_m->module_to_port); - kfree(mlxsw_m->ports); +static int mlxsw_m_ports_module_map(struct mlxsw_m *mlxsw_m) +{ + unsigned int max_ports = mlxsw_core_max_ports(mlxsw_m->core); + u8 last_module = max_ports; + int i, err; + + for (i = 1; i < max_ports; i++) { + err = mlxsw_m_port_module_map(mlxsw_m, i, &last_module); + if (err) + return err; + } + + return 0; +} + +static int mlxsw_m_ports_create(struct mlxsw_m *mlxsw_m) +{ + int err; + + /* Fill out module to local port mapping array */ + err = mlxsw_m_ports_module_map(mlxsw_m); + if (err) + goto err_ports_module_map; + + /* Create port objects for each valid entry */ + err = mlxsw_m_linecard_ports_create(mlxsw_m, 0); + if (err) + goto err_linecard_ports_create; + + return 0; + +err_linecard_ports_create: +err_ports_module_map: + mlxsw_m_linecard_port_module_unmap(mlxsw_m, 0); + + return err; +} + +static void mlxsw_m_ports_remove(struct mlxsw_m *mlxsw_m) +{ + mlxsw_m_linecard_ports_remove(mlxsw_m, 0); +} + +static void +mlxsw_m_ports_remove_selected(struct mlxsw_core *mlxsw_core, + bool (*selector)(void *priv, u16 local_port), + void *priv) +{ + struct mlxsw_m *mlxsw_m = mlxsw_core_driver_priv(mlxsw_core); + struct mlxsw_linecard *linecard_priv = priv; + struct mlxsw_m_line_card *linecard; + + linecard = mlxsw_m->line_cards[linecard_priv->slot_index]; + + if (WARN_ON(!linecard->active)) + return; + + mlxsw_m_linecard_ports_remove(mlxsw_m, linecard_priv->slot_index); + linecard->active = false; } static int mlxsw_m_fw_rev_validate(struct mlxsw_m *mlxsw_m) @@ -418,6 +589,60 @@ static int mlxsw_m_fw_rev_validate(struct mlxsw_m *mlxsw_m) return -EINVAL; } +static void +mlxsw_m_got_active(struct mlxsw_core *mlxsw_core, u8 slot_index, void *priv) +{ + struct mlxsw_m_line_card *linecard; + struct mlxsw_m *mlxsw_m = priv; + int err; + + linecard = mlxsw_m->line_cards[slot_index]; + /* Skip if line card has been already configured during init */ + if (linecard->active) + return; + + /* Fill out module to local port mapping array */ + err = mlxsw_m_ports_module_map(mlxsw_m); + if (err) + goto err_ports_module_map; + + /* Create port objects for each valid entry */ + err = mlxsw_m_linecard_ports_create(mlxsw_m, slot_index); + if (err) { + dev_err(mlxsw_m->bus_info->dev, "Failed to create port for line card at slot %d\n", + slot_index); + goto err_linecard_ports_create; + } + + linecard->active = true; + + return; + +err_linecard_ports_create: +err_ports_module_map: + mlxsw_m_linecard_port_module_unmap(mlxsw_m, slot_index); +} + +static void +mlxsw_m_got_inactive(struct mlxsw_core *mlxsw_core, u8 slot_index, void *priv) +{ + struct mlxsw_m_line_card *linecard; + struct mlxsw_m *mlxsw_m = priv; + + linecard = mlxsw_m->line_cards[slot_index]; + + if (WARN_ON(!linecard->active)) + return; + + mlxsw_m_linecard_ports_remove(mlxsw_m, slot_index); + linecard->active = false; +} + +static struct mlxsw_linecards_event_ops mlxsw_m_event_ops = { + .got_active = mlxsw_m_got_active, + .got_inactive = mlxsw_m_got_inactive, +}; + static int mlxsw_m_init(struct mlxsw_core *mlxsw_core, const struct mlxsw_bus_info *mlxsw_bus_info, struct netlink_ext_ack *extack) @@ -438,13 +663,33 @@ static int mlxsw_m_init(struct mlxsw_core *mlxsw_core, return err; } + err = mlxsw_m_linecards_init(mlxsw_m); + if (err) { + dev_err(mlxsw_m->bus_info->dev, "Failed to create line cards\n"); + return err; + } + + err = mlxsw_linecards_event_ops_register(mlxsw_core, + &mlxsw_m_event_ops, mlxsw_m); + if (err) { + dev_err(mlxsw_m->bus_info->dev, "Failed to register line cards operations\n"); + goto linecards_event_ops_register; + } + err = mlxsw_m_ports_create(mlxsw_m); if (err) { dev_err(mlxsw_m->bus_info->dev, "Failed to create ports\n"); - return err; + goto err_ports_create; } return 0; + +err_ports_create: + mlxsw_linecards_event_ops_unregister(mlxsw_core, + &mlxsw_m_event_ops, mlxsw_m); +linecards_event_ops_register: + mlxsw_m_linecards_fini(mlxsw_m); + return err; } static void mlxsw_m_fini(struct mlxsw_core *mlxsw_core) @@ -452,6 +697,9 @@ static void mlxsw_m_fini(struct mlxsw_core *mlxsw_core) struct mlxsw_m *mlxsw_m = mlxsw_core_driver_priv(mlxsw_core); mlxsw_m_ports_remove(mlxsw_m); + mlxsw_linecards_event_ops_unregister(mlxsw_core, + &mlxsw_m_event_ops, mlxsw_m); + mlxsw_m_linecards_fini(mlxsw_m); } static const struct mlxsw_config_profile mlxsw_m_config_profile; @@ -461,6 +709,7 @@ static struct mlxsw_driver mlxsw_m_driver = { .priv_size = sizeof(struct mlxsw_m), .init = mlxsw_m_init, .fini = mlxsw_m_fini, + .ports_remove_selected = mlxsw_m_ports_remove_selected, .profile = &mlxsw_m_config_profile, }; diff --git a/drivers/net/ethernet/mellanox/mlxsw/pci.c b/drivers/net/ethernet/mellanox/mlxsw/pci.c index 50527adc5b5a..c968309657dd 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/pci.c +++ b/drivers/net/ethernet/mellanox/mlxsw/pci.c @@ -1187,6 +1187,11 @@ static int mlxsw_pci_config_profile(struct mlxsw_pci *mlxsw_pci, char *mbox, mlxsw_cmd_mbox_config_profile_max_vepa_channels_set( mbox, profile->max_vepa_channels); } + if (profile->used_max_lag) { + mlxsw_cmd_mbox_config_profile_set_max_lag_set(mbox, 1); + mlxsw_cmd_mbox_config_profile_max_lag_set(mbox, + profile->max_lag); + } if (profile->used_max_mid) { mlxsw_cmd_mbox_config_profile_set_max_mid_set( mbox, 1); diff --git a/drivers/net/ethernet/mellanox/mlxsw/reg.h b/drivers/net/ethernet/mellanox/mlxsw/reg.h index f27bdecdf952..0777bed5bb1a 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/reg.h +++ b/drivers/net/ethernet/mellanox/mlxsw/reg.h @@ -2218,76 +2218,6 @@ static inline void mlxsw_reg_smpe_pack(char *payload, u16 local_port, mlxsw_reg_smpe_evid_set(payload, evid); } -/* SFTR-V2 - Switch Flooding Table Version 2 Register - * -------------------------------------------------- - * The switch flooding table is used for flooding packet replication. The table - * defines a bit mask of ports for packet replication. - */ -#define MLXSW_REG_SFTR2_ID 0x202F -#define MLXSW_REG_SFTR2_LEN 0x120 - -MLXSW_REG_DEFINE(sftr2, MLXSW_REG_SFTR2_ID, MLXSW_REG_SFTR2_LEN); - -/* reg_sftr2_swid - * Switch partition ID with which to associate the port. - * Access: Index - */ -MLXSW_ITEM32(reg, sftr2, swid, 0x00, 24, 8); - -/* reg_sftr2_flood_table - * Flooding table index to associate with the specific type on the specific - * switch partition. - * Access: Index - */ -MLXSW_ITEM32(reg, sftr2, flood_table, 0x00, 16, 6); - -/* reg_sftr2_index - * Index. Used as an index into the Flooding Table in case the table is - * configured to use VID / FID or FID Offset. - * Access: Index - */ -MLXSW_ITEM32(reg, sftr2, index, 0x00, 0, 16); - -/* reg_sftr2_table_type - * See mlxsw_flood_table_type - * Access: RW - */ -MLXSW_ITEM32(reg, sftr2, table_type, 0x04, 16, 3); - -/* reg_sftr2_range - * Range of entries to update - * Access: Index - */ -MLXSW_ITEM32(reg, sftr2, range, 0x04, 0, 16); - -/* reg_sftr2_port - * Local port membership (1 bit per port). - * Access: RW - */ -MLXSW_ITEM_BIT_ARRAY(reg, sftr2, port, 0x20, 0x80, 1); - -/* reg_sftr2_port_mask - * Local port mask (1 bit per port). - * Access: WO - */ -MLXSW_ITEM_BIT_ARRAY(reg, sftr2, port_mask, 0xA0, 0x80, 1); - -static inline void mlxsw_reg_sftr2_pack(char *payload, - unsigned int flood_table, - unsigned int index, - enum mlxsw_flood_table_type table_type, - unsigned int range, u16 port, bool set) -{ - MLXSW_REG_ZERO(sftr2, payload); - mlxsw_reg_sftr2_swid_set(payload, 0); - mlxsw_reg_sftr2_flood_table_set(payload, flood_table); - mlxsw_reg_sftr2_index_set(payload, index); - mlxsw_reg_sftr2_table_type_set(payload, table_type); - mlxsw_reg_sftr2_range_set(payload, range); - mlxsw_reg_sftr2_port_set(payload, port, set); - mlxsw_reg_sftr2_port_mask_set(payload, port, 1); -} - /* SMID-V2 - Switch Multicast ID Version 2 Register * ------------------------------------------------ * The MID record maps from a MID (Multicast ID), which is a unique identifier @@ -4729,25 +4659,6 @@ MLXSW_ITEM32(reg, ptys, ext_eth_proto_cap, 0x08, 0, 32); */ MLXSW_ITEM32(reg, ptys, eth_proto_cap, 0x0C, 0, 32); -/* reg_ptys_ib_link_width_cap - * IB port supported widths. - * Access: RO - */ -MLXSW_ITEM32(reg, ptys, ib_link_width_cap, 0x10, 16, 16); - -#define MLXSW_REG_PTYS_IB_SPEED_SDR BIT(0) -#define MLXSW_REG_PTYS_IB_SPEED_DDR BIT(1) -#define MLXSW_REG_PTYS_IB_SPEED_QDR BIT(2) -#define MLXSW_REG_PTYS_IB_SPEED_FDR10 BIT(3) -#define MLXSW_REG_PTYS_IB_SPEED_FDR BIT(4) -#define MLXSW_REG_PTYS_IB_SPEED_EDR BIT(5) - -/* reg_ptys_ib_proto_cap - * IB port supported speeds and protocols. - * Access: RO - */ -MLXSW_ITEM32(reg, ptys, ib_proto_cap, 0x10, 0, 16); - /* reg_ptys_ext_eth_proto_admin * Extended speed and protocol to set port to. * Access: RW @@ -4760,18 +4671,6 @@ MLXSW_ITEM32(reg, ptys, ext_eth_proto_admin, 0x14, 0, 32); */ MLXSW_ITEM32(reg, ptys, eth_proto_admin, 0x18, 0, 32); -/* reg_ptys_ib_link_width_admin - * IB width to set port to. - * Access: RW - */ -MLXSW_ITEM32(reg, ptys, ib_link_width_admin, 0x1C, 16, 16); - -/* reg_ptys_ib_proto_admin - * IB speeds and protocols to set port to. - * Access: RW - */ -MLXSW_ITEM32(reg, ptys, ib_proto_admin, 0x1C, 0, 16); - /* reg_ptys_ext_eth_proto_oper * The extended current speed and protocol configured for the port. * Access: RO @@ -4784,18 +4683,6 @@ MLXSW_ITEM32(reg, ptys, ext_eth_proto_oper, 0x20, 0, 32); */ MLXSW_ITEM32(reg, ptys, eth_proto_oper, 0x24, 0, 32); -/* reg_ptys_ib_link_width_oper - * The current IB width to set port to. - * Access: RO - */ -MLXSW_ITEM32(reg, ptys, ib_link_width_oper, 0x28, 16, 16); - -/* reg_ptys_ib_proto_oper - * The current IB speed and protocol. - * Access: RO - */ -MLXSW_ITEM32(reg, ptys, ib_proto_oper, 0x28, 0, 16); - enum mlxsw_reg_ptys_connector_type { MLXSW_REG_PTYS_CONNECTOR_TYPE_UNKNOWN_OR_NO_CONNECTOR, MLXSW_REG_PTYS_CONNECTOR_TYPE_PORT_NONE, @@ -4866,33 +4753,6 @@ static inline void mlxsw_reg_ptys_ext_eth_unpack(char *payload, mlxsw_reg_ptys_ext_eth_proto_oper_get(payload); } -static inline void mlxsw_reg_ptys_ib_pack(char *payload, u16 local_port, - u16 proto_admin, u16 link_width) -{ - MLXSW_REG_ZERO(ptys, payload); - mlxsw_reg_ptys_local_port_set(payload, local_port); - mlxsw_reg_ptys_proto_mask_set(payload, MLXSW_REG_PTYS_PROTO_MASK_IB); - mlxsw_reg_ptys_ib_proto_admin_set(payload, proto_admin); - mlxsw_reg_ptys_ib_link_width_admin_set(payload, link_width); -} - -static inline void mlxsw_reg_ptys_ib_unpack(char *payload, u16 *p_ib_proto_cap, - u16 *p_ib_link_width_cap, - u16 *p_ib_proto_oper, - u16 *p_ib_link_width_oper) -{ - if (p_ib_proto_cap) - *p_ib_proto_cap = mlxsw_reg_ptys_ib_proto_cap_get(payload); - if (p_ib_link_width_cap) - *p_ib_link_width_cap = - mlxsw_reg_ptys_ib_link_width_cap_get(payload); - if (p_ib_proto_oper) - *p_ib_proto_oper = mlxsw_reg_ptys_ib_proto_oper_get(payload); - if (p_ib_link_width_oper) - *p_ib_link_width_oper = - mlxsw_reg_ptys_ib_link_width_oper_get(payload); -} - /* PPAD - Port Physical Address Register * ------------------------------------- * The PPAD register configures the per port physical MAC address. @@ -5666,27 +5526,6 @@ static inline void mlxsw_reg_ppcnt_pack(char *payload, u16 local_port, mlxsw_reg_ppcnt_prio_tc_set(payload, prio_tc); } -/* PLIB - Port Local to InfiniBand Port - * ------------------------------------ - * The PLIB register performs mapping from Local Port into InfiniBand Port. - */ -#define MLXSW_REG_PLIB_ID 0x500A -#define MLXSW_REG_PLIB_LEN 0x10 - -MLXSW_REG_DEFINE(plib, MLXSW_REG_PLIB_ID, MLXSW_REG_PLIB_LEN); - -/* reg_plib_local_port - * Local port number. - * Access: Index - */ -MLXSW_ITEM32_LP(reg, plib, 0x00, 16, 0x00, 12); - -/* reg_plib_ib_port - * InfiniBand port remapping for local_port. - * Access: RW - */ -MLXSW_ITEM32(reg, plib, ib_port, 0x00, 0, 8); - /* PPTB - Port Prio To Buffer Register * ----------------------------------- * Configures the switch priority to buffer table. @@ -12924,7 +12763,6 @@ static const struct mlxsw_reg_info *mlxsw_reg_infos[] = { MLXSW_REG(spvc), MLXSW_REG(spevet), MLXSW_REG(smpe), - MLXSW_REG(sftr2), MLXSW_REG(smid2), MLXSW_REG(cwtp), MLXSW_REG(cwtpm), @@ -12962,7 +12800,6 @@ static const struct mlxsw_reg_info *mlxsw_reg_infos[] = { MLXSW_REG(paos), MLXSW_REG(pfcc), MLXSW_REG(ppcnt), - MLXSW_REG(plib), MLXSW_REG(pptb), MLXSW_REG(pbmc), MLXSW_REG(pspa), diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index 30c7b0e15721..5bcf5bceff71 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -2691,6 +2691,7 @@ static void mlxsw_sp_traps_fini(struct mlxsw_sp *mlxsw_sp) static int mlxsw_sp_lag_init(struct mlxsw_sp *mlxsw_sp) { char slcr_pl[MLXSW_REG_SLCR_LEN]; + u16 max_lag; u32 seed; int err; @@ -2709,12 +2710,14 @@ static int mlxsw_sp_lag_init(struct mlxsw_sp *mlxsw_sp) if (err) return err; - if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, MAX_LAG) || - !MLXSW_CORE_RES_VALID(mlxsw_sp->core, MAX_LAG_MEMBERS)) + err = mlxsw_core_max_lag(mlxsw_sp->core, &max_lag); + if (err) + return err; + + if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, MAX_LAG_MEMBERS)) return -EIO; - mlxsw_sp->lags = kcalloc(MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_LAG), - sizeof(struct mlxsw_sp_upper), + mlxsw_sp->lags = kcalloc(max_lag, sizeof(struct mlxsw_sp_upper), GFP_KERNEL); if (!mlxsw_sp->lags) return -ENOMEM; @@ -3509,6 +3512,33 @@ static const struct mlxsw_config_profile mlxsw_sp2_config_profile = { .cqe_time_stamp_type = MLXSW_CMD_MBOX_CONFIG_PROFILE_CQE_TIME_STAMP_TYPE_UTC, }; +/* Reduce number of LAGs from full capacity (256) to the maximum supported LAGs + * in Spectrum-2/3, to avoid regression in number of free entries in the PGT + * table. + */ +#define MLXSW_SP4_CONFIG_PROFILE_MAX_LAG 128 + +static const struct mlxsw_config_profile mlxsw_sp4_config_profile = { + .used_max_lag = 1, + .max_lag = MLXSW_SP4_CONFIG_PROFILE_MAX_LAG, + .used_flood_mode = 1, + .flood_mode = MLXSW_CMD_MBOX_CONFIG_PROFILE_FLOOD_MODE_CONTROLLED, + .used_max_ib_mc = 1, + .max_ib_mc = 0, + .used_max_pkey = 1, + .max_pkey = 0, + .used_ubridge = 1, + .ubridge = 1, + .swid_config = { + { + .used_type = 1, + .type = MLXSW_PORT_SWID_TYPE_ETH, + } + }, + .used_cqe_time_stamp_type = 1, + .cqe_time_stamp_type = MLXSW_CMD_MBOX_CONFIG_PROFILE_CQE_TIME_STAMP_TYPE_UTC, +}; + static void mlxsw_sp_resource_size_params_prepare(struct mlxsw_core *mlxsw_core, struct devlink_resource_size_params *kvd_size_params, @@ -4039,7 +4069,7 @@ static struct mlxsw_driver mlxsw_sp4_driver = { .params_unregister = mlxsw_sp2_params_unregister, .ptp_transmitted = mlxsw_sp_ptp_transmitted, .txhdr_len = MLXSW_TXHDR_LEN, - .profile = &mlxsw_sp2_config_profile, + .profile = &mlxsw_sp4_config_profile, .sdq_supports_cqe_v2 = true, }; @@ -4263,10 +4293,13 @@ static int mlxsw_sp_lag_index_get(struct mlxsw_sp *mlxsw_sp, { struct mlxsw_sp_upper *lag; int free_lag_id = -1; - u64 max_lag; - int i; + u16 max_lag; + int err, i; + + err = mlxsw_core_max_lag(mlxsw_sp->core, &max_lag); + if (err) + return err; - max_lag = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_LAG); for (i = 0; i < max_lag; i++) { lag = mlxsw_sp_lag_get(mlxsw_sp, i); if (lag->ref_count) { diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_ethtool.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_ethtool.c index 915dffb85a1c..dcd79d7e2af4 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_ethtool.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_ethtool.c @@ -14,16 +14,16 @@ static void mlxsw_sp_port_get_drvinfo(struct net_device *dev, struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; - strlcpy(drvinfo->driver, mlxsw_sp->bus_info->device_kind, + strscpy(drvinfo->driver, mlxsw_sp->bus_info->device_kind, sizeof(drvinfo->driver)); - strlcpy(drvinfo->version, mlxsw_sp_driver_version, + strscpy(drvinfo->version, mlxsw_sp_driver_version, sizeof(drvinfo->version)); snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version), "%d.%d.%d", mlxsw_sp->bus_info->fw_rev.major, mlxsw_sp->bus_info->fw_rev.minor, mlxsw_sp->bus_info->fw_rev.subminor); - strlcpy(drvinfo->bus_info, mlxsw_sp->bus_info->device_name, + strscpy(drvinfo->bus_info, mlxsw_sp->bus_info->device_name, sizeof(drvinfo->bus_info)); } diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index 2c4443c6b964..48f1fa62a4fd 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -1819,7 +1819,7 @@ void mlxsw_sp_ipip_entry_demote_tunnel(struct mlxsw_sp *mlxsw_sp, /* The configuration where several tunnels have the same local address in the * same underlay table needs special treatment in the HW. That is currently not * implemented in the driver. This function finds and demotes the first tunnel - * with a given source address, except the one passed in in the argument + * with a given source address, except the one passed in the argument * `except'. */ bool diff --git a/drivers/net/ethernet/micrel/ks8851.h b/drivers/net/ethernet/micrel/ks8851.h index 6f34a61739b6..fecd43754cea 100644 --- a/drivers/net/ethernet/micrel/ks8851.h +++ b/drivers/net/ethernet/micrel/ks8851.h @@ -403,7 +403,7 @@ struct ks8851_net { struct eeprom_93cx6 eeprom; struct regulator *vdd_reg; struct regulator *vdd_io; - int gpio; + struct gpio_desc *gpio; struct mii_bus *mii_bus; void (*lock)(struct ks8851_net *ks, diff --git a/drivers/net/ethernet/micrel/ks8851_common.c b/drivers/net/ethernet/micrel/ks8851_common.c index 691206f19ea7..cfbc900d4aeb 100644 --- a/drivers/net/ethernet/micrel/ks8851_common.c +++ b/drivers/net/ethernet/micrel/ks8851_common.c @@ -17,10 +17,9 @@ #include <linux/cache.h> #include <linux/crc32.h> #include <linux/mii.h> +#include <linux/gpio/consumer.h> #include <linux/regulator/consumer.h> -#include <linux/gpio.h> -#include <linux/of_gpio.h> #include <linux/of_mdio.h> #include <linux/of_net.h> @@ -703,9 +702,9 @@ static const struct net_device_ops ks8851_netdev_ops = { static void ks8851_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *di) { - strlcpy(di->driver, "KS8851", sizeof(di->driver)); - strlcpy(di->version, "1.00", sizeof(di->version)); - strlcpy(di->bus_info, dev_name(dev->dev.parent), sizeof(di->bus_info)); + strscpy(di->driver, "KS8851", sizeof(di->driver)); + strscpy(di->version, "1.00", sizeof(di->version)); + strscpy(di->bus_info, dev_name(dev->dev.parent), sizeof(di->bus_info)); } static u32 ks8851_get_msglevel(struct net_device *dev) @@ -1117,24 +1116,23 @@ int ks8851_probe_common(struct net_device *netdev, struct device *dev, { struct ks8851_net *ks = netdev_priv(netdev); unsigned cider; - int gpio; int ret; ks->netdev = netdev; ks->tx_space = 6144; - gpio = of_get_named_gpio_flags(dev->of_node, "reset-gpios", 0, NULL); - if (gpio == -EPROBE_DEFER) - return gpio; - - ks->gpio = gpio; - if (gpio_is_valid(gpio)) { - ret = devm_gpio_request_one(dev, gpio, - GPIOF_OUT_INIT_LOW, "ks8851_rst_n"); - if (ret) { - dev_err(dev, "reset gpio request failed\n"); - return ret; - } + ks->gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); + ret = PTR_ERR_OR_ZERO(ks->gpio); + if (ret) { + if (ret != -EPROBE_DEFER) + dev_err(dev, "reset gpio request failed: %d\n", ret); + return ret; + } + + ret = gpiod_set_consumer_name(ks->gpio, "ks8851_rst_n"); + if (ret) { + dev_err(dev, "failed to set reset gpio name: %d\n", ret); + return ret; } ks->vdd_io = devm_regulator_get(dev, "vdd-io"); @@ -1161,9 +1159,9 @@ int ks8851_probe_common(struct net_device *netdev, struct device *dev, goto err_reg; } - if (gpio_is_valid(gpio)) { + if (ks->gpio) { usleep_range(10000, 11000); - gpio_set_value(gpio, 1); + gpiod_set_value_cansleep(ks->gpio, 0); } spin_lock_init(&ks->statelock); @@ -1239,8 +1237,8 @@ int ks8851_probe_common(struct net_device *netdev, struct device *dev, err_id: ks8851_unregister_mdiobus(ks); err_mdio: - if (gpio_is_valid(gpio)) - gpio_set_value(gpio, 0); + if (ks->gpio) + gpiod_set_value_cansleep(ks->gpio, 1); regulator_disable(ks->vdd_reg); err_reg: regulator_disable(ks->vdd_io); @@ -1259,8 +1257,8 @@ void ks8851_remove_common(struct device *dev) dev_info(dev, "remove\n"); unregister_netdev(priv->netdev); - if (gpio_is_valid(priv->gpio)) - gpio_set_value(priv->gpio, 0); + if (priv->gpio) + gpiod_set_value_cansleep(priv->gpio, 1); regulator_disable(priv->vdd_reg); regulator_disable(priv->vdd_io); } diff --git a/drivers/net/ethernet/micrel/ks8851_spi.c b/drivers/net/ethernet/micrel/ks8851_spi.c index 82d55fc27edc..70bc7253454f 100644 --- a/drivers/net/ethernet/micrel/ks8851_spi.c +++ b/drivers/net/ethernet/micrel/ks8851_spi.c @@ -413,7 +413,8 @@ static int ks8851_probe_spi(struct spi_device *spi) spi->bits_per_word = 8; - ks = netdev_priv(netdev); + kss = netdev_priv(netdev); + ks = &kss->ks8851; ks->lock = ks8851_lock_spi; ks->unlock = ks8851_unlock_spi; @@ -433,8 +434,6 @@ static int ks8851_probe_spi(struct spi_device *spi) IRQ_RXPSI) /* RX process stop */ ks->rc_ier = STD_IRQ; - kss = to_ks8851_spi(ks); - kss->spidev = spi; mutex_init(&kss->lock); INIT_WORK(&kss->tx_work, ks8851_tx_work); diff --git a/drivers/net/ethernet/micrel/ksz884x.c b/drivers/net/ethernet/micrel/ksz884x.c index 2b3eb5ed8233..468520079c65 100644 --- a/drivers/net/ethernet/micrel/ksz884x.c +++ b/drivers/net/ethernet/micrel/ksz884x.c @@ -5998,9 +5998,9 @@ static void netdev_get_drvinfo(struct net_device *dev, struct dev_priv *priv = netdev_priv(dev); struct dev_info *hw_priv = priv->adapter; - strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); - strlcpy(info->version, DRV_VERSION, sizeof(info->version)); - strlcpy(info->bus_info, pci_name(hw_priv->pdev), + strscpy(info->driver, DRV_NAME, sizeof(info->driver)); + strscpy(info->version, DRV_VERSION, sizeof(info->version)); + strscpy(info->bus_info, pci_name(hw_priv->pdev), sizeof(info->bus_info)); } diff --git a/drivers/net/ethernet/microchip/enc28j60.c b/drivers/net/ethernet/microchip/enc28j60.c index 559ad94a44d0..176efbeae127 100644 --- a/drivers/net/ethernet/microchip/enc28j60.c +++ b/drivers/net/ethernet/microchip/enc28j60.c @@ -1467,9 +1467,9 @@ static void enc28j60_restart_work_handler(struct work_struct *work) static void enc28j60_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { - strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); - strlcpy(info->version, DRV_VERSION, sizeof(info->version)); - strlcpy(info->bus_info, + strscpy(info->driver, DRV_NAME, sizeof(info->driver)); + strscpy(info->version, DRV_VERSION, sizeof(info->version)); + strscpy(info->bus_info, dev_name(dev->dev.parent), sizeof(info->bus_info)); } diff --git a/drivers/net/ethernet/microchip/encx24j600.c b/drivers/net/ethernet/microchip/encx24j600.c index dc1840cb5b10..d7c8aa77ec75 100644 --- a/drivers/net/ethernet/microchip/encx24j600.c +++ b/drivers/net/ethernet/microchip/encx24j600.c @@ -925,9 +925,9 @@ static void encx24j600_get_regs(struct net_device *dev, static void encx24j600_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { - strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); - strlcpy(info->version, DRV_VERSION, sizeof(info->version)); - strlcpy(info->bus_info, dev_name(dev->dev.parent), + strscpy(info->driver, DRV_NAME, sizeof(info->driver)); + strscpy(info->version, DRV_VERSION, sizeof(info->version)); + strscpy(info->bus_info, dev_name(dev->dev.parent), sizeof(info->bus_info)); } diff --git a/drivers/net/ethernet/microchip/lan743x_ethtool.c b/drivers/net/ethernet/microchip/lan743x_ethtool.c index b1c74e6cb012..c739d60ee17d 100644 --- a/drivers/net/ethernet/microchip/lan743x_ethtool.c +++ b/drivers/net/ethernet/microchip/lan743x_ethtool.c @@ -579,8 +579,8 @@ static void lan743x_ethtool_get_drvinfo(struct net_device *netdev, { struct lan743x_adapter *adapter = netdev_priv(netdev); - strlcpy(info->driver, DRIVER_NAME, sizeof(info->driver)); - strlcpy(info->bus_info, + strscpy(info->driver, DRIVER_NAME, sizeof(info->driver)); + strscpy(info->bus_info, pci_name(adapter->pdev), sizeof(info->bus_info)); } diff --git a/drivers/net/ethernet/microchip/lan743x_main.c b/drivers/net/ethernet/microchip/lan743x_main.c index a9a1dea6d731..50eeecba1f18 100644 --- a/drivers/net/ethernet/microchip/lan743x_main.c +++ b/drivers/net/ethernet/microchip/lan743x_main.c @@ -1585,6 +1585,9 @@ static void lan743x_rfe_set_multicast(struct lan743x_adapter *adapter) rfctl |= RFE_CTL_AM_; } + if (netdev->features & NETIF_F_RXCSUM) + rfctl |= RFE_CTL_IP_COE_ | RFE_CTL_TCP_UDP_COE_; + memset(hash_table, 0, DP_SEL_VHF_HASH_LEN * sizeof(u32)); if (netdev_mc_count(netdev)) { struct netdev_hw_addr *ha; @@ -2066,11 +2069,13 @@ static netdev_tx_t lan743x_tx_xmit_frame(struct lan743x_tx *tx, { int required_number_of_descriptors = 0; unsigned int start_frame_length = 0; + netdev_tx_t retval = NETDEV_TX_OK; unsigned int frame_length = 0; unsigned int head_length = 0; unsigned long irq_flags = 0; bool do_timestamp = false; bool ignore_sync = false; + struct netdev_queue *txq; int nr_frags = 0; bool gso = false; int j; @@ -2083,9 +2088,12 @@ static netdev_tx_t lan743x_tx_xmit_frame(struct lan743x_tx *tx, if (required_number_of_descriptors > (tx->ring_size - 1)) { dev_kfree_skb_irq(skb); } else { - /* save to overflow buffer */ - tx->overflow_skb = skb; - netif_stop_queue(tx->adapter->netdev); + /* save how many descriptors we needed to restart the queue */ + tx->rqd_descriptors = required_number_of_descriptors; + retval = NETDEV_TX_BUSY; + txq = netdev_get_tx_queue(tx->adapter->netdev, + tx->channel_number); + netif_tx_stop_queue(txq); } goto unlock; } @@ -2144,15 +2152,15 @@ finish: unlock: spin_unlock_irqrestore(&tx->ring_lock, irq_flags); - return NETDEV_TX_OK; + return retval; } static int lan743x_tx_napi_poll(struct napi_struct *napi, int weight) { struct lan743x_tx *tx = container_of(napi, struct lan743x_tx, napi); struct lan743x_adapter *adapter = tx->adapter; - bool start_transmitter = false; unsigned long irq_flags = 0; + struct netdev_queue *txq; u32 ioc_bit = 0; ioc_bit = DMAC_INT_BIT_TX_IOC_(tx->channel_number); @@ -2163,24 +2171,20 @@ static int lan743x_tx_napi_poll(struct napi_struct *napi, int weight) /* clean up tx ring */ lan743x_tx_release_completed_descriptors(tx); - if (netif_queue_stopped(adapter->netdev)) { - if (tx->overflow_skb) { - if (lan743x_tx_get_desc_cnt(tx, tx->overflow_skb) <= - lan743x_tx_get_avail_desc(tx)) - start_transmitter = true; + txq = netdev_get_tx_queue(adapter->netdev, tx->channel_number); + if (netif_tx_queue_stopped(txq)) { + if (tx->rqd_descriptors) { + if (tx->rqd_descriptors <= + lan743x_tx_get_avail_desc(tx)) { + tx->rqd_descriptors = 0; + netif_tx_wake_queue(txq); + } } else { - netif_wake_queue(adapter->netdev); + netif_tx_wake_queue(txq); } } spin_unlock_irqrestore(&tx->ring_lock, irq_flags); - if (start_transmitter) { - /* space is now available, transmit overflow skb */ - lan743x_tx_xmit_frame(tx, tx->overflow_skb); - tx->overflow_skb = NULL; - netif_wake_queue(adapter->netdev); - } - if (!napi_complete(napi)) goto done; @@ -2304,10 +2308,7 @@ static void lan743x_tx_close(struct lan743x_tx *tx) lan743x_tx_release_all_descriptors(tx); - if (tx->overflow_skb) { - dev_kfree_skb(tx->overflow_skb); - tx->overflow_skb = NULL; - } + tx->rqd_descriptors = 0; lan743x_tx_ring_cleanup(tx); } @@ -2387,7 +2388,7 @@ static int lan743x_tx_open(struct lan743x_tx *tx) (tx->channel_number)); netif_napi_add_tx_weight(adapter->netdev, &tx->napi, lan743x_tx_napi_poll, - tx->ring_size - 1); + NAPI_POLL_WEIGHT); napi_enable(&tx->napi); data = 0; @@ -2549,6 +2550,7 @@ static int lan743x_rx_process_buffer(struct lan743x_rx *rx) int result = RX_PROCESS_RESULT_NOTHING_TO_DO; struct lan743x_rx_buffer_info *buffer_info; int frame_length, buffer_length; + bool is_ice, is_tce, is_icsm; int extension_index = -1; bool is_last, is_first; struct sk_buff *skb; @@ -2595,6 +2597,9 @@ static int lan743x_rx_process_buffer(struct lan743x_rx *rx) frame_length = RX_DESC_DATA0_FRAME_LENGTH_GET_(le32_to_cpu(descriptor->data0)); buffer_length = buffer_info->buffer_length; + is_ice = le32_to_cpu(descriptor->data1) & RX_DESC_DATA1_STATUS_ICE_; + is_tce = le32_to_cpu(descriptor->data1) & RX_DESC_DATA1_STATUS_TCE_; + is_icsm = le32_to_cpu(descriptor->data1) & RX_DESC_DATA1_STATUS_ICSM_; netdev_dbg(netdev, "%s%schunk: %d/%d", is_first ? "first " : " ", @@ -2663,6 +2668,10 @@ process_extension: if (is_last && rx->skb_head) { rx->skb_head->protocol = eth_type_trans(rx->skb_head, rx->adapter->netdev); + if (rx->adapter->netdev->features & NETIF_F_RXCSUM) { + if (!is_ice && !is_tce && !is_icsm) + skb->ip_summed = CHECKSUM_UNNECESSARY; + } netdev_dbg(netdev, "sending %d byte frame to OS", rx->skb_head->len); napi_gro_receive(&rx->napi, rx->skb_head); @@ -2866,9 +2875,7 @@ static int lan743x_rx_open(struct lan743x_rx *rx) if (ret) goto return_error; - netif_napi_add(adapter->netdev, - &rx->napi, lan743x_rx_napi_poll, - NAPI_POLL_WEIGHT); + netif_napi_add(adapter->netdev, &rx->napi, lan743x_rx_napi_poll); lan743x_csr_write(adapter, DMAC_CMD, DMAC_CMD_RX_SWR_(rx->channel_number)); @@ -3347,8 +3354,10 @@ static int lan743x_pcidev_probe(struct pci_dev *pdev, PCI11X1X_USED_TX_CHANNELS, LAN743X_USED_RX_CHANNELS); } else { - netdev = devm_alloc_etherdev(&pdev->dev, - sizeof(struct lan743x_adapter)); + netdev = devm_alloc_etherdev_mqs(&pdev->dev, + sizeof(struct lan743x_adapter), + LAN743X_USED_TX_CHANNELS, + LAN743X_USED_RX_CHANNELS); } if (!netdev) @@ -3383,7 +3392,8 @@ static int lan743x_pcidev_probe(struct pci_dev *pdev, adapter->netdev->netdev_ops = &lan743x_netdev_ops; adapter->netdev->ethtool_ops = &lan743x_ethtool_ops; - adapter->netdev->features = NETIF_F_SG | NETIF_F_TSO | NETIF_F_HW_CSUM; + adapter->netdev->features = NETIF_F_SG | NETIF_F_TSO | + NETIF_F_HW_CSUM | NETIF_F_RXCSUM; adapter->netdev->hw_features = adapter->netdev->features; /* carrier off reporting is important to ethtool even BEFORE open */ diff --git a/drivers/net/ethernet/microchip/lan743x_main.h b/drivers/net/ethernet/microchip/lan743x_main.h index 72adae4f2aa0..67877d3b6dd9 100644 --- a/drivers/net/ethernet/microchip/lan743x_main.h +++ b/drivers/net/ethernet/microchip/lan743x_main.h @@ -266,6 +266,8 @@ #define RFE_ADDR_FILT_LO(x) (0x404 + (8 * (x))) #define RFE_CTL (0x508) +#define RFE_CTL_TCP_UDP_COE_ BIT(12) +#define RFE_CTL_IP_COE_ BIT(11) #define RFE_CTL_AB_ BIT(10) #define RFE_CTL_AM_ BIT(9) #define RFE_CTL_AU_ BIT(8) @@ -954,8 +956,7 @@ struct lan743x_tx { struct napi_struct napi; u32 frame_count; - - struct sk_buff *overflow_skb; + u32 rqd_descriptors; }; void lan743x_tx_set_timestamping_mode(struct lan743x_tx *tx, @@ -1110,7 +1111,7 @@ struct lan743x_tx_buffer_info { unsigned int buffer_length; }; -#define LAN743X_TX_RING_SIZE (50) +#define LAN743X_TX_RING_SIZE (128) /* OWN bit is set. ie, Descs are owned by RX DMAC */ #define RX_DESC_DATA0_OWN_ (0x00008000) @@ -1122,6 +1123,9 @@ struct lan743x_tx_buffer_info { (((data0) & RX_DESC_DATA0_FRAME_LENGTH_MASK_) >> 16) #define RX_DESC_DATA0_EXT_ (0x00004000) #define RX_DESC_DATA0_BUF_LENGTH_MASK_ (0x00003FFF) +#define RX_DESC_DATA1_STATUS_ICE_ (0x00020000) +#define RX_DESC_DATA1_STATUS_TCE_ (0x00010000) +#define RX_DESC_DATA1_STATUS_ICSM_ (0x00000001) #define RX_DESC_DATA2_TS_NS_MASK_ (0x3FFFFFFF) #if ((NET_IP_ALIGN != 0) && (NET_IP_ALIGN != 2)) diff --git a/drivers/net/ethernet/microchip/lan743x_ptp.c b/drivers/net/ethernet/microchip/lan743x_ptp.c index 6a11e2ceb013..da3ea905adbb 100644 --- a/drivers/net/ethernet/microchip/lan743x_ptp.c +++ b/drivers/net/ethernet/microchip/lan743x_ptp.c @@ -1049,6 +1049,10 @@ static int lan743x_ptpci_verify_pin_config(struct ptp_clock_info *ptp, enum ptp_pin_function func, unsigned int chan) { + struct lan743x_ptp *lan_ptp = + container_of(ptp, struct lan743x_ptp, ptp_clock_info); + struct lan743x_adapter *adapter = + container_of(lan_ptp, struct lan743x_adapter, ptp); int result = 0; /* Confirm the requested function is supported. Parameter @@ -1057,7 +1061,10 @@ static int lan743x_ptpci_verify_pin_config(struct ptp_clock_info *ptp, switch (func) { case PTP_PF_NONE: case PTP_PF_PEROUT: + break; case PTP_PF_EXTTS: + if (!adapter->is_pci11x1x) + result = -1; break; case PTP_PF_PHYSYNC: default: diff --git a/drivers/net/ethernet/microchip/lan966x/Kconfig b/drivers/net/ethernet/microchip/lan966x/Kconfig index 4241ff0e5098..49e1464a4313 100644 --- a/drivers/net/ethernet/microchip/lan966x/Kconfig +++ b/drivers/net/ethernet/microchip/lan966x/Kconfig @@ -4,6 +4,7 @@ config LAN966X_SWITCH depends on HAS_IOMEM depends on OF depends on NET_SWITCHDEV + depends on BRIDGE || BRIDGE=n select PHYLINK select PACKING help diff --git a/drivers/net/ethernet/microchip/lan966x/Makefile b/drivers/net/ethernet/microchip/lan966x/Makefile index fd2e0ebb2427..962f7c5f9e7d 100644 --- a/drivers/net/ethernet/microchip/lan966x/Makefile +++ b/drivers/net/ethernet/microchip/lan966x/Makefile @@ -8,4 +8,7 @@ obj-$(CONFIG_LAN966X_SWITCH) += lan966x-switch.o lan966x-switch-objs := lan966x_main.o lan966x_phylink.o lan966x_port.o \ lan966x_mac.o lan966x_ethtool.o lan966x_switchdev.o \ lan966x_vlan.o lan966x_fdb.o lan966x_mdb.o \ - lan966x_ptp.o lan966x_fdma.o + lan966x_ptp.o lan966x_fdma.o lan966x_lag.o \ + lan966x_tc.o lan966x_mqprio.o lan966x_taprio.o \ + lan966x_tbf.o lan966x_cbs.o lan966x_ets.o \ + lan966x_tc_matchall.o lan966x_police.o lan966x_mirror.o diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_cbs.c b/drivers/net/ethernet/microchip/lan966x/lan966x_cbs.c new file mode 100644 index 000000000000..70cbbf8d2b67 --- /dev/null +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_cbs.c @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: GPL-2.0+ + +#include "lan966x_main.h" + +int lan966x_cbs_add(struct lan966x_port *port, + struct tc_cbs_qopt_offload *qopt) +{ + struct lan966x *lan966x = port->lan966x; + u32 cir, cbs; + u8 se_idx; + + /* Check for invalid values */ + if (qopt->idleslope <= 0 || + qopt->sendslope >= 0 || + qopt->locredit >= qopt->hicredit) + return -EINVAL; + + se_idx = SE_IDX_QUEUE + port->chip_port * NUM_PRIO_QUEUES + qopt->queue; + cir = qopt->idleslope; + cbs = (qopt->idleslope - qopt->sendslope) * + (qopt->hicredit - qopt->locredit) / + -qopt->sendslope; + + /* Rate unit is 100 kbps */ + cir = DIV_ROUND_UP(cir, 100); + /* Avoid using zero rate */ + cir = cir ?: 1; + /* Burst unit is 4kB */ + cbs = DIV_ROUND_UP(cbs, 4096); + /* Avoid using zero burst */ + cbs = cbs ?: 1; + + /* Check that actually the result can be written */ + if (cir > GENMASK(15, 0) || + cbs > GENMASK(6, 0)) + return -EINVAL; + + lan_rmw(QSYS_SE_CFG_SE_AVB_ENA_SET(1) | + QSYS_SE_CFG_SE_FRM_MODE_SET(1), + QSYS_SE_CFG_SE_AVB_ENA | + QSYS_SE_CFG_SE_FRM_MODE, + lan966x, QSYS_SE_CFG(se_idx)); + + lan_wr(QSYS_CIR_CFG_CIR_RATE_SET(cir) | + QSYS_CIR_CFG_CIR_BURST_SET(cbs), + lan966x, QSYS_CIR_CFG(se_idx)); + + return 0; +} + +int lan966x_cbs_del(struct lan966x_port *port, + struct tc_cbs_qopt_offload *qopt) +{ + struct lan966x *lan966x = port->lan966x; + u8 se_idx; + + se_idx = SE_IDX_QUEUE + port->chip_port * NUM_PRIO_QUEUES + qopt->queue; + + lan_rmw(QSYS_SE_CFG_SE_AVB_ENA_SET(1) | + QSYS_SE_CFG_SE_FRM_MODE_SET(0), + QSYS_SE_CFG_SE_AVB_ENA | + QSYS_SE_CFG_SE_FRM_MODE, + lan966x, QSYS_SE_CFG(se_idx)); + + lan_wr(QSYS_CIR_CFG_CIR_RATE_SET(0) | + QSYS_CIR_CFG_CIR_BURST_SET(0), + lan966x, QSYS_CIR_CFG(se_idx)); + + return 0; +} diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_ets.c b/drivers/net/ethernet/microchip/lan966x/lan966x_ets.c new file mode 100644 index 000000000000..8310d3f35404 --- /dev/null +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_ets.c @@ -0,0 +1,96 @@ +// SPDX-License-Identifier: GPL-2.0+ + +#include "lan966x_main.h" + +#define DWRR_COST_BIT_WIDTH BIT(5) + +static u32 lan966x_ets_hw_cost(u32 w_min, u32 weight) +{ + u32 res; + + /* Round half up: Multiply with 16 before division, + * add 8 and divide result with 16 again + */ + res = (((DWRR_COST_BIT_WIDTH << 4) * w_min / weight) + 8) >> 4; + return max_t(u32, 1, res) - 1; +} + +int lan966x_ets_add(struct lan966x_port *port, + struct tc_ets_qopt_offload *qopt) +{ + struct tc_ets_qopt_offload_replace_params *params; + struct lan966x *lan966x = port->lan966x; + u32 w_min = 100; + u8 count = 0; + u32 se_idx; + u8 i; + + /* Check the input */ + if (qopt->parent != TC_H_ROOT) + return -EINVAL; + + params = &qopt->replace_params; + if (params->bands != NUM_PRIO_QUEUES) + return -EINVAL; + + for (i = 0; i < params->bands; ++i) { + /* In the switch the DWRR is always on the lowest consecutive + * priorities. Due to this, the first priority must map to the + * first DWRR band. + */ + if (params->priomap[i] != (7 - i)) + return -EINVAL; + + if (params->quanta[i] && params->weights[i] == 0) + return -EINVAL; + } + + se_idx = SE_IDX_PORT + port->chip_port; + + /* Find minimum weight */ + for (i = 0; i < params->bands; ++i) { + if (params->quanta[i] == 0) + continue; + + w_min = min(w_min, params->weights[i]); + } + + for (i = 0; i < params->bands; ++i) { + if (params->quanta[i] == 0) + continue; + + ++count; + + lan_wr(lan966x_ets_hw_cost(w_min, params->weights[i]), + lan966x, QSYS_SE_DWRR_CFG(se_idx, 7 - i)); + } + + lan_rmw(QSYS_SE_CFG_SE_DWRR_CNT_SET(count) | + QSYS_SE_CFG_SE_RR_ENA_SET(0), + QSYS_SE_CFG_SE_DWRR_CNT | + QSYS_SE_CFG_SE_RR_ENA, + lan966x, QSYS_SE_CFG(se_idx)); + + return 0; +} + +int lan966x_ets_del(struct lan966x_port *port, + struct tc_ets_qopt_offload *qopt) +{ + struct lan966x *lan966x = port->lan966x; + u32 se_idx; + int i; + + se_idx = SE_IDX_PORT + port->chip_port; + + for (i = 0; i < NUM_PRIO_QUEUES; ++i) + lan_wr(0, lan966x, QSYS_SE_DWRR_CFG(se_idx, i)); + + lan_rmw(QSYS_SE_CFG_SE_DWRR_CNT_SET(0) | + QSYS_SE_CFG_SE_RR_ENA_SET(0), + QSYS_SE_CFG_SE_DWRR_CNT | + QSYS_SE_CFG_SE_RR_ENA, + lan966x, QSYS_SE_CFG(se_idx)); + + return 0; +} diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_fdb.c b/drivers/net/ethernet/microchip/lan966x/lan966x_fdb.c index da5ca7188679..2ea263e893ee 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_fdb.c +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_fdb.c @@ -8,6 +8,7 @@ struct lan966x_fdb_event_work { struct work_struct work; struct switchdev_notifier_fdb_info fdb_info; struct net_device *dev; + struct net_device *orig_dev; struct lan966x *lan966x; unsigned long event; }; @@ -127,75 +128,119 @@ void lan966x_fdb_deinit(struct lan966x *lan966x) lan966x_fdb_purge_entries(lan966x); } -static void lan966x_fdb_event_work(struct work_struct *work) +void lan966x_fdb_flush_workqueue(struct lan966x *lan966x) +{ + flush_workqueue(lan966x->fdb_work); +} + +static void lan966x_fdb_port_event_work(struct lan966x_fdb_event_work *fdb_work) { - struct lan966x_fdb_event_work *fdb_work = - container_of(work, struct lan966x_fdb_event_work, work); struct switchdev_notifier_fdb_info *fdb_info; - struct net_device *dev = fdb_work->dev; struct lan966x_port *port; struct lan966x *lan966x; - int ret; - fdb_info = &fdb_work->fdb_info; lan966x = fdb_work->lan966x; + port = netdev_priv(fdb_work->orig_dev); + fdb_info = &fdb_work->fdb_info; - if (lan966x_netdevice_check(dev)) { - port = netdev_priv(dev); + switch (fdb_work->event) { + case SWITCHDEV_FDB_ADD_TO_DEVICE: + if (!fdb_info->added_by_user) + break; + lan966x_mac_add_entry(lan966x, port, fdb_info->addr, + fdb_info->vid); + break; + case SWITCHDEV_FDB_DEL_TO_DEVICE: + if (!fdb_info->added_by_user) + break; + lan966x_mac_del_entry(lan966x, fdb_info->addr, + fdb_info->vid); + break; + } +} + +static void lan966x_fdb_bridge_event_work(struct lan966x_fdb_event_work *fdb_work) +{ + struct switchdev_notifier_fdb_info *fdb_info; + struct lan966x *lan966x; + int ret; - switch (fdb_work->event) { - case SWITCHDEV_FDB_ADD_TO_DEVICE: - if (!fdb_info->added_by_user) - break; - lan966x_mac_add_entry(lan966x, port, fdb_info->addr, - fdb_info->vid); + lan966x = fdb_work->lan966x; + fdb_info = &fdb_work->fdb_info; + + /* In case the bridge is called */ + switch (fdb_work->event) { + case SWITCHDEV_FDB_ADD_TO_DEVICE: + /* If there is no front port in this vlan, there is no + * point to copy the frame to CPU because it would be + * just dropped at later point. So add it only if + * there is a port but it is required to store the fdb + * entry for later point when a port actually gets in + * the vlan. + */ + lan966x_fdb_add_entry(lan966x, fdb_info); + if (!lan966x_vlan_cpu_member_cpu_vlan_mask(lan966x, + fdb_info->vid)) break; - case SWITCHDEV_FDB_DEL_TO_DEVICE: - if (!fdb_info->added_by_user) - break; - lan966x_mac_del_entry(lan966x, fdb_info->addr, - fdb_info->vid); + + lan966x_mac_cpu_learn(lan966x, fdb_info->addr, + fdb_info->vid); + break; + case SWITCHDEV_FDB_DEL_TO_DEVICE: + ret = lan966x_fdb_del_entry(lan966x, fdb_info); + if (!lan966x_vlan_cpu_member_cpu_vlan_mask(lan966x, + fdb_info->vid)) break; - } - } else { - if (!netif_is_bridge_master(dev)) - goto out; - - /* In case the bridge is called */ - switch (fdb_work->event) { - case SWITCHDEV_FDB_ADD_TO_DEVICE: - /* If there is no front port in this vlan, there is no - * point to copy the frame to CPU because it would be - * just dropped at later point. So add it only if - * there is a port but it is required to store the fdb - * entry for later point when a port actually gets in - * the vlan. - */ - lan966x_fdb_add_entry(lan966x, fdb_info); - if (!lan966x_vlan_cpu_member_cpu_vlan_mask(lan966x, - fdb_info->vid)) - break; - - lan966x_mac_cpu_learn(lan966x, fdb_info->addr, - fdb_info->vid); + + if (ret) + lan966x_mac_cpu_forget(lan966x, fdb_info->addr, + fdb_info->vid); + break; + } +} + +static void lan966x_fdb_lag_event_work(struct lan966x_fdb_event_work *fdb_work) +{ + struct switchdev_notifier_fdb_info *fdb_info; + struct lan966x_port *port; + struct lan966x *lan966x; + + if (!lan966x_lag_first_port(fdb_work->orig_dev, fdb_work->dev)) + return; + + lan966x = fdb_work->lan966x; + port = netdev_priv(fdb_work->dev); + fdb_info = &fdb_work->fdb_info; + + switch (fdb_work->event) { + case SWITCHDEV_FDB_ADD_TO_DEVICE: + if (!fdb_info->added_by_user) break; - case SWITCHDEV_FDB_DEL_TO_DEVICE: - ret = lan966x_fdb_del_entry(lan966x, fdb_info); - if (!lan966x_vlan_cpu_member_cpu_vlan_mask(lan966x, - fdb_info->vid)) - break; - - if (ret) - lan966x_mac_cpu_forget(lan966x, fdb_info->addr, - fdb_info->vid); + lan966x_mac_add_entry(lan966x, port, fdb_info->addr, + fdb_info->vid); + break; + case SWITCHDEV_FDB_DEL_TO_DEVICE: + if (!fdb_info->added_by_user) break; - } + lan966x_mac_del_entry(lan966x, fdb_info->addr, fdb_info->vid); + break; } +} + +static void lan966x_fdb_event_work(struct work_struct *work) +{ + struct lan966x_fdb_event_work *fdb_work = + container_of(work, struct lan966x_fdb_event_work, work); + + if (lan966x_netdevice_check(fdb_work->orig_dev)) + lan966x_fdb_port_event_work(fdb_work); + else if (netif_is_bridge_master(fdb_work->orig_dev)) + lan966x_fdb_bridge_event_work(fdb_work); + else if (netif_is_lag_master(fdb_work->orig_dev)) + lan966x_fdb_lag_event_work(fdb_work); -out: kfree(fdb_work->fdb_info.addr); kfree(fdb_work); - dev_put(dev); } int lan966x_handle_fdb(struct net_device *dev, @@ -221,7 +266,8 @@ int lan966x_handle_fdb(struct net_device *dev, if (!fdb_work) return -ENOMEM; - fdb_work->dev = orig_dev; + fdb_work->dev = dev; + fdb_work->orig_dev = orig_dev; fdb_work->lan966x = lan966x; fdb_work->event = event; INIT_WORK(&fdb_work->work, lan966x_fdb_event_work); @@ -231,7 +277,6 @@ int lan966x_handle_fdb(struct net_device *dev, goto err_addr_alloc; ether_addr_copy((u8 *)fdb_work->fdb_info.addr, fdb_info->addr); - dev_hold(orig_dev); queue_work(lan966x->fdb_work, &fdb_work->work); break; diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_fdma.c b/drivers/net/ethernet/microchip/lan966x/lan966x_fdma.c index 51f8a0816377..7e4061c854f0 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_fdma.c +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_fdma.c @@ -787,8 +787,7 @@ void lan966x_fdma_netdev_init(struct lan966x *lan966x, struct net_device *dev) return; lan966x->fdma_ndev = dev; - netif_napi_add(dev, &lan966x->napi, lan966x_fdma_napi_poll, - NAPI_POLL_WEIGHT); + netif_napi_add(dev, &lan966x->napi, lan966x_fdma_napi_poll); napi_enable(&lan966x->napi); } diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_lag.c b/drivers/net/ethernet/microchip/lan966x/lan966x_lag.c new file mode 100644 index 000000000000..41fa2523d91d --- /dev/null +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_lag.c @@ -0,0 +1,363 @@ +// SPDX-License-Identifier: GPL-2.0+ + +#include <linux/if_bridge.h> + +#include "lan966x_main.h" + +static void lan966x_lag_set_aggr_pgids(struct lan966x *lan966x) +{ + u32 visited = GENMASK(lan966x->num_phys_ports - 1, 0); + int p, lag, i; + + /* Reset destination and aggregation PGIDS */ + for (p = 0; p < lan966x->num_phys_ports; ++p) + lan_wr(ANA_PGID_PGID_SET(BIT(p)), + lan966x, ANA_PGID(p)); + + for (p = PGID_AGGR; p < PGID_SRC; ++p) + lan_wr(ANA_PGID_PGID_SET(visited), + lan966x, ANA_PGID(p)); + + /* The visited ports bitmask holds the list of ports offloading any + * bonding interface. Initially we mark all these ports as unvisited, + * then every time we visit a port in this bitmask, we know that it is + * the lowest numbered port, i.e. the one whose logical ID == physical + * port ID == LAG ID. So we mark as visited all further ports in the + * bitmask that are offloading the same bonding interface. This way, + * we set up the aggregation PGIDs only once per bonding interface. + */ + for (p = 0; p < lan966x->num_phys_ports; ++p) { + struct lan966x_port *port = lan966x->ports[p]; + + if (!port || !port->bond) + continue; + + visited &= ~BIT(p); + } + + /* Now, set PGIDs for each active LAG */ + for (lag = 0; lag < lan966x->num_phys_ports; ++lag) { + struct net_device *bond = lan966x->ports[lag]->bond; + int num_active_ports = 0; + unsigned long bond_mask; + u8 aggr_idx[16]; + + if (!bond || (visited & BIT(lag))) + continue; + + bond_mask = lan966x_lag_get_mask(lan966x, bond); + + for_each_set_bit(p, &bond_mask, lan966x->num_phys_ports) { + struct lan966x_port *port = lan966x->ports[p]; + + lan_wr(ANA_PGID_PGID_SET(bond_mask), + lan966x, ANA_PGID(p)); + if (port->lag_tx_active) + aggr_idx[num_active_ports++] = p; + } + + for (i = PGID_AGGR; i < PGID_SRC; ++i) { + u32 ac; + + ac = lan_rd(lan966x, ANA_PGID(i)); + ac &= ~bond_mask; + /* Don't do division by zero if there was no active + * port. Just make all aggregation codes zero. + */ + if (num_active_ports) + ac |= BIT(aggr_idx[i % num_active_ports]); + lan_wr(ANA_PGID_PGID_SET(ac), + lan966x, ANA_PGID(i)); + } + + /* Mark all ports in the same LAG as visited to avoid applying + * the same config again. + */ + for (p = lag; p < lan966x->num_phys_ports; p++) { + struct lan966x_port *port = lan966x->ports[p]; + + if (!port) + continue; + + if (port->bond == bond) + visited |= BIT(p); + } + } +} + +static void lan966x_lag_set_port_ids(struct lan966x *lan966x) +{ + struct lan966x_port *port; + u32 bond_mask; + u32 lag_id; + int p; + + for (p = 0; p < lan966x->num_phys_ports; ++p) { + port = lan966x->ports[p]; + if (!port) + continue; + + lag_id = port->chip_port; + + bond_mask = lan966x_lag_get_mask(lan966x, port->bond); + if (bond_mask) + lag_id = __ffs(bond_mask); + + lan_rmw(ANA_PORT_CFG_PORTID_VAL_SET(lag_id), + ANA_PORT_CFG_PORTID_VAL, + lan966x, ANA_PORT_CFG(port->chip_port)); + } +} + +static void lan966x_lag_update_ids(struct lan966x *lan966x) +{ + lan966x_lag_set_port_ids(lan966x); + lan966x_update_fwd_mask(lan966x); + lan966x_lag_set_aggr_pgids(lan966x); +} + +int lan966x_lag_port_join(struct lan966x_port *port, + struct net_device *brport_dev, + struct net_device *bond, + struct netlink_ext_ack *extack) +{ + struct lan966x *lan966x = port->lan966x; + struct net_device *dev = port->dev; + u32 lag_id = -1; + u32 bond_mask; + int err; + + bond_mask = lan966x_lag_get_mask(lan966x, bond); + if (bond_mask) + lag_id = __ffs(bond_mask); + + port->bond = bond; + lan966x_lag_update_ids(lan966x); + + err = switchdev_bridge_port_offload(brport_dev, dev, port, + &lan966x_switchdev_nb, + &lan966x_switchdev_blocking_nb, + false, extack); + if (err) + goto out; + + lan966x_port_stp_state_set(port, br_port_get_stp_state(brport_dev)); + + if (lan966x_lag_first_port(port->bond, port->dev) && + lag_id != -1) + lan966x_mac_lag_replace_port_entry(lan966x, + lan966x->ports[lag_id], + port); + + return 0; + +out: + port->bond = NULL; + lan966x_lag_update_ids(lan966x); + + return err; +} + +void lan966x_lag_port_leave(struct lan966x_port *port, struct net_device *bond) +{ + struct lan966x *lan966x = port->lan966x; + u32 bond_mask; + u32 lag_id; + + if (lan966x_lag_first_port(port->bond, port->dev)) { + bond_mask = lan966x_lag_get_mask(lan966x, port->bond); + bond_mask &= ~BIT(port->chip_port); + if (bond_mask) { + lag_id = __ffs(bond_mask); + lan966x_mac_lag_replace_port_entry(lan966x, port, + lan966x->ports[lag_id]); + } else { + lan966x_mac_lag_remove_port_entry(lan966x, port); + } + } + + port->bond = NULL; + lan966x_lag_update_ids(lan966x); + lan966x_port_stp_state_set(port, BR_STATE_FORWARDING); +} + +static bool lan966x_lag_port_check_hash_types(struct lan966x *lan966x, + enum netdev_lag_hash hash_type) +{ + int p; + + for (p = 0; p < lan966x->num_phys_ports; ++p) { + struct lan966x_port *port = lan966x->ports[p]; + + if (!port || !port->bond) + continue; + + if (port->hash_type != hash_type) + return false; + } + + return true; +} + +int lan966x_lag_port_prechangeupper(struct net_device *dev, + struct netdev_notifier_changeupper_info *info) +{ + struct lan966x_port *port = netdev_priv(dev); + struct lan966x *lan966x = port->lan966x; + struct netdev_lag_upper_info *lui; + struct netlink_ext_ack *extack; + + extack = netdev_notifier_info_to_extack(&info->info); + lui = info->upper_info; + if (!lui) { + port->hash_type = NETDEV_LAG_HASH_NONE; + return NOTIFY_DONE; + } + + if (lui->tx_type != NETDEV_LAG_TX_TYPE_HASH) { + NL_SET_ERR_MSG_MOD(extack, + "LAG device using unsupported Tx type"); + return -EINVAL; + } + + if (!lan966x_lag_port_check_hash_types(lan966x, lui->hash_type)) { + NL_SET_ERR_MSG_MOD(extack, + "LAG devices can have only the same hash_type"); + return -EINVAL; + } + + switch (lui->hash_type) { + case NETDEV_LAG_HASH_L2: + lan_wr(ANA_AGGR_CFG_AC_DMAC_ENA_SET(1) | + ANA_AGGR_CFG_AC_SMAC_ENA_SET(1), + lan966x, ANA_AGGR_CFG); + break; + case NETDEV_LAG_HASH_L34: + lan_wr(ANA_AGGR_CFG_AC_IP6_TCPUDP_ENA_SET(1) | + ANA_AGGR_CFG_AC_IP4_TCPUDP_ENA_SET(1) | + ANA_AGGR_CFG_AC_IP4_SIPDIP_ENA_SET(1), + lan966x, ANA_AGGR_CFG); + break; + case NETDEV_LAG_HASH_L23: + lan_wr(ANA_AGGR_CFG_AC_DMAC_ENA_SET(1) | + ANA_AGGR_CFG_AC_SMAC_ENA_SET(1) | + ANA_AGGR_CFG_AC_IP6_TCPUDP_ENA_SET(1) | + ANA_AGGR_CFG_AC_IP4_TCPUDP_ENA_SET(1), + lan966x, ANA_AGGR_CFG); + break; + default: + NL_SET_ERR_MSG_MOD(extack, + "LAG device using unsupported hash type"); + return -EINVAL; + } + + port->hash_type = lui->hash_type; + + return NOTIFY_OK; +} + +int lan966x_lag_port_changelowerstate(struct net_device *dev, + struct netdev_notifier_changelowerstate_info *info) +{ + struct netdev_lag_lower_state_info *lag = info->lower_state_info; + struct lan966x_port *port = netdev_priv(dev); + struct lan966x *lan966x = port->lan966x; + bool is_active; + + if (!port->bond) + return NOTIFY_DONE; + + is_active = lag->link_up && lag->tx_enabled; + if (port->lag_tx_active == is_active) + return NOTIFY_DONE; + + port->lag_tx_active = is_active; + lan966x_lag_set_aggr_pgids(lan966x); + + return NOTIFY_OK; +} + +int lan966x_lag_netdev_prechangeupper(struct net_device *dev, + struct netdev_notifier_changeupper_info *info) +{ + struct lan966x_port *port; + struct net_device *lower; + struct list_head *iter; + int err; + + netdev_for_each_lower_dev(dev, lower, iter) { + if (!lan966x_netdevice_check(lower)) + continue; + + port = netdev_priv(lower); + if (port->bond != dev) + continue; + + err = lan966x_port_prechangeupper(lower, dev, info); + if (err) + return err; + } + + return NOTIFY_DONE; +} + +int lan966x_lag_netdev_changeupper(struct net_device *dev, + struct netdev_notifier_changeupper_info *info) +{ + struct lan966x_port *port; + struct net_device *lower; + struct list_head *iter; + int err; + + netdev_for_each_lower_dev(dev, lower, iter) { + if (!lan966x_netdevice_check(lower)) + continue; + + port = netdev_priv(lower); + if (port->bond != dev) + continue; + + err = lan966x_port_changeupper(lower, dev, info); + if (err) + return err; + } + + return NOTIFY_DONE; +} + +bool lan966x_lag_first_port(struct net_device *lag, struct net_device *dev) +{ + struct lan966x_port *port = netdev_priv(dev); + struct lan966x *lan966x = port->lan966x; + unsigned long bond_mask; + + if (port->bond != lag) + return false; + + bond_mask = lan966x_lag_get_mask(lan966x, lag); + if (bond_mask && port->chip_port == __ffs(bond_mask)) + return true; + + return false; +} + +u32 lan966x_lag_get_mask(struct lan966x *lan966x, struct net_device *bond) +{ + struct lan966x_port *port; + u32 mask = 0; + int p; + + if (!bond) + return mask; + + for (p = 0; p < lan966x->num_phys_ports; p++) { + port = lan966x->ports[p]; + if (!port) + continue; + + if (port->bond == bond) + mask |= BIT(p); + } + + return mask; +} diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_mac.c b/drivers/net/ethernet/microchip/lan966x/lan966x_mac.c index 5893770bfd94..baa3a30c039f 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_mac.c +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_mac.c @@ -22,6 +22,7 @@ struct lan966x_mac_entry { u16 vid; u16 port_index; int row; + bool lag; }; struct lan966x_mac_raw_entry { @@ -69,15 +70,14 @@ static void lan966x_mac_select(struct lan966x *lan966x, lan_wr(mach, lan966x, ANA_MACHDATA); } -static int __lan966x_mac_learn(struct lan966x *lan966x, int pgid, - bool cpu_copy, - const unsigned char mac[ETH_ALEN], - unsigned int vid, - enum macaccess_entry_type type) +static int __lan966x_mac_learn_locked(struct lan966x *lan966x, int pgid, + bool cpu_copy, + const unsigned char mac[ETH_ALEN], + unsigned int vid, + enum macaccess_entry_type type) { - int ret; + lockdep_assert_held(&lan966x->mac_lock); - spin_lock(&lan966x->mac_lock); lan966x_mac_select(lan966x, mac, vid); /* Issue a write command */ @@ -89,7 +89,19 @@ static int __lan966x_mac_learn(struct lan966x *lan966x, int pgid, ANA_MACACCESS_MAC_TABLE_CMD_SET(MACACCESS_CMD_LEARN), lan966x, ANA_MACACCESS); - ret = lan966x_mac_wait_for_completion(lan966x); + return lan966x_mac_wait_for_completion(lan966x); +} + +static int __lan966x_mac_learn(struct lan966x *lan966x, int pgid, + bool cpu_copy, + const unsigned char mac[ETH_ALEN], + unsigned int vid, + enum macaccess_entry_type type) +{ + int ret; + + spin_lock(&lan966x->mac_lock); + ret = __lan966x_mac_learn_locked(lan966x, pgid, cpu_copy, mac, vid, type); spin_unlock(&lan966x->mac_lock); return ret; @@ -119,6 +131,16 @@ int lan966x_mac_learn(struct lan966x *lan966x, int port, return __lan966x_mac_learn(lan966x, port, false, mac, vid, type); } +static int lan966x_mac_learn_locked(struct lan966x *lan966x, int port, + const unsigned char mac[ETH_ALEN], + unsigned int vid, + enum macaccess_entry_type type) +{ + WARN_ON(type != ENTRYTYPE_NORMAL && type != ENTRYTYPE_LOCKED); + + return __lan966x_mac_learn_locked(lan966x, port, false, mac, vid, type); +} + static int lan966x_mac_forget_locked(struct lan966x *lan966x, const unsigned char mac[ETH_ALEN], unsigned int vid, @@ -178,8 +200,9 @@ void lan966x_mac_init(struct lan966x *lan966x) INIT_LIST_HEAD(&lan966x->mac_entries); } -static struct lan966x_mac_entry *lan966x_mac_alloc_entry(const unsigned char *mac, - u16 vid, u16 port_index) +static struct lan966x_mac_entry *lan966x_mac_alloc_entry(struct lan966x_port *port, + const unsigned char *mac, + u16 vid) { struct lan966x_mac_entry *mac_entry; @@ -189,8 +212,9 @@ static struct lan966x_mac_entry *lan966x_mac_alloc_entry(const unsigned char *ma memcpy(mac_entry->mac, mac, ETH_ALEN); mac_entry->vid = vid; - mac_entry->port_index = port_index; + mac_entry->port_index = port->chip_port; mac_entry->row = LAN966X_MAC_INVALID_ROW; + mac_entry->lag = port->bond ? true : false; return mac_entry; } @@ -269,7 +293,7 @@ int lan966x_mac_add_entry(struct lan966x *lan966x, struct lan966x_port *port, goto mac_learn; } - mac_entry = lan966x_mac_alloc_entry(addr, vid, port->chip_port); + mac_entry = lan966x_mac_alloc_entry(port, addr, vid); if (!mac_entry) { spin_unlock(&lan966x->mac_lock); return -ENOMEM; @@ -278,7 +302,8 @@ int lan966x_mac_add_entry(struct lan966x *lan966x, struct lan966x_port *port, list_add_tail(&mac_entry->list, &lan966x->mac_entries); spin_unlock(&lan966x->mac_lock); - lan966x_fdb_call_notifiers(SWITCHDEV_FDB_OFFLOADED, addr, vid, port->dev); + lan966x_fdb_call_notifiers(SWITCHDEV_FDB_OFFLOADED, addr, vid, + port->bond ?: port->dev); mac_learn: lan966x_mac_learn(lan966x, port->chip_port, addr, vid, ENTRYTYPE_LOCKED); @@ -309,6 +334,50 @@ int lan966x_mac_del_entry(struct lan966x *lan966x, const unsigned char *addr, return 0; } +void lan966x_mac_lag_replace_port_entry(struct lan966x *lan966x, + struct lan966x_port *src, + struct lan966x_port *dst) +{ + struct lan966x_mac_entry *mac_entry; + + spin_lock(&lan966x->mac_lock); + list_for_each_entry(mac_entry, &lan966x->mac_entries, list) { + if (mac_entry->port_index == src->chip_port && + mac_entry->lag) { + lan966x_mac_forget_locked(lan966x, mac_entry->mac, + mac_entry->vid, + ENTRYTYPE_LOCKED); + + lan966x_mac_learn_locked(lan966x, dst->chip_port, + mac_entry->mac, mac_entry->vid, + ENTRYTYPE_LOCKED); + mac_entry->port_index = dst->chip_port; + } + } + spin_unlock(&lan966x->mac_lock); +} + +void lan966x_mac_lag_remove_port_entry(struct lan966x *lan966x, + struct lan966x_port *src) +{ + struct lan966x_mac_entry *mac_entry, *tmp; + + spin_lock(&lan966x->mac_lock); + list_for_each_entry_safe(mac_entry, tmp, &lan966x->mac_entries, + list) { + if (mac_entry->port_index == src->chip_port && + mac_entry->lag) { + lan966x_mac_forget_locked(lan966x, mac_entry->mac, + mac_entry->vid, + ENTRYTYPE_LOCKED); + + list_del(&mac_entry->list); + kfree(mac_entry); + } + } + spin_unlock(&lan966x->mac_lock); +} + void lan966x_mac_purge_entries(struct lan966x *lan966x) { struct lan966x_mac_entry *mac_entry, *tmp; @@ -354,6 +423,7 @@ static void lan966x_mac_irq_process(struct lan966x *lan966x, u32 row, struct lan966x_mac_entry *mac_entry, *tmp; unsigned char mac[ETH_ALEN] __aligned(2); struct list_head mac_deleted_entries; + struct lan966x_port *port; u32 dest_idx; u32 column; u16 vid; @@ -406,9 +476,10 @@ static void lan966x_mac_irq_process(struct lan966x *lan966x, u32 row, /* Notify the bridge that the entry doesn't exist * anymore in the HW */ + port = lan966x->ports[mac_entry->port_index]; lan966x_mac_notifiers(SWITCHDEV_FDB_DEL_TO_BRIDGE, mac_entry->mac, mac_entry->vid, - lan966x->ports[mac_entry->port_index]->dev); + port->bond ?: port->dev); list_del(&mac_entry->list); kfree(mac_entry); } @@ -440,7 +511,8 @@ static void lan966x_mac_irq_process(struct lan966x *lan966x, u32 row, continue; } - mac_entry = lan966x_mac_alloc_entry(mac, vid, dest_idx); + port = lan966x->ports[dest_idx]; + mac_entry = lan966x_mac_alloc_entry(port, mac, vid); if (!mac_entry) { spin_unlock(&lan966x->mac_lock); return; @@ -451,7 +523,7 @@ static void lan966x_mac_irq_process(struct lan966x *lan966x, u32 row, spin_unlock(&lan966x->mac_lock); lan966x_mac_notifiers(SWITCHDEV_FDB_ADD_TO_BRIDGE, - mac, vid, lan966x->ports[dest_idx]->dev); + mac, vid, port->bond ?: port->dev); } } diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_main.c b/drivers/net/ethernet/microchip/lan966x/lan966x_main.c index d928b75f3780..be2fd030cccb 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_main.c +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_main.c @@ -344,7 +344,8 @@ static void lan966x_ifh_set_timestamp(void *ifh, u64 timestamp) IFH_POS_TIMESTAMP, IFH_LEN * 4, PACK, 0); } -static int lan966x_port_xmit(struct sk_buff *skb, struct net_device *dev) +static netdev_tx_t lan966x_port_xmit(struct sk_buff *skb, + struct net_device *dev) { struct lan966x_port *port = netdev_priv(dev); struct lan966x *lan966x = port->lan966x; @@ -466,6 +467,7 @@ static const struct net_device_ops lan966x_port_netdev_ops = { .ndo_set_mac_address = lan966x_port_set_mac_address, .ndo_get_port_parent_id = lan966x_port_get_parent_id, .ndo_eth_ioctl = lan966x_port_ioctl, + .ndo_setup_tc = lan966x_tc_setup, }; bool lan966x_netdevice_check(const struct net_device *dev) @@ -738,7 +740,8 @@ static int lan966x_probe_port(struct lan966x *lan966x, u32 p, return -EINVAL; dev = devm_alloc_etherdev_mqs(lan966x->dev, - sizeof(struct lan966x_port), 8, 1); + sizeof(struct lan966x_port), + NUM_PRIO_QUEUES, 1); if (!dev) return -ENOMEM; @@ -754,7 +757,9 @@ static int lan966x_probe_port(struct lan966x *lan966x, u32 p, dev->netdev_ops = &lan966x_port_netdev_ops; dev->ethtool_ops = &lan966x_ethtool_ops; dev->features |= NETIF_F_HW_VLAN_CTAG_TX | - NETIF_F_HW_VLAN_STAG_TX; + NETIF_F_HW_VLAN_STAG_TX | + NETIF_F_HW_TC; + dev->hw_features |= NETIF_F_HW_TC; dev->needed_headroom = IFH_LEN * sizeof(u32); eth_hw_addr_gen(dev, lan966x->base_mac, p + 1); @@ -770,6 +775,7 @@ static int lan966x_probe_port(struct lan966x *lan966x, u32 p, port->phylink_config.mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE | MAC_10 | MAC_100 | MAC_1000FD | MAC_2500FD; + phy_interface_set_rgmii(port->phylink_config.supported_interfaces); __set_bit(PHY_INTERFACE_MODE_MII, port->phylink_config.supported_interfaces); __set_bit(PHY_INTERFACE_MODE_GMII, @@ -778,6 +784,8 @@ static int lan966x_probe_port(struct lan966x *lan966x, u32 p, port->phylink_config.supported_interfaces); __set_bit(PHY_INTERFACE_MODE_QSGMII, port->phylink_config.supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_QUSGMII, + port->phylink_config.supported_interfaces); __set_bit(PHY_INTERFACE_MODE_1000BASEX, port->phylink_config.supported_interfaces); __set_bit(PHY_INTERFACE_MODE_2500BASEX, @@ -956,6 +964,8 @@ static void lan966x_init(struct lan966x *lan966x) lan966x, ANA_ANAINTR); spin_lock_init(&lan966x->tx_lock); + + lan966x_taprio_init(lan966x); } static int lan966x_ram_init(struct lan966x *lan966x) @@ -969,7 +979,8 @@ static int lan966x_reset_switch(struct lan966x *lan966x) int val = 0; int ret; - switch_reset = devm_reset_control_get_shared(lan966x->dev, "switch"); + switch_reset = devm_reset_control_get_optional_shared(lan966x->dev, + "switch"); if (IS_ERR(switch_reset)) return dev_err_probe(lan966x->dev, PTR_ERR(switch_reset), "Could not obtain switch reset"); @@ -1164,6 +1175,7 @@ static int lan966x_remove(struct platform_device *pdev) { struct lan966x *lan966x = platform_get_drvdata(pdev); + lan966x_taprio_deinit(lan966x); lan966x_fdma_deinit(lan966x); lan966x_cleanup_ports(lan966x); diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_main.h b/drivers/net/ethernet/microchip/lan966x/lan966x_main.h index 2787055c1847..9656071b8289 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_main.h +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_main.h @@ -9,6 +9,8 @@ #include <linux/phy.h> #include <linux/phylink.h> #include <linux/ptp_clock_kernel.h> +#include <net/pkt_cls.h> +#include <net/pkt_sched.h> #include <net/switchdev.h> #include "lan966x_regs.h" @@ -36,6 +38,7 @@ #define NUM_PHYS_PORTS 8 #define CPU_PORT 8 +#define NUM_PRIO_QUEUES 8 /* Reserved PGIDs */ #define PGID_CPU (PGID_AGGR - 6) @@ -79,6 +82,9 @@ #define FDMA_INJ_CHANNEL 0 #define FDMA_DCB_MAX 512 +#define SE_IDX_QUEUE 0 /* 0-79 : Queue scheduler elements */ +#define SE_IDX_PORT 80 /* 80-89 : Port schedular elements */ + /* MAC table entry types. * ENTRYTYPE_NORMAL is subject to aging. * ENTRYTYPE_LOCKED is not subject to aging. @@ -258,6 +264,11 @@ struct lan966x { struct lan966x_rx rx; struct lan966x_tx tx; struct napi_struct napi; + + /* Mirror */ + struct lan966x_port *mirror_monitor; + u32 mirror_mask[2]; + u32 mirror_count; }; struct lan966x_port_config { @@ -270,6 +281,15 @@ struct lan966x_port_config { bool autoneg; }; +struct lan966x_port_tc { + bool ingress_shared_block; + unsigned long police_id; + unsigned long ingress_mirror_id; + unsigned long egress_mirror_id; + struct flow_stats police_stat; + struct flow_stats mirror_stat; +}; + struct lan966x_port { struct net_device *dev; struct lan966x *lan966x; @@ -292,11 +312,19 @@ struct lan966x_port { u8 ptp_cmd; u16 ts_id; struct sk_buff_head tx_skbs; + + struct net_device *bond; + bool lag_tx_active; + enum netdev_lag_hash hash_type; + + struct lan966x_port_tc tc; }; extern const struct phylink_mac_ops lan966x_phylink_mac_ops; extern const struct phylink_pcs_ops lan966x_phylink_pcs_ops; extern const struct ethtool_ops lan966x_ethtool_ops; +extern struct notifier_block lan966x_switchdev_nb __read_mostly; +extern struct notifier_block lan966x_switchdev_blocking_nb __read_mostly; bool lan966x_netdevice_check(const struct net_device *dev); @@ -345,6 +373,11 @@ int lan966x_mac_add_entry(struct lan966x *lan966x, struct lan966x_port *port, const unsigned char *addr, u16 vid); +void lan966x_mac_lag_replace_port_entry(struct lan966x *lan966x, + struct lan966x_port *src, + struct lan966x_port *dst); +void lan966x_mac_lag_remove_port_entry(struct lan966x *lan966x, + struct lan966x_port *src); void lan966x_mac_purge_entries(struct lan966x *lan966x); irqreturn_t lan966x_mac_irq_handler(struct lan966x *lan966x); @@ -369,6 +402,7 @@ void lan966x_fdb_write_entries(struct lan966x *lan966x, u16 vid); void lan966x_fdb_erase_entries(struct lan966x *lan966x, u16 vid); int lan966x_fdb_init(struct lan966x *lan966x); void lan966x_fdb_deinit(struct lan966x *lan966x); +void lan966x_fdb_flush_workqueue(struct lan966x *lan966x); int lan966x_handle_fdb(struct net_device *dev, struct net_device *orig_dev, unsigned long event, const void *ctx, @@ -397,6 +431,8 @@ void lan966x_ptp_txtstamp_release(struct lan966x_port *port, struct sk_buff *skb); irqreturn_t lan966x_ptp_irq_handler(int irq, void *args); irqreturn_t lan966x_ptp_ext_irq_handler(int irq, void *args); +u32 lan966x_ptp_get_period_ps(void); +int lan966x_ptp_gettime64(struct ptp_clock_info *ptp, struct timespec64 *ts); int lan966x_fdma_xmit(struct sk_buff *skb, __be32 *ifh, struct net_device *dev); int lan966x_fdma_change_mtu(struct lan966x *lan966x); @@ -406,6 +442,89 @@ int lan966x_fdma_init(struct lan966x *lan966x); void lan966x_fdma_deinit(struct lan966x *lan966x); irqreturn_t lan966x_fdma_irq_handler(int irq, void *args); +int lan966x_lag_port_join(struct lan966x_port *port, + struct net_device *brport_dev, + struct net_device *bond, + struct netlink_ext_ack *extack); +void lan966x_lag_port_leave(struct lan966x_port *port, struct net_device *bond); +int lan966x_lag_port_prechangeupper(struct net_device *dev, + struct netdev_notifier_changeupper_info *info); +int lan966x_lag_port_changelowerstate(struct net_device *dev, + struct netdev_notifier_changelowerstate_info *info); +int lan966x_lag_netdev_prechangeupper(struct net_device *dev, + struct netdev_notifier_changeupper_info *info); +int lan966x_lag_netdev_changeupper(struct net_device *dev, + struct netdev_notifier_changeupper_info *info); +bool lan966x_lag_first_port(struct net_device *lag, struct net_device *dev); +u32 lan966x_lag_get_mask(struct lan966x *lan966x, struct net_device *bond); + +int lan966x_port_changeupper(struct net_device *dev, + struct net_device *brport_dev, + struct netdev_notifier_changeupper_info *info); +int lan966x_port_prechangeupper(struct net_device *dev, + struct net_device *brport_dev, + struct netdev_notifier_changeupper_info *info); +void lan966x_port_stp_state_set(struct lan966x_port *port, u8 state); +void lan966x_port_ageing_set(struct lan966x_port *port, + unsigned long ageing_clock_t); +void lan966x_update_fwd_mask(struct lan966x *lan966x); + +int lan966x_tc_setup(struct net_device *dev, enum tc_setup_type type, + void *type_data); + +int lan966x_mqprio_add(struct lan966x_port *port, u8 num_tc); +int lan966x_mqprio_del(struct lan966x_port *port); + +void lan966x_taprio_init(struct lan966x *lan966x); +void lan966x_taprio_deinit(struct lan966x *lan966x); +int lan966x_taprio_add(struct lan966x_port *port, + struct tc_taprio_qopt_offload *qopt); +int lan966x_taprio_del(struct lan966x_port *port); +int lan966x_taprio_speed_set(struct lan966x_port *port, int speed); + +int lan966x_tbf_add(struct lan966x_port *port, + struct tc_tbf_qopt_offload *qopt); +int lan966x_tbf_del(struct lan966x_port *port, + struct tc_tbf_qopt_offload *qopt); + +int lan966x_cbs_add(struct lan966x_port *port, + struct tc_cbs_qopt_offload *qopt); +int lan966x_cbs_del(struct lan966x_port *port, + struct tc_cbs_qopt_offload *qopt); + +int lan966x_ets_add(struct lan966x_port *port, + struct tc_ets_qopt_offload *qopt); +int lan966x_ets_del(struct lan966x_port *port, + struct tc_ets_qopt_offload *qopt); + +int lan966x_tc_matchall(struct lan966x_port *port, + struct tc_cls_matchall_offload *f, + bool ingress); + +int lan966x_police_port_add(struct lan966x_port *port, + struct flow_action *action, + struct flow_action_entry *act, + unsigned long police_id, + bool ingress, + struct netlink_ext_ack *extack); +int lan966x_police_port_del(struct lan966x_port *port, + unsigned long police_id, + struct netlink_ext_ack *extack); +void lan966x_police_port_stats(struct lan966x_port *port, + struct flow_stats *stats); + +int lan966x_mirror_port_add(struct lan966x_port *port, + struct flow_action_entry *action, + unsigned long mirror_id, + bool ingress, + struct netlink_ext_ack *extack); +int lan966x_mirror_port_del(struct lan966x_port *port, + bool ingress, + struct netlink_ext_ack *extack); +void lan966x_mirror_port_stats(struct lan966x_port *port, + struct flow_stats *stats, + bool ingress); + static inline void __iomem *lan_addr(void __iomem *base[], int id, int tinst, int tcnt, int gbase, int ginst, diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_mirror.c b/drivers/net/ethernet/microchip/lan966x/lan966x_mirror.c new file mode 100644 index 000000000000..7e1ba3f40c35 --- /dev/null +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_mirror.c @@ -0,0 +1,138 @@ +// SPDX-License-Identifier: GPL-2.0+ + +#include "lan966x_main.h" + +int lan966x_mirror_port_add(struct lan966x_port *port, + struct flow_action_entry *action, + unsigned long mirror_id, + bool ingress, + struct netlink_ext_ack *extack) +{ + struct lan966x *lan966x = port->lan966x; + struct lan966x_port *monitor_port; + + if (!lan966x_netdevice_check(action->dev)) { + NL_SET_ERR_MSG_MOD(extack, + "Destination not an lan966x port"); + return -EOPNOTSUPP; + } + + monitor_port = netdev_priv(action->dev); + + if (lan966x->mirror_mask[ingress] & BIT(port->chip_port)) { + NL_SET_ERR_MSG_MOD(extack, + "Mirror already exists"); + return -EEXIST; + } + + if (lan966x->mirror_monitor && + lan966x->mirror_monitor != monitor_port) { + NL_SET_ERR_MSG_MOD(extack, + "Cannot change mirror port while in use"); + return -EBUSY; + } + + if (port == monitor_port) { + NL_SET_ERR_MSG_MOD(extack, + "Cannot mirror the monitor port"); + return -EINVAL; + } + + lan966x->mirror_mask[ingress] |= BIT(port->chip_port); + + lan966x->mirror_monitor = monitor_port; + lan_wr(BIT(monitor_port->chip_port), lan966x, ANA_MIRRORPORTS); + + if (ingress) { + lan_rmw(ANA_PORT_CFG_SRC_MIRROR_ENA_SET(1), + ANA_PORT_CFG_SRC_MIRROR_ENA, + lan966x, ANA_PORT_CFG(port->chip_port)); + } else { + lan_wr(lan966x->mirror_mask[0], lan966x, + ANA_EMIRRORPORTS); + } + + lan966x->mirror_count++; + + if (ingress) + port->tc.ingress_mirror_id = mirror_id; + else + port->tc.egress_mirror_id = mirror_id; + + return 0; +} + +int lan966x_mirror_port_del(struct lan966x_port *port, + bool ingress, + struct netlink_ext_ack *extack) +{ + struct lan966x *lan966x = port->lan966x; + + if (!(lan966x->mirror_mask[ingress] & BIT(port->chip_port))) { + NL_SET_ERR_MSG_MOD(extack, + "There is no mirroring for this port"); + return -ENOENT; + } + + lan966x->mirror_mask[ingress] &= ~BIT(port->chip_port); + + if (ingress) { + lan_rmw(ANA_PORT_CFG_SRC_MIRROR_ENA_SET(0), + ANA_PORT_CFG_SRC_MIRROR_ENA, + lan966x, ANA_PORT_CFG(port->chip_port)); + } else { + lan_wr(lan966x->mirror_mask[0], lan966x, + ANA_EMIRRORPORTS); + } + + lan966x->mirror_count--; + + if (lan966x->mirror_count == 0) { + lan966x->mirror_monitor = NULL; + lan_wr(0, lan966x, ANA_MIRRORPORTS); + } + + if (ingress) + port->tc.ingress_mirror_id = 0; + else + port->tc.egress_mirror_id = 0; + + return 0; +} + +void lan966x_mirror_port_stats(struct lan966x_port *port, + struct flow_stats *stats, + bool ingress) +{ + struct rtnl_link_stats64 new_stats; + struct flow_stats *old_stats; + + old_stats = &port->tc.mirror_stat; + lan966x_stats_get(port->dev, &new_stats); + + if (ingress) { + flow_stats_update(stats, + new_stats.rx_bytes - old_stats->bytes, + new_stats.rx_packets - old_stats->pkts, + new_stats.rx_dropped - old_stats->drops, + old_stats->lastused, + FLOW_ACTION_HW_STATS_IMMEDIATE); + + old_stats->bytes = new_stats.rx_bytes; + old_stats->pkts = new_stats.rx_packets; + old_stats->drops = new_stats.rx_dropped; + old_stats->lastused = jiffies; + } else { + flow_stats_update(stats, + new_stats.tx_bytes - old_stats->bytes, + new_stats.tx_packets - old_stats->pkts, + new_stats.tx_dropped - old_stats->drops, + old_stats->lastused, + FLOW_ACTION_HW_STATS_IMMEDIATE); + + old_stats->bytes = new_stats.tx_bytes; + old_stats->pkts = new_stats.tx_packets; + old_stats->drops = new_stats.tx_dropped; + old_stats->lastused = jiffies; + } +} diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_mqprio.c b/drivers/net/ethernet/microchip/lan966x/lan966x_mqprio.c new file mode 100644 index 000000000000..7fa76e74f9e2 --- /dev/null +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_mqprio.c @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: GPL-2.0+ + +#include "lan966x_main.h" + +int lan966x_mqprio_add(struct lan966x_port *port, u8 num_tc) +{ + u8 i; + + if (num_tc != NUM_PRIO_QUEUES) { + netdev_err(port->dev, "Only %d traffic classes supported\n", + NUM_PRIO_QUEUES); + return -EINVAL; + } + + netdev_set_num_tc(port->dev, num_tc); + + for (i = 0; i < num_tc; ++i) + netdev_set_tc_queue(port->dev, i, 1, i); + + return 0; +} + +int lan966x_mqprio_del(struct lan966x_port *port) +{ + netdev_reset_tc(port->dev); + + return 0; +} diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_phylink.c b/drivers/net/ethernet/microchip/lan966x/lan966x_phylink.c index 38a7e95d69b4..e4ac59480514 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_phylink.c +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_phylink.c @@ -28,11 +28,12 @@ static int lan966x_phylink_mac_prepare(struct phylink_config *config, phy_interface_t iface) { struct lan966x_port *port = netdev_priv(to_net_dev(config->dev)); + phy_interface_t serdes_mode = iface; int err; if (port->serdes) { err = phy_set_mode_ext(port->serdes, PHY_MODE_ETHERNET, - iface); + serdes_mode); if (err) { netdev_err(to_net_dev(config->dev), "Could not set mode of SerDes\n"); @@ -59,6 +60,9 @@ static void lan966x_phylink_mac_link_up(struct phylink_config *config, port_config->pause |= tx_pause ? MLO_PAUSE_TX : 0; port_config->pause |= rx_pause ? MLO_PAUSE_RX : 0; + if (phy_interface_mode_is_rgmii(interface)) + phy_set_speed(port->serdes, speed); + lan966x_port_config_up(port); } diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_police.c b/drivers/net/ethernet/microchip/lan966x/lan966x_police.c new file mode 100644 index 000000000000..a9aec900d608 --- /dev/null +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_police.c @@ -0,0 +1,235 @@ +// SPDX-License-Identifier: GPL-2.0+ + +#include "lan966x_main.h" + +/* 0-8 : 9 port policers */ +#define POL_IDX_PORT 0 + +/* Policer order: Serial (QoS -> Port -> VCAP) */ +#define POL_ORDER 0x1d3 + +struct lan966x_tc_policer { + /* kilobit per second */ + u32 rate; + /* bytes */ + u32 burst; +}; + +static int lan966x_police_add(struct lan966x_port *port, + struct lan966x_tc_policer *pol, + u16 pol_idx) +{ + struct lan966x *lan966x = port->lan966x; + + /* Rate unit is 33 1/3 kpps */ + pol->rate = DIV_ROUND_UP(pol->rate * 3, 100); + /* Avoid zero burst size */ + pol->burst = pol->burst ?: 1; + /* Unit is 4kB */ + pol->burst = DIV_ROUND_UP(pol->burst, 4096); + + if (pol->rate > GENMASK(15, 0) || + pol->burst > GENMASK(6, 0)) + return -EINVAL; + + lan_wr(ANA_POL_MODE_DROP_ON_YELLOW_ENA_SET(0) | + ANA_POL_MODE_MARK_ALL_FRMS_RED_ENA_SET(0) | + ANA_POL_MODE_IPG_SIZE_SET(20) | + ANA_POL_MODE_FRM_MODE_SET(1) | + ANA_POL_MODE_OVERSHOOT_ENA_SET(1), + lan966x, ANA_POL_MODE(pol_idx)); + + lan_wr(ANA_POL_PIR_STATE_PIR_LVL_SET(0), + lan966x, ANA_POL_PIR_STATE(pol_idx)); + + lan_wr(ANA_POL_PIR_CFG_PIR_RATE_SET(pol->rate) | + ANA_POL_PIR_CFG_PIR_BURST_SET(pol->burst), + lan966x, ANA_POL_PIR_CFG(pol_idx)); + + return 0; +} + +static int lan966x_police_del(struct lan966x_port *port, + u16 pol_idx) +{ + struct lan966x *lan966x = port->lan966x; + + lan_wr(ANA_POL_MODE_DROP_ON_YELLOW_ENA_SET(0) | + ANA_POL_MODE_MARK_ALL_FRMS_RED_ENA_SET(0) | + ANA_POL_MODE_IPG_SIZE_SET(20) | + ANA_POL_MODE_FRM_MODE_SET(2) | + ANA_POL_MODE_OVERSHOOT_ENA_SET(1), + lan966x, ANA_POL_MODE(pol_idx)); + + lan_wr(ANA_POL_PIR_STATE_PIR_LVL_SET(0), + lan966x, ANA_POL_PIR_STATE(pol_idx)); + + lan_wr(ANA_POL_PIR_CFG_PIR_RATE_SET(GENMASK(14, 0)) | + ANA_POL_PIR_CFG_PIR_BURST_SET(0), + lan966x, ANA_POL_PIR_CFG(pol_idx)); + + return 0; +} + +static int lan966x_police_validate(struct lan966x_port *port, + const struct flow_action *action, + const struct flow_action_entry *act, + unsigned long police_id, + bool ingress, + struct netlink_ext_ack *extack) +{ + if (act->police.exceed.act_id != FLOW_ACTION_DROP) { + NL_SET_ERR_MSG_MOD(extack, + "Offload not supported when exceed action is not drop"); + return -EOPNOTSUPP; + } + + if (act->police.notexceed.act_id != FLOW_ACTION_PIPE && + act->police.notexceed.act_id != FLOW_ACTION_ACCEPT) { + NL_SET_ERR_MSG_MOD(extack, + "Offload not supported when conform action is not pipe or ok"); + return -EOPNOTSUPP; + } + + if (act->police.notexceed.act_id == FLOW_ACTION_ACCEPT && + !flow_action_is_last_entry(action, act)) { + NL_SET_ERR_MSG_MOD(extack, + "Offload not supported when conform action is ok, but action is not last"); + return -EOPNOTSUPP; + } + + if (act->police.peakrate_bytes_ps || + act->police.avrate || act->police.overhead) { + NL_SET_ERR_MSG_MOD(extack, + "Offload not supported when peakrate/avrate/overhead is configured"); + return -EOPNOTSUPP; + } + + if (act->police.rate_pkt_ps) { + NL_SET_ERR_MSG_MOD(extack, + "QoS offload not support packets per second"); + return -EOPNOTSUPP; + } + + if (!ingress) { + NL_SET_ERR_MSG_MOD(extack, + "Policer is not supported on egress"); + return -EOPNOTSUPP; + } + + if (port->tc.ingress_shared_block) { + NL_SET_ERR_MSG_MOD(extack, + "Policer is not supported on shared ingress blocks"); + return -EOPNOTSUPP; + } + + if (port->tc.police_id && port->tc.police_id != police_id) { + NL_SET_ERR_MSG_MOD(extack, + "Only one policer per port is supported"); + return -EEXIST; + } + + return 0; +} + +int lan966x_police_port_add(struct lan966x_port *port, + struct flow_action *action, + struct flow_action_entry *act, + unsigned long police_id, + bool ingress, + struct netlink_ext_ack *extack) +{ + struct lan966x *lan966x = port->lan966x; + struct rtnl_link_stats64 new_stats; + struct lan966x_tc_policer pol; + struct flow_stats *old_stats; + int err; + + err = lan966x_police_validate(port, action, act, police_id, ingress, + extack); + if (err) + return err; + + memset(&pol, 0, sizeof(pol)); + + pol.rate = div_u64(act->police.rate_bytes_ps, 1000) * 8; + pol.burst = act->police.burst; + + err = lan966x_police_add(port, &pol, POL_IDX_PORT + port->chip_port); + if (err) { + NL_SET_ERR_MSG_MOD(extack, + "Failed to add policer to port"); + return err; + } + + lan_rmw(ANA_POL_CFG_PORT_POL_ENA_SET(1) | + ANA_POL_CFG_POL_ORDER_SET(POL_ORDER), + ANA_POL_CFG_PORT_POL_ENA | + ANA_POL_CFG_POL_ORDER, + lan966x, ANA_POL_CFG(port->chip_port)); + + port->tc.police_id = police_id; + + /* Setup initial stats */ + old_stats = &port->tc.police_stat; + lan966x_stats_get(port->dev, &new_stats); + old_stats->bytes = new_stats.rx_bytes; + old_stats->pkts = new_stats.rx_packets; + old_stats->drops = new_stats.rx_dropped; + old_stats->lastused = jiffies; + + return 0; +} + +int lan966x_police_port_del(struct lan966x_port *port, + unsigned long police_id, + struct netlink_ext_ack *extack) +{ + struct lan966x *lan966x = port->lan966x; + int err; + + if (port->tc.police_id != police_id) { + NL_SET_ERR_MSG_MOD(extack, + "Invalid policer id"); + return -EINVAL; + } + + err = lan966x_police_del(port, port->tc.police_id); + if (err) { + NL_SET_ERR_MSG_MOD(extack, + "Failed to add policer to port"); + return err; + } + + lan_rmw(ANA_POL_CFG_PORT_POL_ENA_SET(0) | + ANA_POL_CFG_POL_ORDER_SET(POL_ORDER), + ANA_POL_CFG_PORT_POL_ENA | + ANA_POL_CFG_POL_ORDER, + lan966x, ANA_POL_CFG(port->chip_port)); + + port->tc.police_id = 0; + + return 0; +} + +void lan966x_police_port_stats(struct lan966x_port *port, + struct flow_stats *stats) +{ + struct rtnl_link_stats64 new_stats; + struct flow_stats *old_stats; + + old_stats = &port->tc.police_stat; + lan966x_stats_get(port->dev, &new_stats); + + flow_stats_update(stats, + new_stats.rx_bytes - old_stats->bytes, + new_stats.rx_packets - old_stats->pkts, + new_stats.rx_dropped - old_stats->drops, + old_stats->lastused, + FLOW_ACTION_HW_STATS_IMMEDIATE); + + old_stats->bytes = new_stats.rx_bytes; + old_stats->pkts = new_stats.rx_packets; + old_stats->drops = new_stats.rx_dropped; + old_stats->lastused = jiffies; +} diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_port.c b/drivers/net/ethernet/microchip/lan966x/lan966x_port.c index f141644e4372..1a61c6cdb077 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_port.c +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_port.c @@ -165,10 +165,12 @@ static void lan966x_port_link_up(struct lan966x_port *port) break; } + lan966x_taprio_speed_set(port, config->speed); + /* Also the GIGA_MODE_ENA(1) needs to be set regardless of the * port speed for QSGMII ports. */ - if (config->portmode == PHY_INTERFACE_MODE_QSGMII) + if (phy_interface_num_ports(config->portmode) == 4) mode = DEV_MAC_MODE_CFG_GIGA_MODE_ENA_SET(1); lan_wr(config->duplex | mode, @@ -331,10 +333,14 @@ int lan966x_port_pcs_set(struct lan966x_port *port, struct lan966x *lan966x = port->lan966x; bool inband_aneg = false; bool outband; + bool full_preamble = false; + + if (config->portmode == PHY_INTERFACE_MODE_QUSGMII) + full_preamble = true; if (config->inband) { if (config->portmode == PHY_INTERFACE_MODE_SGMII || - config->portmode == PHY_INTERFACE_MODE_QSGMII) + phy_interface_num_ports(config->portmode) == 4) inband_aneg = true; /* Cisco-SGMII in-band-aneg */ else if (config->portmode == PHY_INTERFACE_MODE_1000BASEX && config->autoneg) @@ -345,9 +351,15 @@ int lan966x_port_pcs_set(struct lan966x_port *port, outband = true; } - /* Disable or enable inband */ - lan_rmw(DEV_PCS1G_MODE_CFG_SGMII_MODE_ENA_SET(outband), - DEV_PCS1G_MODE_CFG_SGMII_MODE_ENA, + /* Disable or enable inband. + * For QUSGMII, we rely on the preamble to transmit data such as + * timestamps, therefore force full preamble transmission, and prevent + * premable shortening + */ + lan_rmw(DEV_PCS1G_MODE_CFG_SGMII_MODE_ENA_SET(outband) | + DEV_PCS1G_MODE_CFG_SAVE_PREAMBLE_ENA_SET(full_preamble), + DEV_PCS1G_MODE_CFG_SGMII_MODE_ENA | + DEV_PCS1G_MODE_CFG_SAVE_PREAMBLE_ENA, lan966x, DEV_PCS1G_MODE_CFG(port->chip_port)); /* Enable PCS */ @@ -396,7 +408,7 @@ void lan966x_port_init(struct lan966x_port *port) if (lan966x->fdma) lan966x_fdma_netdev_init(lan966x, port->dev); - if (config->portmode != PHY_INTERFACE_MODE_QSGMII) + if (phy_interface_num_ports(config->portmode) != 4) return; lan_rmw(DEV_CLOCK_CFG_PCS_RX_RST_SET(0) | diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_ptp.c b/drivers/net/ethernet/microchip/lan966x/lan966x_ptp.c index 3a621c5165bc..e5a2bbe064f8 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_ptp.c +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_ptp.c @@ -464,8 +464,7 @@ static int lan966x_ptp_settime64(struct ptp_clock_info *ptp, return 0; } -static int lan966x_ptp_gettime64(struct ptp_clock_info *ptp, - struct timespec64 *ts) +int lan966x_ptp_gettime64(struct ptp_clock_info *ptp, struct timespec64 *ts) { struct lan966x_phc *phc = container_of(ptp, struct lan966x_phc, info); struct lan966x *lan966x = phc->lan966x; @@ -890,3 +889,9 @@ void lan966x_ptp_rxtstamp(struct lan966x *lan966x, struct sk_buff *skb, shhwtstamps = skb_hwtstamps(skb); shhwtstamps->hwtstamp = full_ts_in_ns; } + +u32 lan966x_ptp_get_period_ps(void) +{ + /* This represents the system clock period in picoseconds */ + return 15125; +} diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_regs.h b/drivers/net/ethernet/microchip/lan966x/lan966x_regs.h index 8265ad89f0bc..1d90b93dd417 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_regs.h +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_regs.h @@ -90,6 +90,24 @@ enum lan966x_target { #define ANA_AUTOAGE_AGE_PERIOD_GET(x)\ FIELD_GET(ANA_AUTOAGE_AGE_PERIOD, x) +/* ANA:ANA:MIRRORPORTS */ +#define ANA_MIRRORPORTS __REG(TARGET_ANA, 0, 1, 29824, 0, 1, 244, 60, 0, 1, 4) + +#define ANA_MIRRORPORTS_MIRRORPORTS GENMASK(8, 0) +#define ANA_MIRRORPORTS_MIRRORPORTS_SET(x)\ + FIELD_PREP(ANA_MIRRORPORTS_MIRRORPORTS, x) +#define ANA_MIRRORPORTS_MIRRORPORTS_GET(x)\ + FIELD_GET(ANA_MIRRORPORTS_MIRRORPORTS, x) + +/* ANA:ANA:EMIRRORPORTS */ +#define ANA_EMIRRORPORTS __REG(TARGET_ANA, 0, 1, 29824, 0, 1, 244, 64, 0, 1, 4) + +#define ANA_EMIRRORPORTS_EMIRRORPORTS GENMASK(8, 0) +#define ANA_EMIRRORPORTS_EMIRRORPORTS_SET(x)\ + FIELD_PREP(ANA_EMIRRORPORTS_EMIRRORPORTS, x) +#define ANA_EMIRRORPORTS_EMIRRORPORTS_GET(x)\ + FIELD_GET(ANA_EMIRRORPORTS_EMIRRORPORTS, x) + /* ANA:ANA:FLOODING */ #define ANA_FLOODING(r) __REG(TARGET_ANA, 0, 1, 29824, 0, 1, 244, 68, r, 8, 4) @@ -330,6 +348,12 @@ enum lan966x_target { /* ANA:PORT:PORT_CFG */ #define ANA_PORT_CFG(g) __REG(TARGET_ANA, 0, 1, 28672, g, 9, 128, 112, 0, 1, 4) +#define ANA_PORT_CFG_SRC_MIRROR_ENA BIT(13) +#define ANA_PORT_CFG_SRC_MIRROR_ENA_SET(x)\ + FIELD_PREP(ANA_PORT_CFG_SRC_MIRROR_ENA, x) +#define ANA_PORT_CFG_SRC_MIRROR_ENA_GET(x)\ + FIELD_GET(ANA_PORT_CFG_SRC_MIRROR_ENA, x) + #define ANA_PORT_CFG_LEARNAUTO BIT(6) #define ANA_PORT_CFG_LEARNAUTO_SET(x)\ FIELD_PREP(ANA_PORT_CFG_LEARNAUTO, x) @@ -354,6 +378,21 @@ enum lan966x_target { #define ANA_PORT_CFG_PORTID_VAL_GET(x)\ FIELD_GET(ANA_PORT_CFG_PORTID_VAL, x) +/* ANA:PORT:POL_CFG */ +#define ANA_POL_CFG(g) __REG(TARGET_ANA, 0, 1, 28672, g, 9, 128, 116, 0, 1, 4) + +#define ANA_POL_CFG_PORT_POL_ENA BIT(17) +#define ANA_POL_CFG_PORT_POL_ENA_SET(x)\ + FIELD_PREP(ANA_POL_CFG_PORT_POL_ENA, x) +#define ANA_POL_CFG_PORT_POL_ENA_GET(x)\ + FIELD_GET(ANA_POL_CFG_PORT_POL_ENA, x) + +#define ANA_POL_CFG_POL_ORDER GENMASK(8, 0) +#define ANA_POL_CFG_POL_ORDER_SET(x)\ + FIELD_PREP(ANA_POL_CFG_POL_ORDER, x) +#define ANA_POL_CFG_POL_ORDER_GET(x)\ + FIELD_GET(ANA_POL_CFG_POL_ORDER, x) + /* ANA:PFC:PFC_CFG */ #define ANA_PFC_CFG(g) __REG(TARGET_ANA, 0, 1, 30720, g, 8, 64, 0, 0, 1, 4) @@ -363,6 +402,108 @@ enum lan966x_target { #define ANA_PFC_CFG_FC_LINK_SPEED_GET(x)\ FIELD_GET(ANA_PFC_CFG_FC_LINK_SPEED, x) +/* ANA:COMMON:AGGR_CFG */ +#define ANA_AGGR_CFG __REG(TARGET_ANA, 0, 1, 31232, 0, 1, 552, 0, 0, 1, 4) + +#define ANA_AGGR_CFG_AC_RND_ENA BIT(6) +#define ANA_AGGR_CFG_AC_RND_ENA_SET(x)\ + FIELD_PREP(ANA_AGGR_CFG_AC_RND_ENA, x) +#define ANA_AGGR_CFG_AC_RND_ENA_GET(x)\ + FIELD_GET(ANA_AGGR_CFG_AC_RND_ENA, x) + +#define ANA_AGGR_CFG_AC_DMAC_ENA BIT(5) +#define ANA_AGGR_CFG_AC_DMAC_ENA_SET(x)\ + FIELD_PREP(ANA_AGGR_CFG_AC_DMAC_ENA, x) +#define ANA_AGGR_CFG_AC_DMAC_ENA_GET(x)\ + FIELD_GET(ANA_AGGR_CFG_AC_DMAC_ENA, x) + +#define ANA_AGGR_CFG_AC_SMAC_ENA BIT(4) +#define ANA_AGGR_CFG_AC_SMAC_ENA_SET(x)\ + FIELD_PREP(ANA_AGGR_CFG_AC_SMAC_ENA, x) +#define ANA_AGGR_CFG_AC_SMAC_ENA_GET(x)\ + FIELD_GET(ANA_AGGR_CFG_AC_SMAC_ENA, x) + +#define ANA_AGGR_CFG_AC_IP6_FLOW_LBL_ENA BIT(3) +#define ANA_AGGR_CFG_AC_IP6_FLOW_LBL_ENA_SET(x)\ + FIELD_PREP(ANA_AGGR_CFG_AC_IP6_FLOW_LBL_ENA, x) +#define ANA_AGGR_CFG_AC_IP6_FLOW_LBL_ENA_GET(x)\ + FIELD_GET(ANA_AGGR_CFG_AC_IP6_FLOW_LBL_ENA, x) + +#define ANA_AGGR_CFG_AC_IP6_TCPUDP_ENA BIT(2) +#define ANA_AGGR_CFG_AC_IP6_TCPUDP_ENA_SET(x)\ + FIELD_PREP(ANA_AGGR_CFG_AC_IP6_TCPUDP_ENA, x) +#define ANA_AGGR_CFG_AC_IP6_TCPUDP_ENA_GET(x)\ + FIELD_GET(ANA_AGGR_CFG_AC_IP6_TCPUDP_ENA, x) + +#define ANA_AGGR_CFG_AC_IP4_SIPDIP_ENA BIT(1) +#define ANA_AGGR_CFG_AC_IP4_SIPDIP_ENA_SET(x)\ + FIELD_PREP(ANA_AGGR_CFG_AC_IP4_SIPDIP_ENA, x) +#define ANA_AGGR_CFG_AC_IP4_SIPDIP_ENA_GET(x)\ + FIELD_GET(ANA_AGGR_CFG_AC_IP4_SIPDIP_ENA, x) + +#define ANA_AGGR_CFG_AC_IP4_TCPUDP_ENA BIT(0) +#define ANA_AGGR_CFG_AC_IP4_TCPUDP_ENA_SET(x)\ + FIELD_PREP(ANA_AGGR_CFG_AC_IP4_TCPUDP_ENA, x) +#define ANA_AGGR_CFG_AC_IP4_TCPUDP_ENA_GET(x)\ + FIELD_GET(ANA_AGGR_CFG_AC_IP4_TCPUDP_ENA, x) + +/* ANA:POL:POL_PIR_CFG */ +#define ANA_POL_PIR_CFG(g) __REG(TARGET_ANA, 0, 1, 16384, g, 345, 32, 0, 0, 1, 4) + +#define ANA_POL_PIR_CFG_PIR_RATE GENMASK(20, 6) +#define ANA_POL_PIR_CFG_PIR_RATE_SET(x)\ + FIELD_PREP(ANA_POL_PIR_CFG_PIR_RATE, x) +#define ANA_POL_PIR_CFG_PIR_RATE_GET(x)\ + FIELD_GET(ANA_POL_PIR_CFG_PIR_RATE, x) + +#define ANA_POL_PIR_CFG_PIR_BURST GENMASK(5, 0) +#define ANA_POL_PIR_CFG_PIR_BURST_SET(x)\ + FIELD_PREP(ANA_POL_PIR_CFG_PIR_BURST, x) +#define ANA_POL_PIR_CFG_PIR_BURST_GET(x)\ + FIELD_GET(ANA_POL_PIR_CFG_PIR_BURST, x) + +/* ANA:POL:POL_MODE_CFG */ +#define ANA_POL_MODE(g) __REG(TARGET_ANA, 0, 1, 16384, g, 345, 32, 8, 0, 1, 4) + +#define ANA_POL_MODE_DROP_ON_YELLOW_ENA BIT(11) +#define ANA_POL_MODE_DROP_ON_YELLOW_ENA_SET(x)\ + FIELD_PREP(ANA_POL_MODE_DROP_ON_YELLOW_ENA, x) +#define ANA_POL_MODE_DROP_ON_YELLOW_ENA_GET(x)\ + FIELD_GET(ANA_POL_MODE_DROP_ON_YELLOW_ENA, x) + +#define ANA_POL_MODE_MARK_ALL_FRMS_RED_ENA BIT(10) +#define ANA_POL_MODE_MARK_ALL_FRMS_RED_ENA_SET(x)\ + FIELD_PREP(ANA_POL_MODE_MARK_ALL_FRMS_RED_ENA, x) +#define ANA_POL_MODE_MARK_ALL_FRMS_RED_ENA_GET(x)\ + FIELD_GET(ANA_POL_MODE_MARK_ALL_FRMS_RED_ENA, x) + +#define ANA_POL_MODE_IPG_SIZE GENMASK(9, 5) +#define ANA_POL_MODE_IPG_SIZE_SET(x)\ + FIELD_PREP(ANA_POL_MODE_IPG_SIZE, x) +#define ANA_POL_MODE_IPG_SIZE_GET(x)\ + FIELD_GET(ANA_POL_MODE_IPG_SIZE, x) + +#define ANA_POL_MODE_FRM_MODE GENMASK(4, 3) +#define ANA_POL_MODE_FRM_MODE_SET(x)\ + FIELD_PREP(ANA_POL_MODE_FRM_MODE, x) +#define ANA_POL_MODE_FRM_MODE_GET(x)\ + FIELD_GET(ANA_POL_MODE_FRM_MODE, x) + +#define ANA_POL_MODE_OVERSHOOT_ENA BIT(0) +#define ANA_POL_MODE_OVERSHOOT_ENA_SET(x)\ + FIELD_PREP(ANA_POL_MODE_OVERSHOOT_ENA, x) +#define ANA_POL_MODE_OVERSHOOT_ENA_GET(x)\ + FIELD_GET(ANA_POL_MODE_OVERSHOOT_ENA, x) + +/* ANA:POL:POL_PIR_STATE */ +#define ANA_POL_PIR_STATE(g) __REG(TARGET_ANA, 0, 1, 16384, g, 345, 32, 12, 0, 1, 4) + +#define ANA_POL_PIR_STATE_PIR_LVL GENMASK(21, 0) +#define ANA_POL_PIR_STATE_PIR_LVL_SET(x)\ + FIELD_PREP(ANA_POL_PIR_STATE_PIR_LVL, x) +#define ANA_POL_PIR_STATE_PIR_LVL_GET(x)\ + FIELD_GET(ANA_POL_PIR_STATE_PIR_LVL, x) + /* CHIP_TOP:CUPHY_CFG:CUPHY_PORT_CFG */ #define CHIP_TOP_CUPHY_PORT_CFG(r) __REG(TARGET_CHIP_TOP, 0, 1, 16, 0, 1, 20, 8, r, 2, 4) @@ -504,6 +645,12 @@ enum lan966x_target { #define DEV_PCS1G_MODE_CFG_SGMII_MODE_ENA_GET(x)\ FIELD_GET(DEV_PCS1G_MODE_CFG_SGMII_MODE_ENA, x) +#define DEV_PCS1G_MODE_CFG_SAVE_PREAMBLE_ENA BIT(1) +#define DEV_PCS1G_MODE_CFG_SAVE_PREAMBLE_ENA_SET(x)\ + FIELD_PREP(DEV_PCS1G_MODE_CFG_SAVE_PREAMBLE_ENA, x) +#define DEV_PCS1G_MODE_CFG_SAVE_PREAMBLE_ENA_GET(x)\ + FIELD_GET(DEV_PCS1G_MODE_CFG_SAVE_PREAMBLE_ENA, x) + /* DEV:PCS1G_CFG_STATUS:PCS1G_SD_CFG */ #define DEV_PCS1G_SD_CFG(t) __REG(TARGET_DEV, t, 8, 72, 0, 1, 68, 8, 0, 1, 4) @@ -967,6 +1114,215 @@ enum lan966x_target { /* QSYS:RES_CTRL:RES_CFG */ #define QSYS_RES_CFG(g) __REG(TARGET_QSYS, 0, 1, 32768, g, 1024, 8, 0, 0, 1, 4) +/* QSYS:HSCH:CIR_CFG */ +#define QSYS_CIR_CFG(g) __REG(TARGET_QSYS, 0, 1, 16384, g, 90, 128, 0, 0, 1, 4) + +#define QSYS_CIR_CFG_CIR_RATE GENMASK(20, 6) +#define QSYS_CIR_CFG_CIR_RATE_SET(x)\ + FIELD_PREP(QSYS_CIR_CFG_CIR_RATE, x) +#define QSYS_CIR_CFG_CIR_RATE_GET(x)\ + FIELD_GET(QSYS_CIR_CFG_CIR_RATE, x) + +#define QSYS_CIR_CFG_CIR_BURST GENMASK(5, 0) +#define QSYS_CIR_CFG_CIR_BURST_SET(x)\ + FIELD_PREP(QSYS_CIR_CFG_CIR_BURST, x) +#define QSYS_CIR_CFG_CIR_BURST_GET(x)\ + FIELD_GET(QSYS_CIR_CFG_CIR_BURST, x) + +/* QSYS:HSCH:SE_CFG */ +#define QSYS_SE_CFG(g) __REG(TARGET_QSYS, 0, 1, 16384, g, 90, 128, 8, 0, 1, 4) + +#define QSYS_SE_CFG_SE_DWRR_CNT GENMASK(9, 6) +#define QSYS_SE_CFG_SE_DWRR_CNT_SET(x)\ + FIELD_PREP(QSYS_SE_CFG_SE_DWRR_CNT, x) +#define QSYS_SE_CFG_SE_DWRR_CNT_GET(x)\ + FIELD_GET(QSYS_SE_CFG_SE_DWRR_CNT, x) + +#define QSYS_SE_CFG_SE_RR_ENA BIT(5) +#define QSYS_SE_CFG_SE_RR_ENA_SET(x)\ + FIELD_PREP(QSYS_SE_CFG_SE_RR_ENA, x) +#define QSYS_SE_CFG_SE_RR_ENA_GET(x)\ + FIELD_GET(QSYS_SE_CFG_SE_RR_ENA, x) + +#define QSYS_SE_CFG_SE_AVB_ENA BIT(4) +#define QSYS_SE_CFG_SE_AVB_ENA_SET(x)\ + FIELD_PREP(QSYS_SE_CFG_SE_AVB_ENA, x) +#define QSYS_SE_CFG_SE_AVB_ENA_GET(x)\ + FIELD_GET(QSYS_SE_CFG_SE_AVB_ENA, x) + +#define QSYS_SE_CFG_SE_FRM_MODE GENMASK(3, 2) +#define QSYS_SE_CFG_SE_FRM_MODE_SET(x)\ + FIELD_PREP(QSYS_SE_CFG_SE_FRM_MODE, x) +#define QSYS_SE_CFG_SE_FRM_MODE_GET(x)\ + FIELD_GET(QSYS_SE_CFG_SE_FRM_MODE, x) + +#define QSYS_SE_DWRR_CFG(g, r) __REG(TARGET_QSYS, 0, 1, 16384, g, 90, 128, 12, r, 12, 4) + +#define QSYS_SE_DWRR_CFG_DWRR_COST GENMASK(4, 0) +#define QSYS_SE_DWRR_CFG_DWRR_COST_SET(x)\ + FIELD_PREP(QSYS_SE_DWRR_CFG_DWRR_COST, x) +#define QSYS_SE_DWRR_CFG_DWRR_COST_GET(x)\ + FIELD_GET(QSYS_SE_DWRR_CFG_DWRR_COST, x) + +/* QSYS:TAS_CONFIG:TAS_CFG_CTRL */ +#define QSYS_TAS_CFG_CTRL __REG(TARGET_QSYS, 0, 1, 57372, 0, 1, 12, 0, 0, 1, 4) + +#define QSYS_TAS_CFG_CTRL_LIST_NUM_MAX GENMASK(27, 23) +#define QSYS_TAS_CFG_CTRL_LIST_NUM_MAX_SET(x)\ + FIELD_PREP(QSYS_TAS_CFG_CTRL_LIST_NUM_MAX, x) +#define QSYS_TAS_CFG_CTRL_LIST_NUM_MAX_GET(x)\ + FIELD_GET(QSYS_TAS_CFG_CTRL_LIST_NUM_MAX, x) + +#define QSYS_TAS_CFG_CTRL_LIST_NUM GENMASK(22, 18) +#define QSYS_TAS_CFG_CTRL_LIST_NUM_SET(x)\ + FIELD_PREP(QSYS_TAS_CFG_CTRL_LIST_NUM, x) +#define QSYS_TAS_CFG_CTRL_LIST_NUM_GET(x)\ + FIELD_GET(QSYS_TAS_CFG_CTRL_LIST_NUM, x) + +#define QSYS_TAS_CFG_CTRL_ALWAYS_GB_SCH_Q BIT(17) +#define QSYS_TAS_CFG_CTRL_ALWAYS_GB_SCH_Q_SET(x)\ + FIELD_PREP(QSYS_TAS_CFG_CTRL_ALWAYS_GB_SCH_Q, x) +#define QSYS_TAS_CFG_CTRL_ALWAYS_GB_SCH_Q_GET(x)\ + FIELD_GET(QSYS_TAS_CFG_CTRL_ALWAYS_GB_SCH_Q, x) + +#define QSYS_TAS_CFG_CTRL_GCL_ENTRY_NUM GENMASK(16, 5) +#define QSYS_TAS_CFG_CTRL_GCL_ENTRY_NUM_SET(x)\ + FIELD_PREP(QSYS_TAS_CFG_CTRL_GCL_ENTRY_NUM, x) +#define QSYS_TAS_CFG_CTRL_GCL_ENTRY_NUM_GET(x)\ + FIELD_GET(QSYS_TAS_CFG_CTRL_GCL_ENTRY_NUM, x) + +/* QSYS:TAS_CONFIG:TAS_GATE_STATE_CTRL */ +#define QSYS_TAS_GS_CTRL __REG(TARGET_QSYS, 0, 1, 57372, 0, 1, 12, 4, 0, 1, 4) + +#define QSYS_TAS_GS_CTRL_HSCH_POS GENMASK(2, 0) +#define QSYS_TAS_GS_CTRL_HSCH_POS_SET(x)\ + FIELD_PREP(QSYS_TAS_GS_CTRL_HSCH_POS, x) +#define QSYS_TAS_GS_CTRL_HSCH_POS_GET(x)\ + FIELD_GET(QSYS_TAS_GS_CTRL_HSCH_POS, x) + +/* QSYS:TAS_CONFIG:TAS_STATEMACHINE_CFG */ +#define QSYS_TAS_STM_CFG __REG(TARGET_QSYS, 0, 1, 57372, 0, 1, 12, 8, 0, 1, 4) + +#define QSYS_TAS_STM_CFG_REVISIT_DLY GENMASK(7, 0) +#define QSYS_TAS_STM_CFG_REVISIT_DLY_SET(x)\ + FIELD_PREP(QSYS_TAS_STM_CFG_REVISIT_DLY, x) +#define QSYS_TAS_STM_CFG_REVISIT_DLY_GET(x)\ + FIELD_GET(QSYS_TAS_STM_CFG_REVISIT_DLY, x) + +/* QSYS:TAS_PROFILE_CFG:TAS_PROFILE_CONFIG */ +#define QSYS_TAS_PROFILE_CFG(g) __REG(TARGET_QSYS, 0, 1, 30720, g, 16, 64, 32, 0, 1, 4) + +#define QSYS_TAS_PROFILE_CFG_PORT_NUM GENMASK(21, 19) +#define QSYS_TAS_PROFILE_CFG_PORT_NUM_SET(x)\ + FIELD_PREP(QSYS_TAS_PROFILE_CFG_PORT_NUM, x) +#define QSYS_TAS_PROFILE_CFG_PORT_NUM_GET(x)\ + FIELD_GET(QSYS_TAS_PROFILE_CFG_PORT_NUM, x) + +#define QSYS_TAS_PROFILE_CFG_LINK_SPEED GENMASK(18, 16) +#define QSYS_TAS_PROFILE_CFG_LINK_SPEED_SET(x)\ + FIELD_PREP(QSYS_TAS_PROFILE_CFG_LINK_SPEED, x) +#define QSYS_TAS_PROFILE_CFG_LINK_SPEED_GET(x)\ + FIELD_GET(QSYS_TAS_PROFILE_CFG_LINK_SPEED, x) + +/* QSYS:TAS_LIST_CFG:TAS_BASE_TIME_NSEC */ +#define QSYS_TAS_BT_NSEC __REG(TARGET_QSYS, 0, 1, 27904, 0, 1, 64, 0, 0, 1, 4) + +#define QSYS_TAS_BT_NSEC_NSEC GENMASK(29, 0) +#define QSYS_TAS_BT_NSEC_NSEC_SET(x)\ + FIELD_PREP(QSYS_TAS_BT_NSEC_NSEC, x) +#define QSYS_TAS_BT_NSEC_NSEC_GET(x)\ + FIELD_GET(QSYS_TAS_BT_NSEC_NSEC, x) + +/* QSYS:TAS_LIST_CFG:TAS_BASE_TIME_SEC_LSB */ +#define QSYS_TAS_BT_SEC_LSB __REG(TARGET_QSYS, 0, 1, 27904, 0, 1, 64, 4, 0, 1, 4) + +/* QSYS:TAS_LIST_CFG:TAS_BASE_TIME_SEC_MSB */ +#define QSYS_TAS_BT_SEC_MSB __REG(TARGET_QSYS, 0, 1, 27904, 0, 1, 64, 8, 0, 1, 4) + +#define QSYS_TAS_BT_SEC_MSB_SEC_MSB GENMASK(15, 0) +#define QSYS_TAS_BT_SEC_MSB_SEC_MSB_SET(x)\ + FIELD_PREP(QSYS_TAS_BT_SEC_MSB_SEC_MSB, x) +#define QSYS_TAS_BT_SEC_MSB_SEC_MSB_GET(x)\ + FIELD_GET(QSYS_TAS_BT_SEC_MSB_SEC_MSB, x) + +/* QSYS:TAS_LIST_CFG:TAS_CYCLE_TIME_CFG */ +#define QSYS_TAS_CT_CFG __REG(TARGET_QSYS, 0, 1, 27904, 0, 1, 64, 24, 0, 1, 4) + +/* QSYS:TAS_LIST_CFG:TAS_STARTUP_CFG */ +#define QSYS_TAS_STARTUP_CFG __REG(TARGET_QSYS, 0, 1, 27904, 0, 1, 64, 28, 0, 1, 4) + +#define QSYS_TAS_STARTUP_CFG_OBSOLETE_IDX GENMASK(27, 23) +#define QSYS_TAS_STARTUP_CFG_OBSOLETE_IDX_SET(x)\ + FIELD_PREP(QSYS_TAS_STARTUP_CFG_OBSOLETE_IDX, x) +#define QSYS_TAS_STARTUP_CFG_OBSOLETE_IDX_GET(x)\ + FIELD_GET(QSYS_TAS_STARTUP_CFG_OBSOLETE_IDX, x) + +/* QSYS:TAS_LIST_CFG:TAS_LIST_CFG */ +#define QSYS_TAS_LIST_CFG __REG(TARGET_QSYS, 0, 1, 27904, 0, 1, 64, 32, 0, 1, 4) + +#define QSYS_TAS_LIST_CFG_LIST_BASE_ADDR GENMASK(11, 0) +#define QSYS_TAS_LIST_CFG_LIST_BASE_ADDR_SET(x)\ + FIELD_PREP(QSYS_TAS_LIST_CFG_LIST_BASE_ADDR, x) +#define QSYS_TAS_LIST_CFG_LIST_BASE_ADDR_GET(x)\ + FIELD_GET(QSYS_TAS_LIST_CFG_LIST_BASE_ADDR, x) + +/* QSYS:TAS_LIST_CFG:TAS_LIST_STATE */ +#define QSYS_TAS_LST __REG(TARGET_QSYS, 0, 1, 27904, 0, 1, 64, 36, 0, 1, 4) + +#define QSYS_TAS_LST_LIST_STATE GENMASK(2, 0) +#define QSYS_TAS_LST_LIST_STATE_SET(x)\ + FIELD_PREP(QSYS_TAS_LST_LIST_STATE, x) +#define QSYS_TAS_LST_LIST_STATE_GET(x)\ + FIELD_GET(QSYS_TAS_LST_LIST_STATE, x) + +/* QSYS:TAS_GCL_CFG:TAS_GCL_CTRL_CFG */ +#define QSYS_TAS_GCL_CT_CFG __REG(TARGET_QSYS, 0, 1, 27968, 0, 1, 16, 0, 0, 1, 4) + +#define QSYS_TAS_GCL_CT_CFG_HSCH_POS GENMASK(12, 10) +#define QSYS_TAS_GCL_CT_CFG_HSCH_POS_SET(x)\ + FIELD_PREP(QSYS_TAS_GCL_CT_CFG_HSCH_POS, x) +#define QSYS_TAS_GCL_CT_CFG_HSCH_POS_GET(x)\ + FIELD_GET(QSYS_TAS_GCL_CT_CFG_HSCH_POS, x) + +#define QSYS_TAS_GCL_CT_CFG_GATE_STATE GENMASK(9, 2) +#define QSYS_TAS_GCL_CT_CFG_GATE_STATE_SET(x)\ + FIELD_PREP(QSYS_TAS_GCL_CT_CFG_GATE_STATE, x) +#define QSYS_TAS_GCL_CT_CFG_GATE_STATE_GET(x)\ + FIELD_GET(QSYS_TAS_GCL_CT_CFG_GATE_STATE, x) + +#define QSYS_TAS_GCL_CT_CFG_OP_TYPE GENMASK(1, 0) +#define QSYS_TAS_GCL_CT_CFG_OP_TYPE_SET(x)\ + FIELD_PREP(QSYS_TAS_GCL_CT_CFG_OP_TYPE, x) +#define QSYS_TAS_GCL_CT_CFG_OP_TYPE_GET(x)\ + FIELD_GET(QSYS_TAS_GCL_CT_CFG_OP_TYPE, x) + +/* QSYS:TAS_GCL_CFG:TAS_GCL_CTRL_CFG2 */ +#define QSYS_TAS_GCL_CT_CFG2 __REG(TARGET_QSYS, 0, 1, 27968, 0, 1, 16, 4, 0, 1, 4) + +#define QSYS_TAS_GCL_CT_CFG2_PORT_PROFILE GENMASK(15, 12) +#define QSYS_TAS_GCL_CT_CFG2_PORT_PROFILE_SET(x)\ + FIELD_PREP(QSYS_TAS_GCL_CT_CFG2_PORT_PROFILE, x) +#define QSYS_TAS_GCL_CT_CFG2_PORT_PROFILE_GET(x)\ + FIELD_GET(QSYS_TAS_GCL_CT_CFG2_PORT_PROFILE, x) + +#define QSYS_TAS_GCL_CT_CFG2_NEXT_GCL GENMASK(11, 0) +#define QSYS_TAS_GCL_CT_CFG2_NEXT_GCL_SET(x)\ + FIELD_PREP(QSYS_TAS_GCL_CT_CFG2_NEXT_GCL, x) +#define QSYS_TAS_GCL_CT_CFG2_NEXT_GCL_GET(x)\ + FIELD_GET(QSYS_TAS_GCL_CT_CFG2_NEXT_GCL, x) + +/* QSYS:TAS_GCL_CFG:TAS_GCL_TIME_CFG */ +#define QSYS_TAS_GCL_TM_CFG __REG(TARGET_QSYS, 0, 1, 27968, 0, 1, 16, 8, 0, 1, 4) + +/* QSYS:HSCH_TAS_STATE:TAS_GATE_STATE */ +#define QSYS_TAS_GATE_STATE __REG(TARGET_QSYS, 0, 1, 28004, 0, 1, 4, 0, 0, 1, 4) + +#define QSYS_TAS_GATE_STATE_TAS_GATE_STATE GENMASK(7, 0) +#define QSYS_TAS_GATE_STATE_TAS_GATE_STATE_SET(x)\ + FIELD_PREP(QSYS_TAS_GATE_STATE_TAS_GATE_STATE, x) +#define QSYS_TAS_GATE_STATE_TAS_GATE_STATE_GET(x)\ + FIELD_GET(QSYS_TAS_GATE_STATE_TAS_GATE_STATE, x) + /* REW:PORT:PORT_VLAN_CFG */ #define REW_PORT_VLAN_CFG(g) __REG(TARGET_REW, 0, 1, 0, g, 10, 128, 0, 0, 1, 4) diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_switchdev.c b/drivers/net/ethernet/microchip/lan966x/lan966x_switchdev.c index df2bee678559..1c88120eb291 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_switchdev.c +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_switchdev.c @@ -6,8 +6,6 @@ #include "lan966x_main.h" static struct notifier_block lan966x_netdevice_nb __read_mostly; -static struct notifier_block lan966x_switchdev_nb __read_mostly; -static struct notifier_block lan966x_switchdev_blocking_nb __read_mostly; static void lan966x_port_set_mcast_ip_flood(struct lan966x_port *port, u32 pgid_ip) @@ -132,7 +130,7 @@ static int lan966x_port_pre_bridge_flags(struct lan966x_port *port, return 0; } -static void lan966x_update_fwd_mask(struct lan966x *lan966x) +void lan966x_update_fwd_mask(struct lan966x *lan966x) { int i; @@ -140,9 +138,14 @@ static void lan966x_update_fwd_mask(struct lan966x *lan966x) struct lan966x_port *port = lan966x->ports[i]; unsigned long mask = 0; - if (port && lan966x->bridge_fwd_mask & BIT(i)) + if (port && lan966x->bridge_fwd_mask & BIT(i)) { mask = lan966x->bridge_fwd_mask & ~BIT(i); + if (port->bond) + mask &= ~lan966x_lag_get_mask(lan966x, + port->bond); + } + mask |= BIT(CPU_PORT); lan_wr(ANA_PGID_PGID_SET(mask), @@ -150,7 +153,7 @@ static void lan966x_update_fwd_mask(struct lan966x *lan966x) } } -static void lan966x_port_stp_state_set(struct lan966x_port *port, u8 state) +void lan966x_port_stp_state_set(struct lan966x_port *port, u8 state) { struct lan966x *lan966x = port->lan966x; bool learn_ena = false; @@ -171,8 +174,8 @@ static void lan966x_port_stp_state_set(struct lan966x_port *port, u8 state) lan966x_update_fwd_mask(lan966x); } -static void lan966x_port_ageing_set(struct lan966x_port *port, - unsigned long ageing_clock_t) +void lan966x_port_ageing_set(struct lan966x_port *port, + unsigned long ageing_clock_t) { unsigned long ageing_jiffies = clock_t_to_jiffies(ageing_clock_t); u32 ageing_time = jiffies_to_msecs(ageing_jiffies) / 1000; @@ -241,6 +244,7 @@ static int lan966x_port_attr_set(struct net_device *dev, const void *ctx, } static int lan966x_port_bridge_join(struct lan966x_port *port, + struct net_device *brport_dev, struct net_device *bridge, struct netlink_ext_ack *extack) { @@ -258,7 +262,7 @@ static int lan966x_port_bridge_join(struct lan966x_port *port, } } - err = switchdev_bridge_port_offload(dev, dev, port, + err = switchdev_bridge_port_offload(brport_dev, dev, port, &lan966x_switchdev_nb, &lan966x_switchdev_blocking_nb, false, extack); @@ -295,8 +299,9 @@ static void lan966x_port_bridge_leave(struct lan966x_port *port, lan966x_vlan_port_apply(port); } -static int lan966x_port_changeupper(struct net_device *dev, - struct netdev_notifier_changeupper_info *info) +int lan966x_port_changeupper(struct net_device *dev, + struct net_device *brport_dev, + struct netdev_notifier_changeupper_info *info) { struct lan966x_port *port = netdev_priv(dev); struct netlink_ext_ack *extack; @@ -306,44 +311,68 @@ static int lan966x_port_changeupper(struct net_device *dev, if (netif_is_bridge_master(info->upper_dev)) { if (info->linking) - err = lan966x_port_bridge_join(port, info->upper_dev, + err = lan966x_port_bridge_join(port, brport_dev, + info->upper_dev, extack); else lan966x_port_bridge_leave(port, info->upper_dev); } + if (netif_is_lag_master(info->upper_dev)) { + if (info->linking) + err = lan966x_lag_port_join(port, info->upper_dev, + info->upper_dev, + extack); + else + lan966x_lag_port_leave(port, info->upper_dev); + } + return err; } -static int lan966x_port_prechangeupper(struct net_device *dev, - struct netdev_notifier_changeupper_info *info) +int lan966x_port_prechangeupper(struct net_device *dev, + struct net_device *brport_dev, + struct netdev_notifier_changeupper_info *info) { struct lan966x_port *port = netdev_priv(dev); + int err = NOTIFY_DONE; - if (netif_is_bridge_master(info->upper_dev) && !info->linking) - switchdev_bridge_port_unoffload(port->dev, port, - NULL, NULL); + if (netif_is_bridge_master(info->upper_dev) && !info->linking) { + switchdev_bridge_port_unoffload(port->dev, port, NULL, NULL); + lan966x_fdb_flush_workqueue(port->lan966x); + } - return NOTIFY_DONE; + if (netif_is_lag_master(info->upper_dev)) { + err = lan966x_lag_port_prechangeupper(dev, info); + if (err || info->linking) + return err; + + switchdev_bridge_port_unoffload(brport_dev, port, NULL, NULL); + lan966x_fdb_flush_workqueue(port->lan966x); + } + + return err; } -static int lan966x_foreign_bridging_check(struct net_device *bridge, +static int lan966x_foreign_bridging_check(struct net_device *upper, + bool *has_foreign, + bool *seen_lan966x, struct netlink_ext_ack *extack) { struct lan966x *lan966x = NULL; - bool has_foreign = false; struct net_device *dev; struct list_head *iter; - if (!netif_is_bridge_master(bridge)) + if (!netif_is_bridge_master(upper) && + !netif_is_lag_master(upper)) return 0; - netdev_for_each_lower_dev(bridge, dev, iter) { + netdev_for_each_lower_dev(upper, dev, iter) { if (lan966x_netdevice_check(dev)) { struct lan966x_port *port = netdev_priv(dev); if (lan966x) { - /* Bridge already has at least one port of a + /* Upper already has at least one port of a * lan966x switch inside it, check that it's * the same instance of the driver. */ @@ -354,15 +383,24 @@ static int lan966x_foreign_bridging_check(struct net_device *bridge, } } else { /* This is the first lan966x port inside this - * bridge + * upper device */ lan966x = port->lan966x; + *seen_lan966x = true; } + } else if (netif_is_lag_master(dev)) { + /* Allow to have bond interfaces that have only lan966x + * devices + */ + if (lan966x_foreign_bridging_check(dev, has_foreign, + seen_lan966x, + extack)) + return -EINVAL; } else { - has_foreign = true; + *has_foreign = true; } - if (lan966x && has_foreign) { + if (*seen_lan966x && *has_foreign) { NL_SET_ERR_MSG_MOD(extack, "Bridging lan966x ports with foreign interfaces disallowed"); return -EINVAL; @@ -375,7 +413,12 @@ static int lan966x_foreign_bridging_check(struct net_device *bridge, static int lan966x_bridge_check(struct net_device *dev, struct netdev_notifier_changeupper_info *info) { + bool has_foreign = false; + bool seen_lan966x = false; + return lan966x_foreign_bridging_check(info->upper_dev, + &has_foreign, + &seen_lan966x, info->info.extack); } @@ -386,21 +429,44 @@ static int lan966x_netdevice_port_event(struct net_device *dev, int err = 0; if (!lan966x_netdevice_check(dev)) { - if (event == NETDEV_CHANGEUPPER) - return lan966x_bridge_check(dev, ptr); + switch (event) { + case NETDEV_CHANGEUPPER: + case NETDEV_PRECHANGEUPPER: + err = lan966x_bridge_check(dev, ptr); + if (err) + return err; + + if (netif_is_lag_master(dev)) { + if (event == NETDEV_CHANGEUPPER) + err = lan966x_lag_netdev_changeupper(dev, + ptr); + else + err = lan966x_lag_netdev_prechangeupper(dev, + ptr); + + return err; + } + break; + default: + return 0; + } + return 0; } switch (event) { case NETDEV_PRECHANGEUPPER: - err = lan966x_port_prechangeupper(dev, ptr); + err = lan966x_port_prechangeupper(dev, dev, ptr); break; case NETDEV_CHANGEUPPER: err = lan966x_bridge_check(dev, ptr); if (err) return err; - err = lan966x_port_changeupper(dev, ptr); + err = lan966x_port_changeupper(dev, dev, ptr); + break; + case NETDEV_CHANGELOWERSTATE: + err = lan966x_lag_port_changelowerstate(dev, ptr); break; } @@ -418,19 +484,23 @@ static int lan966x_netdevice_event(struct notifier_block *nb, return notifier_from_errno(ret); } -/* We don't offload uppers such as LAG as bridge ports, so every device except - * the bridge itself is foreign. - */ static bool lan966x_foreign_dev_check(const struct net_device *dev, const struct net_device *foreign_dev) { struct lan966x_port *port = netdev_priv(dev); struct lan966x *lan966x = port->lan966x; + int i; if (netif_is_bridge_master(foreign_dev)) if (lan966x->bridge == foreign_dev) return false; + if (netif_is_lag_master(foreign_dev)) + for (i = 0; i < lan966x->num_phys_ports; ++i) + if (lan966x->ports[i] && + lan966x->ports[i]->bond == foreign_dev) + return false; + return true; } @@ -571,11 +641,11 @@ static struct notifier_block lan966x_netdevice_nb __read_mostly = { .notifier_call = lan966x_netdevice_event, }; -static struct notifier_block lan966x_switchdev_nb __read_mostly = { +struct notifier_block lan966x_switchdev_nb __read_mostly = { .notifier_call = lan966x_switchdev_event, }; -static struct notifier_block lan966x_switchdev_blocking_nb __read_mostly = { +struct notifier_block lan966x_switchdev_blocking_nb __read_mostly = { .notifier_call = lan966x_switchdev_blocking_event, }; diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_taprio.c b/drivers/net/ethernet/microchip/lan966x/lan966x_taprio.c new file mode 100644 index 000000000000..3f5b212066c5 --- /dev/null +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_taprio.c @@ -0,0 +1,528 @@ +// SPDX-License-Identifier: GPL-2.0+ + +#include "lan966x_main.h" + +#define LAN966X_TAPRIO_TIMEOUT_MS 1000 +#define LAN966X_TAPRIO_ENTRIES_PER_PORT 2 + +/* Minimum supported cycle time in nanoseconds */ +#define LAN966X_TAPRIO_MIN_CYCLE_TIME_NS NSEC_PER_USEC + +/* Maximum supported cycle time in nanoseconds */ +#define LAN966X_TAPRIO_MAX_CYCLE_TIME_NS (NSEC_PER_SEC - 1) + +/* Total number of TAS GCL entries */ +#define LAN966X_TAPRIO_NUM_GCL 256 + +/* TAPRIO link speeds for calculation of guard band */ +enum lan966x_taprio_link_speed { + LAN966X_TAPRIO_SPEED_NO_GB, + LAN966X_TAPRIO_SPEED_10, + LAN966X_TAPRIO_SPEED_100, + LAN966X_TAPRIO_SPEED_1000, + LAN966X_TAPRIO_SPEED_2500, +}; + +/* TAPRIO list states */ +enum lan966x_taprio_state { + LAN966X_TAPRIO_STATE_ADMIN, + LAN966X_TAPRIO_STATE_ADVANCING, + LAN966X_TAPRIO_STATE_PENDING, + LAN966X_TAPRIO_STATE_OPERATING, + LAN966X_TAPRIO_STATE_TERMINATING, + LAN966X_TAPRIO_STATE_MAX, +}; + +/* TAPRIO GCL command */ +enum lan966x_taprio_gcl_cmd { + LAN966X_TAPRIO_GCL_CMD_SET_GATE_STATES = 0, +}; + +static u32 lan966x_taprio_list_index(struct lan966x_port *port, u8 entry) +{ + return port->chip_port * LAN966X_TAPRIO_ENTRIES_PER_PORT + entry; +} + +static u32 lan966x_taprio_list_state_get(struct lan966x_port *port) +{ + struct lan966x *lan966x = port->lan966x; + u32 val; + + val = lan_rd(lan966x, QSYS_TAS_LST); + return QSYS_TAS_LST_LIST_STATE_GET(val); +} + +static u32 lan966x_taprio_list_index_state_get(struct lan966x_port *port, + u32 list) +{ + struct lan966x *lan966x = port->lan966x; + + lan_rmw(QSYS_TAS_CFG_CTRL_LIST_NUM_SET(list), + QSYS_TAS_CFG_CTRL_LIST_NUM, + lan966x, QSYS_TAS_CFG_CTRL); + + return lan966x_taprio_list_state_get(port); +} + +static void lan966x_taprio_list_state_set(struct lan966x_port *port, + u32 state) +{ + struct lan966x *lan966x = port->lan966x; + + lan_rmw(QSYS_TAS_LST_LIST_STATE_SET(state), + QSYS_TAS_LST_LIST_STATE, + lan966x, QSYS_TAS_LST); +} + +static int lan966x_taprio_list_shutdown(struct lan966x_port *port, + u32 list) +{ + struct lan966x *lan966x = port->lan966x; + bool pending, operating; + unsigned long end; + u32 state; + + end = jiffies + msecs_to_jiffies(LAN966X_TAPRIO_TIMEOUT_MS); + /* It is required to try multiple times to set the state of list, + * because the HW can overwrite this. + */ + do { + state = lan966x_taprio_list_state_get(port); + + pending = false; + operating = false; + + if (state == LAN966X_TAPRIO_STATE_ADVANCING || + state == LAN966X_TAPRIO_STATE_PENDING) { + lan966x_taprio_list_state_set(port, + LAN966X_TAPRIO_STATE_ADMIN); + pending = true; + } + + if (state == LAN966X_TAPRIO_STATE_OPERATING) { + lan966x_taprio_list_state_set(port, + LAN966X_TAPRIO_STATE_TERMINATING); + operating = true; + } + + /* If the entry was in pending and now gets in admin, then there + * is nothing else to do, so just bail out + */ + state = lan966x_taprio_list_state_get(port); + if (pending && + state == LAN966X_TAPRIO_STATE_ADMIN) + return 0; + + /* If the list was in operating and now is in terminating or + * admin, then is OK to exit but it needs to wait until the list + * will get in admin. It is not required to set the state + * again. + */ + if (operating && + (state == LAN966X_TAPRIO_STATE_TERMINATING || + state == LAN966X_TAPRIO_STATE_ADMIN)) + break; + + } while (!time_after(jiffies, end)); + + end = jiffies + msecs_to_jiffies(LAN966X_TAPRIO_TIMEOUT_MS); + do { + state = lan966x_taprio_list_state_get(port); + if (state == LAN966X_TAPRIO_STATE_ADMIN) + break; + + } while (!time_after(jiffies, end)); + + /* If the list was in operating mode, it could be stopped while some + * queues where closed, so make sure to restore "all-queues-open" + */ + if (operating) { + lan_wr(QSYS_TAS_GS_CTRL_HSCH_POS_SET(port->chip_port), + lan966x, QSYS_TAS_GS_CTRL); + + lan_wr(QSYS_TAS_GATE_STATE_TAS_GATE_STATE_SET(0xff), + lan966x, QSYS_TAS_GATE_STATE); + } + + return 0; +} + +static int lan966x_taprio_shutdown(struct lan966x_port *port) +{ + u32 i, list, state; + int err; + + for (i = 0; i < LAN966X_TAPRIO_ENTRIES_PER_PORT; ++i) { + list = lan966x_taprio_list_index(port, i); + state = lan966x_taprio_list_index_state_get(port, list); + if (state == LAN966X_TAPRIO_STATE_ADMIN) + continue; + + err = lan966x_taprio_list_shutdown(port, list); + if (err) + return err; + } + + return 0; +} + +/* Find a suitable list for a new schedule. First priority is a list in state + * pending. Second priority is a list in state admin. + */ +static int lan966x_taprio_find_list(struct lan966x_port *port, + struct tc_taprio_qopt_offload *qopt, + int *new_list, int *obs_list) +{ + int state[LAN966X_TAPRIO_ENTRIES_PER_PORT]; + int list[LAN966X_TAPRIO_ENTRIES_PER_PORT]; + int err, oper = -1; + u32 i; + + *new_list = -1; + *obs_list = -1; + + /* If there is already an entry in operating mode, return this list in + * obs_list, such that when the new list will get activated the + * operating list will be stopped. In this way is possible to have + * smooth transitions between the lists + */ + for (i = 0; i < LAN966X_TAPRIO_ENTRIES_PER_PORT; ++i) { + list[i] = lan966x_taprio_list_index(port, i); + state[i] = lan966x_taprio_list_index_state_get(port, list[i]); + if (state[i] == LAN966X_TAPRIO_STATE_OPERATING) + oper = list[i]; + } + + for (i = 0; i < LAN966X_TAPRIO_ENTRIES_PER_PORT; ++i) { + if (state[i] == LAN966X_TAPRIO_STATE_PENDING) { + err = lan966x_taprio_shutdown(port); + if (err) + return err; + + *new_list = list[i]; + *obs_list = (oper == -1) ? *new_list : oper; + return 0; + } + } + + for (i = 0; i < LAN966X_TAPRIO_ENTRIES_PER_PORT; ++i) { + if (state[i] == LAN966X_TAPRIO_STATE_ADMIN) { + *new_list = list[i]; + *obs_list = (oper == -1) ? *new_list : oper; + return 0; + } + } + + return -ENOSPC; +} + +static int lan966x_taprio_check(struct tc_taprio_qopt_offload *qopt) +{ + u64 total_time = 0; + u32 i; + + /* This is not supported by th HW */ + if (qopt->cycle_time_extension) + return -EOPNOTSUPP; + + /* There is a limited number of gcl entries that can be used, they are + * shared by all ports + */ + if (qopt->num_entries > LAN966X_TAPRIO_NUM_GCL) + return -EINVAL; + + /* Don't allow cycle times bigger than 1 sec or smaller than 1 usec */ + if (qopt->cycle_time < LAN966X_TAPRIO_MIN_CYCLE_TIME_NS || + qopt->cycle_time > LAN966X_TAPRIO_MAX_CYCLE_TIME_NS) + return -EINVAL; + + for (i = 0; i < qopt->num_entries; ++i) { + struct tc_taprio_sched_entry *entry = &qopt->entries[i]; + + /* Don't allow intervals bigger than 1 sec or smaller than 1 + * usec + */ + if (entry->interval < LAN966X_TAPRIO_MIN_CYCLE_TIME_NS || + entry->interval > LAN966X_TAPRIO_MAX_CYCLE_TIME_NS) + return -EINVAL; + + if (qopt->entries[i].command != TC_TAPRIO_CMD_SET_GATES) + return -EINVAL; + + total_time += qopt->entries[i].interval; + } + + /* Don't allow the total time of intervals be bigger than 1 sec */ + if (total_time > LAN966X_TAPRIO_MAX_CYCLE_TIME_NS) + return -EINVAL; + + /* The HW expects that the cycle time to be at least as big as sum of + * each interval of gcl + */ + if (qopt->cycle_time < total_time) + return -EINVAL; + + return 0; +} + +static int lan966x_taprio_gcl_free_get(struct lan966x_port *port, + unsigned long *free_list) +{ + struct lan966x *lan966x = port->lan966x; + u32 num_free, state, list; + u32 base, next, max_list; + + /* By default everything is free */ + bitmap_fill(free_list, LAN966X_TAPRIO_NUM_GCL); + num_free = LAN966X_TAPRIO_NUM_GCL; + + /* Iterate over all gcl entries and find out which are free. And mark + * those that are not free. + */ + max_list = lan966x->num_phys_ports * LAN966X_TAPRIO_ENTRIES_PER_PORT; + for (list = 0; list < max_list; ++list) { + state = lan966x_taprio_list_index_state_get(port, list); + if (state == LAN966X_TAPRIO_STATE_ADMIN) + continue; + + base = lan_rd(lan966x, QSYS_TAS_LIST_CFG); + base = QSYS_TAS_LIST_CFG_LIST_BASE_ADDR_GET(base); + next = base; + + do { + clear_bit(next, free_list); + num_free--; + + lan_rmw(QSYS_TAS_CFG_CTRL_GCL_ENTRY_NUM_SET(next), + QSYS_TAS_CFG_CTRL_GCL_ENTRY_NUM, + lan966x, QSYS_TAS_CFG_CTRL); + + next = lan_rd(lan966x, QSYS_TAS_GCL_CT_CFG2); + next = QSYS_TAS_GCL_CT_CFG2_NEXT_GCL_GET(next); + } while (base != next); + } + + return num_free; +} + +static void lan966x_taprio_gcl_setup_entry(struct lan966x_port *port, + struct tc_taprio_sched_entry *entry, + u32 next_entry) +{ + struct lan966x *lan966x = port->lan966x; + + /* Setup a single gcl entry */ + lan_wr(QSYS_TAS_GCL_CT_CFG_GATE_STATE_SET(entry->gate_mask) | + QSYS_TAS_GCL_CT_CFG_HSCH_POS_SET(port->chip_port) | + QSYS_TAS_GCL_CT_CFG_OP_TYPE_SET(LAN966X_TAPRIO_GCL_CMD_SET_GATE_STATES), + lan966x, QSYS_TAS_GCL_CT_CFG); + + lan_wr(QSYS_TAS_GCL_CT_CFG2_PORT_PROFILE_SET(port->chip_port) | + QSYS_TAS_GCL_CT_CFG2_NEXT_GCL_SET(next_entry), + lan966x, QSYS_TAS_GCL_CT_CFG2); + + lan_wr(entry->interval, lan966x, QSYS_TAS_GCL_TM_CFG); +} + +static int lan966x_taprio_gcl_setup(struct lan966x_port *port, + struct tc_taprio_qopt_offload *qopt, + int list) +{ + DECLARE_BITMAP(free_list, LAN966X_TAPRIO_NUM_GCL); + struct lan966x *lan966x = port->lan966x; + u32 i, base, next; + + if (lan966x_taprio_gcl_free_get(port, free_list) < qopt->num_entries) + return -ENOSPC; + + /* Select list */ + lan_rmw(QSYS_TAS_CFG_CTRL_LIST_NUM_SET(list), + QSYS_TAS_CFG_CTRL_LIST_NUM, + lan966x, QSYS_TAS_CFG_CTRL); + + /* Setup the address of the first gcl entry */ + base = find_first_bit(free_list, LAN966X_TAPRIO_NUM_GCL); + lan_rmw(QSYS_TAS_LIST_CFG_LIST_BASE_ADDR_SET(base), + QSYS_TAS_LIST_CFG_LIST_BASE_ADDR, + lan966x, QSYS_TAS_LIST_CFG); + + /* Iterate over entries and add them to the gcl list */ + next = base; + for (i = 0; i < qopt->num_entries; ++i) { + lan_rmw(QSYS_TAS_CFG_CTRL_GCL_ENTRY_NUM_SET(next), + QSYS_TAS_CFG_CTRL_GCL_ENTRY_NUM, + lan966x, QSYS_TAS_CFG_CTRL); + + /* If the entry is last, point back to the start of the list */ + if (i == qopt->num_entries - 1) + next = base; + else + next = find_next_bit(free_list, LAN966X_TAPRIO_NUM_GCL, + next + 1); + + lan966x_taprio_gcl_setup_entry(port, &qopt->entries[i], next); + } + + return 0; +} + +/* Calculate new base_time based on cycle_time. The HW recommends to have the + * new base time at least 2 * cycle type + current time + */ +static void lan966x_taprio_new_base_time(struct lan966x *lan966x, + const u32 cycle_time, + const ktime_t org_base_time, + ktime_t *new_base_time) +{ + ktime_t current_time, threshold_time; + struct timespec64 ts; + + /* Get the current time and calculate the threshold_time */ + lan966x_ptp_gettime64(&lan966x->phc[LAN966X_PHC_PORT].info, &ts); + current_time = timespec64_to_ktime(ts); + threshold_time = current_time + (2 * cycle_time); + + /* If the org_base_time is in enough in future just use it */ + if (org_base_time >= threshold_time) { + *new_base_time = org_base_time; + return; + } + + /* If the org_base_time is smaller than current_time, calculate the new + * base time as following. + */ + if (org_base_time <= current_time) { + u64 tmp = current_time - org_base_time; + u32 rem = 0; + + if (tmp > cycle_time) + div_u64_rem(tmp, cycle_time, &rem); + rem = cycle_time - rem; + *new_base_time = threshold_time + rem; + return; + } + + /* The only left place for org_base_time is between current_time and + * threshold_time. In this case the new_base_time is calculated like + * org_base_time + 2 * cycletime + */ + *new_base_time = org_base_time + 2 * cycle_time; +} + +int lan966x_taprio_speed_set(struct lan966x_port *port, int speed) +{ + struct lan966x *lan966x = port->lan966x; + u8 taprio_speed; + + switch (speed) { + case SPEED_10: + taprio_speed = LAN966X_TAPRIO_SPEED_10; + break; + case SPEED_100: + taprio_speed = LAN966X_TAPRIO_SPEED_100; + break; + case SPEED_1000: + taprio_speed = LAN966X_TAPRIO_SPEED_1000; + break; + case SPEED_2500: + taprio_speed = LAN966X_TAPRIO_SPEED_2500; + break; + default: + return -EINVAL; + } + + lan_rmw(QSYS_TAS_PROFILE_CFG_LINK_SPEED_SET(taprio_speed), + QSYS_TAS_PROFILE_CFG_LINK_SPEED, + lan966x, QSYS_TAS_PROFILE_CFG(port->chip_port)); + + return 0; +} + +int lan966x_taprio_add(struct lan966x_port *port, + struct tc_taprio_qopt_offload *qopt) +{ + struct lan966x *lan966x = port->lan966x; + int err, new_list, obs_list; + struct timespec64 ts; + ktime_t base_time; + + err = lan966x_taprio_check(qopt); + if (err) + return err; + + err = lan966x_taprio_find_list(port, qopt, &new_list, &obs_list); + if (err) + return err; + + err = lan966x_taprio_gcl_setup(port, qopt, new_list); + if (err) + return err; + + lan966x_taprio_new_base_time(lan966x, qopt->cycle_time, + qopt->base_time, &base_time); + + ts = ktime_to_timespec64(base_time); + lan_wr(QSYS_TAS_BT_NSEC_NSEC_SET(ts.tv_nsec), + lan966x, QSYS_TAS_BT_NSEC); + + lan_wr(lower_32_bits(ts.tv_sec), + lan966x, QSYS_TAS_BT_SEC_LSB); + + lan_wr(QSYS_TAS_BT_SEC_MSB_SEC_MSB_SET(upper_32_bits(ts.tv_sec)), + lan966x, QSYS_TAS_BT_SEC_MSB); + + lan_wr(qopt->cycle_time, lan966x, QSYS_TAS_CT_CFG); + + lan_rmw(QSYS_TAS_STARTUP_CFG_OBSOLETE_IDX_SET(obs_list), + QSYS_TAS_STARTUP_CFG_OBSOLETE_IDX, + lan966x, QSYS_TAS_STARTUP_CFG); + + /* Start list processing */ + lan_rmw(QSYS_TAS_LST_LIST_STATE_SET(LAN966X_TAPRIO_STATE_ADVANCING), + QSYS_TAS_LST_LIST_STATE, + lan966x, QSYS_TAS_LST); + + return err; +} + +int lan966x_taprio_del(struct lan966x_port *port) +{ + return lan966x_taprio_shutdown(port); +} + +void lan966x_taprio_init(struct lan966x *lan966x) +{ + int num_taprio_lists; + int p; + + lan_wr(QSYS_TAS_STM_CFG_REVISIT_DLY_SET((256 * 1000) / + lan966x_ptp_get_period_ps()), + lan966x, QSYS_TAS_STM_CFG); + + num_taprio_lists = lan966x->num_phys_ports * + LAN966X_TAPRIO_ENTRIES_PER_PORT; + + /* For now we always use guard band on all queues */ + lan_rmw(QSYS_TAS_CFG_CTRL_LIST_NUM_MAX_SET(num_taprio_lists) | + QSYS_TAS_CFG_CTRL_ALWAYS_GB_SCH_Q_SET(1), + QSYS_TAS_CFG_CTRL_LIST_NUM_MAX | + QSYS_TAS_CFG_CTRL_ALWAYS_GB_SCH_Q, + lan966x, QSYS_TAS_CFG_CTRL); + + for (p = 0; p < lan966x->num_phys_ports; p++) + lan_rmw(QSYS_TAS_PROFILE_CFG_PORT_NUM_SET(p), + QSYS_TAS_PROFILE_CFG_PORT_NUM, + lan966x, QSYS_TAS_PROFILE_CFG(p)); +} + +void lan966x_taprio_deinit(struct lan966x *lan966x) +{ + int p; + + for (p = 0; p < lan966x->num_phys_ports; ++p) { + if (!lan966x->ports[p]) + continue; + + lan966x_taprio_del(lan966x->ports[p]); + } +} diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_tbf.c b/drivers/net/ethernet/microchip/lan966x/lan966x_tbf.c new file mode 100644 index 000000000000..4555a35d0d28 --- /dev/null +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_tbf.c @@ -0,0 +1,85 @@ +// SPDX-License-Identifier: GPL-2.0+ + +#include "lan966x_main.h" + +int lan966x_tbf_add(struct lan966x_port *port, + struct tc_tbf_qopt_offload *qopt) +{ + struct lan966x *lan966x = port->lan966x; + bool root = qopt->parent == TC_H_ROOT; + u32 queue = 0; + u32 cir, cbs; + u32 se_idx; + + if (!root) { + queue = TC_H_MIN(qopt->parent) - 1; + if (queue >= NUM_PRIO_QUEUES) + return -EOPNOTSUPP; + } + + if (root) + se_idx = SE_IDX_PORT + port->chip_port; + else + se_idx = SE_IDX_QUEUE + port->chip_port * NUM_PRIO_QUEUES + queue; + + cir = div_u64(qopt->replace_params.rate.rate_bytes_ps, 1000) * 8; + cbs = qopt->replace_params.max_size; + + /* Rate unit is 100 kbps */ + cir = DIV_ROUND_UP(cir, 100); + /* Avoid using zero rate */ + cir = cir ?: 1; + /* Burst unit is 4kB */ + cbs = DIV_ROUND_UP(cbs, 4096); + /* Avoid using zero burst */ + cbs = cbs ?: 1; + + /* Check that actually the result can be written */ + if (cir > GENMASK(15, 0) || + cbs > GENMASK(6, 0)) + return -EINVAL; + + lan_rmw(QSYS_SE_CFG_SE_AVB_ENA_SET(0) | + QSYS_SE_CFG_SE_FRM_MODE_SET(1), + QSYS_SE_CFG_SE_AVB_ENA | + QSYS_SE_CFG_SE_FRM_MODE, + lan966x, QSYS_SE_CFG(se_idx)); + + lan_wr(QSYS_CIR_CFG_CIR_RATE_SET(cir) | + QSYS_CIR_CFG_CIR_BURST_SET(cbs), + lan966x, QSYS_CIR_CFG(se_idx)); + + return 0; +} + +int lan966x_tbf_del(struct lan966x_port *port, + struct tc_tbf_qopt_offload *qopt) +{ + struct lan966x *lan966x = port->lan966x; + bool root = qopt->parent == TC_H_ROOT; + u32 queue = 0; + u32 se_idx; + + if (!root) { + queue = TC_H_MIN(qopt->parent) - 1; + if (queue >= NUM_PRIO_QUEUES) + return -EOPNOTSUPP; + } + + if (root) + se_idx = SE_IDX_PORT + port->chip_port; + else + se_idx = SE_IDX_QUEUE + port->chip_port * NUM_PRIO_QUEUES + queue; + + lan_rmw(QSYS_SE_CFG_SE_AVB_ENA_SET(0) | + QSYS_SE_CFG_SE_FRM_MODE_SET(0), + QSYS_SE_CFG_SE_AVB_ENA | + QSYS_SE_CFG_SE_FRM_MODE, + lan966x, QSYS_SE_CFG(se_idx)); + + lan_wr(QSYS_CIR_CFG_CIR_RATE_SET(0) | + QSYS_CIR_CFG_CIR_BURST_SET(0), + lan966x, QSYS_CIR_CFG(se_idx)); + + return 0; +} diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_tc.c b/drivers/net/ethernet/microchip/lan966x/lan966x_tc.c new file mode 100644 index 000000000000..651d5493ae55 --- /dev/null +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_tc.c @@ -0,0 +1,133 @@ +// SPDX-License-Identifier: GPL-2.0+ + +#include <net/pkt_cls.h> + +#include "lan966x_main.h" + +static LIST_HEAD(lan966x_tc_block_cb_list); + +static int lan966x_tc_setup_qdisc_mqprio(struct lan966x_port *port, + struct tc_mqprio_qopt_offload *mqprio) +{ + u8 num_tc = mqprio->qopt.num_tc; + + mqprio->qopt.hw = TC_MQPRIO_HW_OFFLOAD_TCS; + + return num_tc ? lan966x_mqprio_add(port, num_tc) : + lan966x_mqprio_del(port); +} + +static int lan966x_tc_setup_qdisc_taprio(struct lan966x_port *port, + struct tc_taprio_qopt_offload *taprio) +{ + return taprio->enable ? lan966x_taprio_add(port, taprio) : + lan966x_taprio_del(port); +} + +static int lan966x_tc_setup_qdisc_tbf(struct lan966x_port *port, + struct tc_tbf_qopt_offload *qopt) +{ + switch (qopt->command) { + case TC_TBF_REPLACE: + return lan966x_tbf_add(port, qopt); + case TC_TBF_DESTROY: + return lan966x_tbf_del(port, qopt); + default: + return -EOPNOTSUPP; + } + + return -EOPNOTSUPP; +} + +static int lan966x_tc_setup_qdisc_cbs(struct lan966x_port *port, + struct tc_cbs_qopt_offload *qopt) +{ + return qopt->enable ? lan966x_cbs_add(port, qopt) : + lan966x_cbs_del(port, qopt); +} + +static int lan966x_tc_setup_qdisc_ets(struct lan966x_port *port, + struct tc_ets_qopt_offload *qopt) +{ + switch (qopt->command) { + case TC_ETS_REPLACE: + return lan966x_ets_add(port, qopt); + case TC_ETS_DESTROY: + return lan966x_ets_del(port, qopt); + default: + return -EOPNOTSUPP; + }; + + return -EOPNOTSUPP; +} + +static int lan966x_tc_block_cb(enum tc_setup_type type, void *type_data, + void *cb_priv, bool ingress) +{ + struct lan966x_port *port = cb_priv; + + switch (type) { + case TC_SETUP_CLSMATCHALL: + return lan966x_tc_matchall(port, type_data, ingress); + default: + return -EOPNOTSUPP; + } +} + +static int lan966x_tc_block_cb_ingress(enum tc_setup_type type, + void *type_data, void *cb_priv) +{ + return lan966x_tc_block_cb(type, type_data, cb_priv, true); +} + +static int lan966x_tc_block_cb_egress(enum tc_setup_type type, + void *type_data, void *cb_priv) +{ + return lan966x_tc_block_cb(type, type_data, cb_priv, false); +} + +static int lan966x_tc_setup_block(struct lan966x_port *port, + struct flow_block_offload *f) +{ + flow_setup_cb_t *cb; + bool ingress; + + if (f->binder_type == FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS) { + cb = lan966x_tc_block_cb_ingress; + port->tc.ingress_shared_block = f->block_shared; + ingress = true; + } else if (f->binder_type == FLOW_BLOCK_BINDER_TYPE_CLSACT_EGRESS) { + cb = lan966x_tc_block_cb_egress; + ingress = false; + } else { + return -EOPNOTSUPP; + } + + return flow_block_cb_setup_simple(f, &lan966x_tc_block_cb_list, + cb, port, port, ingress); +} + +int lan966x_tc_setup(struct net_device *dev, enum tc_setup_type type, + void *type_data) +{ + struct lan966x_port *port = netdev_priv(dev); + + switch (type) { + case TC_SETUP_QDISC_MQPRIO: + return lan966x_tc_setup_qdisc_mqprio(port, type_data); + case TC_SETUP_QDISC_TAPRIO: + return lan966x_tc_setup_qdisc_taprio(port, type_data); + case TC_SETUP_QDISC_TBF: + return lan966x_tc_setup_qdisc_tbf(port, type_data); + case TC_SETUP_QDISC_CBS: + return lan966x_tc_setup_qdisc_cbs(port, type_data); + case TC_SETUP_QDISC_ETS: + return lan966x_tc_setup_qdisc_ets(port, type_data); + case TC_SETUP_BLOCK: + return lan966x_tc_setup_block(port, type_data); + default: + return -EOPNOTSUPP; + } + + return 0; +} diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_tc_matchall.c b/drivers/net/ethernet/microchip/lan966x/lan966x_tc_matchall.c new file mode 100644 index 000000000000..7368433b9277 --- /dev/null +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_tc_matchall.c @@ -0,0 +1,95 @@ +// SPDX-License-Identifier: GPL-2.0+ + +#include "lan966x_main.h" + +static int lan966x_tc_matchall_add(struct lan966x_port *port, + struct tc_cls_matchall_offload *f, + bool ingress) +{ + struct flow_action_entry *act; + + if (!flow_offload_has_one_action(&f->rule->action)) { + NL_SET_ERR_MSG_MOD(f->common.extack, + "Only once action per filter is supported"); + return -EOPNOTSUPP; + } + + act = &f->rule->action.entries[0]; + switch (act->id) { + case FLOW_ACTION_POLICE: + return lan966x_police_port_add(port, &f->rule->action, act, + f->cookie, ingress, + f->common.extack); + case FLOW_ACTION_MIRRED: + return lan966x_mirror_port_add(port, act, f->cookie, + ingress, f->common.extack); + default: + NL_SET_ERR_MSG_MOD(f->common.extack, + "Unsupported action"); + return -EOPNOTSUPP; + } + + return 0; +} + +static int lan966x_tc_matchall_del(struct lan966x_port *port, + struct tc_cls_matchall_offload *f, + bool ingress) +{ + if (f->cookie == port->tc.police_id) { + return lan966x_police_port_del(port, f->cookie, + f->common.extack); + } else if (f->cookie == port->tc.ingress_mirror_id || + f->cookie == port->tc.egress_mirror_id) { + return lan966x_mirror_port_del(port, ingress, + f->common.extack); + } else { + NL_SET_ERR_MSG_MOD(f->common.extack, + "Unsupported action"); + return -EOPNOTSUPP; + } + + return 0; +} + +static int lan966x_tc_matchall_stats(struct lan966x_port *port, + struct tc_cls_matchall_offload *f, + bool ingress) +{ + if (f->cookie == port->tc.police_id) { + lan966x_police_port_stats(port, &f->stats); + } else if (f->cookie == port->tc.ingress_mirror_id || + f->cookie == port->tc.egress_mirror_id) { + lan966x_mirror_port_stats(port, &f->stats, ingress); + } else { + NL_SET_ERR_MSG_MOD(f->common.extack, + "Unsupported action"); + return -EOPNOTSUPP; + } + + return 0; +} + +int lan966x_tc_matchall(struct lan966x_port *port, + struct tc_cls_matchall_offload *f, + bool ingress) +{ + if (!tc_cls_can_offload_and_chain0(port->dev, &f->common)) { + NL_SET_ERR_MSG_MOD(f->common.extack, + "Only chain zero is supported"); + return -EOPNOTSUPP; + } + + switch (f->command) { + case TC_CLSMATCHALL_REPLACE: + return lan966x_tc_matchall_add(port, f, ingress); + case TC_CLSMATCHALL_DESTROY: + return lan966x_tc_matchall_del(port, f, ingress); + case TC_CLSMATCHALL_STATS: + return lan966x_tc_matchall_stats(port, f, ingress); + default: + return -EOPNOTSUPP; + } + + return 0; +} diff --git a/drivers/net/ethernet/microchip/sparx5/Makefile b/drivers/net/ethernet/microchip/sparx5/Makefile index 4402c3ed1dc5..d1c6ad966747 100644 --- a/drivers/net/ethernet/microchip/sparx5/Makefile +++ b/drivers/net/ethernet/microchip/sparx5/Makefile @@ -8,4 +8,4 @@ obj-$(CONFIG_SPARX5_SWITCH) += sparx5-switch.o sparx5-switch-objs := sparx5_main.o sparx5_packet.o \ sparx5_netdev.o sparx5_phylink.o sparx5_port.o sparx5_mactable.o sparx5_vlan.o \ sparx5_switchdev.o sparx5_calendar.o sparx5_ethtool.o sparx5_fdma.o \ - sparx5_ptp.o sparx5_pgid.o + sparx5_ptp.o sparx5_pgid.o sparx5_tc.o sparx5_qos.o diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_mactable.c b/drivers/net/ethernet/microchip/sparx5/sparx5_mactable.c index a5837dbe0c7e..4af285918ea2 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_mactable.c +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_mactable.c @@ -186,8 +186,8 @@ bool sparx5_mact_getnext(struct sparx5 *sparx5, return ret == 0; } -bool sparx5_mact_find(struct sparx5 *sparx5, - const unsigned char mac[ETH_ALEN], u16 vid, u32 *pcfg2) +int sparx5_mact_find(struct sparx5 *sparx5, + const unsigned char mac[ETH_ALEN], u16 vid, u32 *pcfg2) { int ret; u32 cfg2; diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_main.c b/drivers/net/ethernet/microchip/sparx5/sparx5_main.c index 01be7bd84181..62a325e96345 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_main.c +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_main.c @@ -27,6 +27,7 @@ #include "sparx5_main_regs.h" #include "sparx5_main.h" #include "sparx5_port.h" +#include "sparx5_qos.h" #define QLIM_WM(fraction) \ ((SPX5_BUFFER_MEMORY / SPX5_BUFFER_CELL_SZ - 100) * (fraction) / 100) @@ -277,6 +278,7 @@ static int sparx5_create_port(struct sparx5 *sparx5, spx5_port->custom_etype = 0x8880; /* Vitesse */ spx5_port->phylink_pcs.poll = true; spx5_port->phylink_pcs.ops = &sparx5_phylink_pcs_ops; + spx5_port->is_mrouter = false; sparx5->ports[config->portno] = spx5_port; err = sparx5_port_init(sparx5, spx5_port, &config->conf); @@ -661,6 +663,9 @@ static int sparx5_start(struct sparx5 *sparx5) queue_delayed_work(sparx5->mact_queue, &sparx5->mact_work, SPX5_MACT_PULL_DELAY); + mutex_init(&sparx5->mdb_lock); + INIT_LIST_HEAD(&sparx5->mdb_entries); + err = sparx5_register_netdevs(sparx5); if (err) return err; @@ -864,6 +869,12 @@ static int mchp_sparx5_probe(struct platform_device *pdev) goto cleanup_ports; } + err = sparx5_qos_init(sparx5); + if (err) { + dev_err(sparx5->dev, "Failed to initialize QoS\n"); + goto cleanup_ports; + } + err = sparx5_ptp_init(sparx5); if (err) { dev_err(sparx5->dev, "PTP failed\n"); diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_main.h b/drivers/net/ethernet/microchip/sparx5/sparx5_main.h index b197129044b5..7a83222caa73 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_main.h +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_main.h @@ -190,6 +190,7 @@ struct sparx5_port { u8 ptp_cmd; u16 ts_id; struct sk_buff_head tx_skbs; + bool is_mrouter; }; enum sparx5_core_clockfreq { @@ -215,6 +216,15 @@ struct sparx5_skb_cb { unsigned long jiffies; }; +struct sparx5_mdb_entry { + struct list_head list; + DECLARE_BITMAP(port_mask, SPX5_PORTS); + unsigned char addr[ETH_ALEN]; + bool cpu_copy; + u16 vid; + u16 pgid_idx; +}; + #define SPARX5_PTP_TIMEOUT msecs_to_jiffies(10) #define SPARX5_SKB_CB(skb) \ ((struct sparx5_skb_cb *)((skb)->cb)) @@ -256,6 +266,10 @@ struct sparx5 { struct list_head mact_entries; /* mac table list (mact_entries) mutex */ struct mutex mact_lock; + /* SW MDB table */ + struct list_head mdb_entries; + /* mdb list mutex */ + struct mutex mdb_lock; struct delayed_work mact_work; struct workqueue_struct *mact_queue; /* Board specifics */ @@ -291,7 +305,7 @@ struct frame_info { void sparx5_xtr_flush(struct sparx5 *sparx5, u8 grp); void sparx5_ifh_parse(u32 *ifh, struct frame_info *info); irqreturn_t sparx5_xtr_handler(int irq, void *_priv); -int sparx5_port_xmit_impl(struct sk_buff *skb, struct net_device *dev); +netdev_tx_t sparx5_port_xmit_impl(struct sk_buff *skb, struct net_device *dev); int sparx5_manual_injection_mode(struct sparx5 *sparx5); void sparx5_port_inj_timer_setup(struct sparx5_port *port); @@ -307,8 +321,8 @@ int sparx5_mact_learn(struct sparx5 *sparx5, int port, const unsigned char mac[ETH_ALEN], u16 vid); bool sparx5_mact_getnext(struct sparx5 *sparx5, unsigned char mac[ETH_ALEN], u16 *vid, u32 *pcfg2); -bool sparx5_mact_find(struct sparx5 *sparx5, - const unsigned char mac[ETH_ALEN], u16 vid, u32 *pcfg2); +int sparx5_mact_find(struct sparx5 *sparx5, + const unsigned char mac[ETH_ALEN], u16 vid, u32 *pcfg2); int sparx5_mact_forget(struct sparx5 *sparx5, const unsigned char mac[ETH_ALEN], u16 vid); int sparx5_add_mact_entry(struct sparx5 *sparx5, @@ -325,6 +339,7 @@ void sparx5_mact_init(struct sparx5 *sparx5); /* sparx5_vlan.c */ void sparx5_pgid_update_mask(struct sparx5_port *port, int pgid, bool enable); +void sparx5_pgid_clear(struct sparx5 *spx5, int pgid); void sparx5_pgid_read_mask(struct sparx5 *sparx5, int pgid, u32 portmask[3]); void sparx5_update_fwd(struct sparx5 *sparx5); void sparx5_vlan_init(struct sparx5 *sparx5); diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_main_regs.h b/drivers/net/ethernet/microchip/sparx5/sparx5_main_regs.h index c94de436b281..fa2eb70f487a 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_main_regs.h +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_main_regs.h @@ -2993,6 +2993,147 @@ enum sparx5_target { #define GCB_SIO_CLOCK_SYS_CLK_PERIOD_GET(x)\ FIELD_GET(GCB_SIO_CLOCK_SYS_CLK_PERIOD, x) +/* HSCH:HSCH_CFG:CIR_CFG */ +#define HSCH_CIR_CFG(g) __REG(TARGET_HSCH, 0, 1, 0, g, 5040, 32, 0, 0, 1, 4) + +#define HSCH_CIR_CFG_CIR_RATE GENMASK(22, 6) +#define HSCH_CIR_CFG_CIR_RATE_SET(x)\ + FIELD_PREP(HSCH_CIR_CFG_CIR_RATE, x) +#define HSCH_CIR_CFG_CIR_RATE_GET(x)\ + FIELD_GET(HSCH_CIR_CFG_CIR_RATE, x) + +#define HSCH_CIR_CFG_CIR_BURST GENMASK(5, 0) +#define HSCH_CIR_CFG_CIR_BURST_SET(x)\ + FIELD_PREP(HSCH_CIR_CFG_CIR_BURST, x) +#define HSCH_CIR_CFG_CIR_BURST_GET(x)\ + FIELD_GET(HSCH_CIR_CFG_CIR_BURST, x) + +/* HSCH:HSCH_CFG:EIR_CFG */ +#define HSCH_EIR_CFG(g) __REG(TARGET_HSCH, 0, 1, 0, g, 5040, 32, 4, 0, 1, 4) + +#define HSCH_EIR_CFG_EIR_RATE GENMASK(22, 6) +#define HSCH_EIR_CFG_EIR_RATE_SET(x)\ + FIELD_PREP(HSCH_EIR_CFG_EIR_RATE, x) +#define HSCH_EIR_CFG_EIR_RATE_GET(x)\ + FIELD_GET(HSCH_EIR_CFG_EIR_RATE, x) + +#define HSCH_EIR_CFG_EIR_BURST GENMASK(5, 0) +#define HSCH_EIR_CFG_EIR_BURST_SET(x)\ + FIELD_PREP(HSCH_EIR_CFG_EIR_BURST, x) +#define HSCH_EIR_CFG_EIR_BURST_GET(x)\ + FIELD_GET(HSCH_EIR_CFG_EIR_BURST, x) + +/* HSCH:HSCH_CFG:SE_CFG */ +#define HSCH_SE_CFG(g) __REG(TARGET_HSCH, 0, 1, 0, g, 5040, 32, 8, 0, 1, 4) + +#define HSCH_SE_CFG_SE_DWRR_CNT GENMASK(12, 6) +#define HSCH_SE_CFG_SE_DWRR_CNT_SET(x)\ + FIELD_PREP(HSCH_SE_CFG_SE_DWRR_CNT, x) +#define HSCH_SE_CFG_SE_DWRR_CNT_GET(x)\ + FIELD_GET(HSCH_SE_CFG_SE_DWRR_CNT, x) + +#define HSCH_SE_CFG_SE_AVB_ENA BIT(5) +#define HSCH_SE_CFG_SE_AVB_ENA_SET(x)\ + FIELD_PREP(HSCH_SE_CFG_SE_AVB_ENA, x) +#define HSCH_SE_CFG_SE_AVB_ENA_GET(x)\ + FIELD_GET(HSCH_SE_CFG_SE_AVB_ENA, x) + +#define HSCH_SE_CFG_SE_FRM_MODE GENMASK(4, 3) +#define HSCH_SE_CFG_SE_FRM_MODE_SET(x)\ + FIELD_PREP(HSCH_SE_CFG_SE_FRM_MODE, x) +#define HSCH_SE_CFG_SE_FRM_MODE_GET(x)\ + FIELD_GET(HSCH_SE_CFG_SE_FRM_MODE, x) + +#define HSCH_SE_CFG_SE_DWRR_FRM_MODE GENMASK(2, 1) +#define HSCH_SE_CFG_SE_DWRR_FRM_MODE_SET(x)\ + FIELD_PREP(HSCH_SE_CFG_SE_DWRR_FRM_MODE, x) +#define HSCH_SE_CFG_SE_DWRR_FRM_MODE_GET(x)\ + FIELD_GET(HSCH_SE_CFG_SE_DWRR_FRM_MODE, x) + +#define HSCH_SE_CFG_SE_STOP BIT(0) +#define HSCH_SE_CFG_SE_STOP_SET(x)\ + FIELD_PREP(HSCH_SE_CFG_SE_STOP, x) +#define HSCH_SE_CFG_SE_STOP_GET(x)\ + FIELD_GET(HSCH_SE_CFG_SE_STOP, x) + +/* HSCH:HSCH_CFG:SE_CONNECT */ +#define HSCH_SE_CONNECT(g) __REG(TARGET_HSCH, 0, 1, 0, g, 5040, 32, 12, 0, 1, 4) + +#define HSCH_SE_CONNECT_SE_LEAK_LINK GENMASK(15, 0) +#define HSCH_SE_CONNECT_SE_LEAK_LINK_SET(x)\ + FIELD_PREP(HSCH_SE_CONNECT_SE_LEAK_LINK, x) +#define HSCH_SE_CONNECT_SE_LEAK_LINK_GET(x)\ + FIELD_GET(HSCH_SE_CONNECT_SE_LEAK_LINK, x) + +/* HSCH:HSCH_CFG:SE_DLB_SENSE */ +#define HSCH_SE_DLB_SENSE(g) __REG(TARGET_HSCH, 0, 1, 0, g, 5040, 32, 16, 0, 1, 4) + +#define HSCH_SE_DLB_SENSE_SE_DLB_PRIO GENMASK(12, 10) +#define HSCH_SE_DLB_SENSE_SE_DLB_PRIO_SET(x)\ + FIELD_PREP(HSCH_SE_DLB_SENSE_SE_DLB_PRIO, x) +#define HSCH_SE_DLB_SENSE_SE_DLB_PRIO_GET(x)\ + FIELD_GET(HSCH_SE_DLB_SENSE_SE_DLB_PRIO, x) + +#define HSCH_SE_DLB_SENSE_SE_DLB_DPORT GENMASK(9, 3) +#define HSCH_SE_DLB_SENSE_SE_DLB_DPORT_SET(x)\ + FIELD_PREP(HSCH_SE_DLB_SENSE_SE_DLB_DPORT, x) +#define HSCH_SE_DLB_SENSE_SE_DLB_DPORT_GET(x)\ + FIELD_GET(HSCH_SE_DLB_SENSE_SE_DLB_DPORT, x) + +#define HSCH_SE_DLB_SENSE_SE_DLB_SE_ENA BIT(2) +#define HSCH_SE_DLB_SENSE_SE_DLB_SE_ENA_SET(x)\ + FIELD_PREP(HSCH_SE_DLB_SENSE_SE_DLB_SE_ENA, x) +#define HSCH_SE_DLB_SENSE_SE_DLB_SE_ENA_GET(x)\ + FIELD_GET(HSCH_SE_DLB_SENSE_SE_DLB_SE_ENA, x) + +#define HSCH_SE_DLB_SENSE_SE_DLB_PRIO_ENA BIT(1) +#define HSCH_SE_DLB_SENSE_SE_DLB_PRIO_ENA_SET(x)\ + FIELD_PREP(HSCH_SE_DLB_SENSE_SE_DLB_PRIO_ENA, x) +#define HSCH_SE_DLB_SENSE_SE_DLB_PRIO_ENA_GET(x)\ + FIELD_GET(HSCH_SE_DLB_SENSE_SE_DLB_PRIO_ENA, x) + +#define HSCH_SE_DLB_SENSE_SE_DLB_DPORT_ENA BIT(0) +#define HSCH_SE_DLB_SENSE_SE_DLB_DPORT_ENA_SET(x)\ + FIELD_PREP(HSCH_SE_DLB_SENSE_SE_DLB_DPORT_ENA, x) +#define HSCH_SE_DLB_SENSE_SE_DLB_DPORT_ENA_GET(x)\ + FIELD_GET(HSCH_SE_DLB_SENSE_SE_DLB_DPORT_ENA, x) + +/* HSCH:HSCH_DWRR:DWRR_ENTRY */ +#define HSCH_DWRR_ENTRY(g) __REG(TARGET_HSCH, 0, 1, 162816, g, 72, 4, 0, 0, 1, 4) + +#define HSCH_DWRR_ENTRY_DWRR_COST GENMASK(24, 20) +#define HSCH_DWRR_ENTRY_DWRR_COST_SET(x)\ + FIELD_PREP(HSCH_DWRR_ENTRY_DWRR_COST, x) +#define HSCH_DWRR_ENTRY_DWRR_COST_GET(x)\ + FIELD_GET(HSCH_DWRR_ENTRY_DWRR_COST, x) + +#define HSCH_DWRR_ENTRY_DWRR_BALANCE GENMASK(19, 0) +#define HSCH_DWRR_ENTRY_DWRR_BALANCE_SET(x)\ + FIELD_PREP(HSCH_DWRR_ENTRY_DWRR_BALANCE, x) +#define HSCH_DWRR_ENTRY_DWRR_BALANCE_GET(x)\ + FIELD_GET(HSCH_DWRR_ENTRY_DWRR_BALANCE, x) + +/* HSCH:HSCH_MISC:HSCH_CFG_CFG */ +#define HSCH_HSCH_CFG_CFG __REG(TARGET_HSCH, 0, 1, 163104, 0, 1, 648, 284, 0, 1, 4) + +#define HSCH_HSCH_CFG_CFG_CFG_SE_IDX GENMASK(26, 14) +#define HSCH_HSCH_CFG_CFG_CFG_SE_IDX_SET(x)\ + FIELD_PREP(HSCH_HSCH_CFG_CFG_CFG_SE_IDX, x) +#define HSCH_HSCH_CFG_CFG_CFG_SE_IDX_GET(x)\ + FIELD_GET(HSCH_HSCH_CFG_CFG_CFG_SE_IDX, x) + +#define HSCH_HSCH_CFG_CFG_HSCH_LAYER GENMASK(13, 12) +#define HSCH_HSCH_CFG_CFG_HSCH_LAYER_SET(x)\ + FIELD_PREP(HSCH_HSCH_CFG_CFG_HSCH_LAYER, x) +#define HSCH_HSCH_CFG_CFG_HSCH_LAYER_GET(x)\ + FIELD_GET(HSCH_HSCH_CFG_CFG_HSCH_LAYER, x) + +#define HSCH_HSCH_CFG_CFG_CSR_GRANT GENMASK(11, 0) +#define HSCH_HSCH_CFG_CFG_CSR_GRANT_SET(x)\ + FIELD_PREP(HSCH_HSCH_CFG_CFG_CSR_GRANT, x) +#define HSCH_HSCH_CFG_CFG_CSR_GRANT_GET(x)\ + FIELD_GET(HSCH_HSCH_CFG_CFG_CSR_GRANT, x) + /* HSCH:HSCH_MISC:SYS_CLK_PER */ #define HSCH_SYS_CLK_PER __REG(TARGET_HSCH, 0, 1, 163104, 0, 1, 648, 640, 0, 1, 4) @@ -3002,6 +3143,30 @@ enum sparx5_target { #define HSCH_SYS_CLK_PER_SYS_CLK_PER_100PS_GET(x)\ FIELD_GET(HSCH_SYS_CLK_PER_SYS_CLK_PER_100PS, x) +/* HSCH:HSCH_LEAK_LISTS:HSCH_TIMER_CFG */ +#define HSCH_HSCH_TIMER_CFG(g, r) __REG(TARGET_HSCH, 0, 1, 161664, g, 4, 32, 0, r, 4, 4) + +#define HSCH_HSCH_TIMER_CFG_LEAK_TIME GENMASK(17, 0) +#define HSCH_HSCH_TIMER_CFG_LEAK_TIME_SET(x)\ + FIELD_PREP(HSCH_HSCH_TIMER_CFG_LEAK_TIME, x) +#define HSCH_HSCH_TIMER_CFG_LEAK_TIME_GET(x)\ + FIELD_GET(HSCH_HSCH_TIMER_CFG_LEAK_TIME, x) + +/* HSCH:HSCH_LEAK_LISTS:HSCH_LEAK_CFG */ +#define HSCH_HSCH_LEAK_CFG(g, r) __REG(TARGET_HSCH, 0, 1, 161664, g, 4, 32, 16, r, 4, 4) + +#define HSCH_HSCH_LEAK_CFG_LEAK_FIRST GENMASK(16, 1) +#define HSCH_HSCH_LEAK_CFG_LEAK_FIRST_SET(x)\ + FIELD_PREP(HSCH_HSCH_LEAK_CFG_LEAK_FIRST, x) +#define HSCH_HSCH_LEAK_CFG_LEAK_FIRST_GET(x)\ + FIELD_GET(HSCH_HSCH_LEAK_CFG_LEAK_FIRST, x) + +#define HSCH_HSCH_LEAK_CFG_LEAK_ERR BIT(0) +#define HSCH_HSCH_LEAK_CFG_LEAK_ERR_SET(x)\ + FIELD_PREP(HSCH_HSCH_LEAK_CFG_LEAK_ERR, x) +#define HSCH_HSCH_LEAK_CFG_LEAK_ERR_GET(x)\ + FIELD_GET(HSCH_HSCH_LEAK_CFG_LEAK_ERR, x) + /* HSCH:SYSTEM:FLUSH_CTRL */ #define HSCH_FLUSH_CTRL __REG(TARGET_HSCH, 0, 1, 184000, 0, 1, 312, 4, 0, 1, 4) diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_netdev.c b/drivers/net/ethernet/microchip/sparx5/sparx5_netdev.c index af4d3e1f1a6d..19516ccad533 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_netdev.c +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_netdev.c @@ -7,6 +7,7 @@ #include "sparx5_main_regs.h" #include "sparx5_main.h" #include "sparx5_port.h" +#include "sparx5_tc.h" /* The IFH bit position of the first VSTAX bit. This is because the * VSTAX bit positions in Data sheet is starting from zero. @@ -228,6 +229,7 @@ static const struct net_device_ops sparx5_port_netdev_ops = { .ndo_get_stats64 = sparx5_get_stats64, .ndo_get_port_parent_id = sparx5_get_port_parent_id, .ndo_eth_ioctl = sparx5_port_ioctl, + .ndo_setup_tc = sparx5_port_setup_tc, }; bool sparx5_netdevice_check(const struct net_device *dev) @@ -240,10 +242,14 @@ struct net_device *sparx5_create_netdev(struct sparx5 *sparx5, u32 portno) struct sparx5_port *spx5_port; struct net_device *ndev; - ndev = devm_alloc_etherdev(sparx5->dev, sizeof(struct sparx5_port)); + ndev = devm_alloc_etherdev_mqs(sparx5->dev, sizeof(struct sparx5_port), + SPX5_PRIOS, 1); if (!ndev) return ERR_PTR(-ENOMEM); + ndev->hw_features |= NETIF_F_HW_TC; + ndev->features |= NETIF_F_HW_TC; + SET_NETDEV_DEV(ndev, sparx5->dev); spx5_port = netdev_priv(ndev); spx5_port->ndev = ndev; diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_packet.c b/drivers/net/ethernet/microchip/sparx5/sparx5_packet.c index 21844beba72d..83c16ca5b30f 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_packet.c +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_packet.c @@ -222,13 +222,13 @@ static int sparx5_inject(struct sparx5 *sparx5, return NETDEV_TX_OK; } -int sparx5_port_xmit_impl(struct sk_buff *skb, struct net_device *dev) +netdev_tx_t sparx5_port_xmit_impl(struct sk_buff *skb, struct net_device *dev) { struct net_device_stats *stats = &dev->stats; struct sparx5_port *port = netdev_priv(dev); struct sparx5 *sparx5 = port->sparx5; u32 ifh[IFH_LEN]; - int ret; + netdev_tx_t ret; memset(ifh, 0, IFH_LEN * 4); sparx5_set_port_ifh(ifh, port->portno); diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_qos.c b/drivers/net/ethernet/microchip/sparx5/sparx5_qos.c new file mode 100644 index 000000000000..1e79d0ef0cb8 --- /dev/null +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_qos.c @@ -0,0 +1,513 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* Microchip Sparx5 Switch driver + * + * Copyright (c) 2022 Microchip Technology Inc. and its subsidiaries. + */ + +#include <net/pkt_cls.h> + +#include "sparx5_main.h" +#include "sparx5_qos.h" + +/* Max rates for leak groups */ +static const u32 spx5_hsch_max_group_rate[SPX5_HSCH_LEAK_GRP_CNT] = { + 1048568, /* 1.049 Gbps */ + 2621420, /* 2.621 Gbps */ + 10485680, /* 10.486 Gbps */ + 26214200 /* 26.214 Gbps */ +}; + +static struct sparx5_layer layers[SPX5_HSCH_LAYER_CNT]; + +static u32 sparx5_lg_get_leak_time(struct sparx5 *sparx5, u32 layer, u32 group) +{ + u32 value; + + value = spx5_rd(sparx5, HSCH_HSCH_TIMER_CFG(layer, group)); + return HSCH_HSCH_TIMER_CFG_LEAK_TIME_GET(value); +} + +static void sparx5_lg_set_leak_time(struct sparx5 *sparx5, u32 layer, u32 group, + u32 leak_time) +{ + spx5_wr(HSCH_HSCH_TIMER_CFG_LEAK_TIME_SET(leak_time), sparx5, + HSCH_HSCH_TIMER_CFG(layer, group)); +} + +static u32 sparx5_lg_get_first(struct sparx5 *sparx5, u32 layer, u32 group) +{ + u32 value; + + value = spx5_rd(sparx5, HSCH_HSCH_LEAK_CFG(layer, group)); + return HSCH_HSCH_LEAK_CFG_LEAK_FIRST_GET(value); +} + +static u32 sparx5_lg_get_next(struct sparx5 *sparx5, u32 layer, u32 group, + u32 idx) + +{ + u32 value; + + value = spx5_rd(sparx5, HSCH_SE_CONNECT(idx)); + return HSCH_SE_CONNECT_SE_LEAK_LINK_GET(value); +} + +static u32 sparx5_lg_get_last(struct sparx5 *sparx5, u32 layer, u32 group) +{ + u32 itr, next; + + itr = sparx5_lg_get_first(sparx5, layer, group); + + for (;;) { + next = sparx5_lg_get_next(sparx5, layer, group, itr); + if (itr == next) + return itr; + + itr = next; + } +} + +static bool sparx5_lg_is_last(struct sparx5 *sparx5, u32 layer, u32 group, + u32 idx) +{ + return idx == sparx5_lg_get_next(sparx5, layer, group, idx); +} + +static bool sparx5_lg_is_first(struct sparx5 *sparx5, u32 layer, u32 group, + u32 idx) +{ + return idx == sparx5_lg_get_first(sparx5, layer, group); +} + +static bool sparx5_lg_is_empty(struct sparx5 *sparx5, u32 layer, u32 group) +{ + return sparx5_lg_get_leak_time(sparx5, layer, group) == 0; +} + +static bool sparx5_lg_is_singular(struct sparx5 *sparx5, u32 layer, u32 group) +{ + if (sparx5_lg_is_empty(sparx5, layer, group)) + return false; + + return sparx5_lg_get_first(sparx5, layer, group) == + sparx5_lg_get_last(sparx5, layer, group); +} + +static void sparx5_lg_enable(struct sparx5 *sparx5, u32 layer, u32 group, + u32 leak_time) +{ + sparx5_lg_set_leak_time(sparx5, layer, group, leak_time); +} + +static void sparx5_lg_disable(struct sparx5 *sparx5, u32 layer, u32 group) +{ + sparx5_lg_set_leak_time(sparx5, layer, group, 0); +} + +static int sparx5_lg_get_group_by_index(struct sparx5 *sparx5, u32 layer, + u32 idx, u32 *group) +{ + u32 itr, next; + int i; + + for (i = 0; i < SPX5_HSCH_LEAK_GRP_CNT; i++) { + if (sparx5_lg_is_empty(sparx5, layer, i)) + continue; + + itr = sparx5_lg_get_first(sparx5, layer, i); + + for (;;) { + next = sparx5_lg_get_next(sparx5, layer, i, itr); + + if (itr == idx) { + *group = i; + return 0; /* Found it */ + } + if (itr == next) + break; /* Was not found */ + + itr = next; + } + } + + return -1; +} + +static int sparx5_lg_get_group_by_rate(u32 layer, u32 rate, u32 *group) +{ + struct sparx5_layer *l = &layers[layer]; + struct sparx5_lg *lg; + u32 i; + + for (i = 0; i < SPX5_HSCH_LEAK_GRP_CNT; i++) { + lg = &l->leak_groups[i]; + if (rate <= lg->max_rate) { + *group = i; + return 0; + } + } + + return -1; +} + +static int sparx5_lg_get_adjacent(struct sparx5 *sparx5, u32 layer, u32 group, + u32 idx, u32 *prev, u32 *next, u32 *first) +{ + u32 itr; + + *first = sparx5_lg_get_first(sparx5, layer, group); + *prev = *first; + *next = *first; + itr = *first; + + for (;;) { + *next = sparx5_lg_get_next(sparx5, layer, group, itr); + + if (itr == idx) + return 0; /* Found it */ + + if (itr == *next) + return -1; /* Was not found */ + + *prev = itr; + itr = *next; + } + + return -1; +} + +static int sparx5_lg_conf_set(struct sparx5 *sparx5, u32 layer, u32 group, + u32 se_first, u32 idx, u32 idx_next, bool empty) +{ + u32 leak_time = layers[layer].leak_groups[group].leak_time; + + /* Stop leaking */ + sparx5_lg_disable(sparx5, layer, group); + + if (empty) + return 0; + + /* Select layer */ + spx5_rmw(HSCH_HSCH_CFG_CFG_HSCH_LAYER_SET(layer), + HSCH_HSCH_CFG_CFG_HSCH_LAYER, sparx5, HSCH_HSCH_CFG_CFG); + + /* Link elements */ + spx5_wr(HSCH_SE_CONNECT_SE_LEAK_LINK_SET(idx_next), sparx5, + HSCH_SE_CONNECT(idx)); + + /* Set the first element. */ + spx5_rmw(HSCH_HSCH_LEAK_CFG_LEAK_FIRST_SET(se_first), + HSCH_HSCH_LEAK_CFG_LEAK_FIRST, sparx5, + HSCH_HSCH_LEAK_CFG(layer, group)); + + /* Start leaking */ + sparx5_lg_enable(sparx5, layer, group, leak_time); + + return 0; +} + +static int sparx5_lg_del(struct sparx5 *sparx5, u32 layer, u32 group, u32 idx) +{ + u32 first, next, prev; + bool empty = false; + + /* idx *must* be present in the leak group */ + WARN_ON(sparx5_lg_get_adjacent(sparx5, layer, group, idx, &prev, &next, + &first) < 0); + + if (sparx5_lg_is_singular(sparx5, layer, group)) { + empty = true; + } else if (sparx5_lg_is_last(sparx5, layer, group, idx)) { + /* idx is removed, prev is now last */ + idx = prev; + next = prev; + } else if (sparx5_lg_is_first(sparx5, layer, group, idx)) { + /* idx is removed and points to itself, first is next */ + first = next; + next = idx; + } else { + /* Next is not touched */ + idx = prev; + } + + return sparx5_lg_conf_set(sparx5, layer, group, first, idx, next, + empty); +} + +static int sparx5_lg_add(struct sparx5 *sparx5, u32 layer, u32 new_group, + u32 idx) +{ + u32 first, next, old_group; + + pr_debug("ADD: layer: %d, new_group: %d, idx: %d", layer, new_group, + idx); + + /* Is this SE already shaping ? */ + if (sparx5_lg_get_group_by_index(sparx5, layer, idx, &old_group) >= 0) { + if (old_group != new_group) { + /* Delete from old group */ + sparx5_lg_del(sparx5, layer, old_group, idx); + } else { + /* Nothing to do here */ + return 0; + } + } + + /* We always add to head of the list */ + first = idx; + + if (sparx5_lg_is_empty(sparx5, layer, new_group)) + next = idx; + else + next = sparx5_lg_get_first(sparx5, layer, new_group); + + return sparx5_lg_conf_set(sparx5, layer, new_group, first, idx, next, + false); +} + +static int sparx5_shaper_conf_set(struct sparx5_port *port, + const struct sparx5_shaper *sh, u32 layer, + u32 idx, u32 group) +{ + int (*sparx5_lg_action)(struct sparx5 *, u32, u32, u32); + struct sparx5 *sparx5 = port->sparx5; + + if (!sh->rate && !sh->burst) + sparx5_lg_action = &sparx5_lg_del; + else + sparx5_lg_action = &sparx5_lg_add; + + /* Select layer */ + spx5_rmw(HSCH_HSCH_CFG_CFG_HSCH_LAYER_SET(layer), + HSCH_HSCH_CFG_CFG_HSCH_LAYER, sparx5, HSCH_HSCH_CFG_CFG); + + /* Set frame mode */ + spx5_rmw(HSCH_SE_CFG_SE_FRM_MODE_SET(sh->mode), HSCH_SE_CFG_SE_FRM_MODE, + sparx5, HSCH_SE_CFG(idx)); + + /* Set committed rate and burst */ + spx5_wr(HSCH_CIR_CFG_CIR_RATE_SET(sh->rate) | + HSCH_CIR_CFG_CIR_BURST_SET(sh->burst), + sparx5, HSCH_CIR_CFG(idx)); + + /* This has to be done after the shaper configuration has been set */ + sparx5_lg_action(sparx5, layer, group, idx); + + return 0; +} + +static u32 sparx5_weight_to_hw_cost(u32 weight_min, u32 weight) +{ + return ((((SPX5_DWRR_COST_MAX << 4) * weight_min / weight) + 8) >> 4) - + 1; +} + +static int sparx5_dwrr_conf_set(struct sparx5_port *port, + struct sparx5_dwrr *dwrr) +{ + int i; + + spx5_rmw(HSCH_HSCH_CFG_CFG_HSCH_LAYER_SET(2) | + HSCH_HSCH_CFG_CFG_CFG_SE_IDX_SET(port->portno), + HSCH_HSCH_CFG_CFG_HSCH_LAYER | HSCH_HSCH_CFG_CFG_CFG_SE_IDX, + port->sparx5, HSCH_HSCH_CFG_CFG); + + /* Number of *lower* indexes that are arbitrated dwrr */ + spx5_rmw(HSCH_SE_CFG_SE_DWRR_CNT_SET(dwrr->count), + HSCH_SE_CFG_SE_DWRR_CNT, port->sparx5, + HSCH_SE_CFG(port->portno)); + + for (i = 0; i < dwrr->count; i++) { + spx5_rmw(HSCH_DWRR_ENTRY_DWRR_COST_SET(dwrr->cost[i]), + HSCH_DWRR_ENTRY_DWRR_COST, port->sparx5, + HSCH_DWRR_ENTRY(i)); + } + + return 0; +} + +static int sparx5_leak_groups_init(struct sparx5 *sparx5) +{ + struct sparx5_layer *layer; + u32 sys_clk_per_100ps; + struct sparx5_lg *lg; + u32 leak_time_us; + int i, ii; + + sys_clk_per_100ps = spx5_rd(sparx5, HSCH_SYS_CLK_PER); + + for (i = 0; i < SPX5_HSCH_LAYER_CNT; i++) { + layer = &layers[i]; + for (ii = 0; ii < SPX5_HSCH_LEAK_GRP_CNT; ii++) { + lg = &layer->leak_groups[ii]; + lg->max_rate = spx5_hsch_max_group_rate[ii]; + + /* Calculate the leak time in us, to serve a maximum + * rate of 'max_rate' for this group + */ + leak_time_us = (SPX5_SE_RATE_MAX * 1000) / lg->max_rate; + + /* Hardware wants leak time in ns */ + lg->leak_time = 1000 * leak_time_us; + + /* Calculate resolution */ + lg->resolution = 1000 / leak_time_us; + + /* Maximum number of shapers that can be served by + * this leak group + */ + lg->max_ses = (1000 * leak_time_us) / sys_clk_per_100ps; + + /* Example: + * Wanted bandwidth is 100Mbit: + * + * 100 mbps can be served by leak group zero. + * + * leak_time is 125000 ns. + * resolution is: 8 + * + * cir = 100000 / 8 = 12500 + * leaks_pr_sec = 125000 / 10^9 = 8000 + * bw = 12500 * 8000 = 10^8 (100 Mbit) + */ + + /* Disable by default - this also indicates an empty + * leak group + */ + sparx5_lg_disable(sparx5, i, ii); + } + } + + return 0; +} + +int sparx5_qos_init(struct sparx5 *sparx5) +{ + int ret; + + ret = sparx5_leak_groups_init(sparx5); + if (ret < 0) + return ret; + + return 0; +} + +int sparx5_tc_mqprio_add(struct net_device *ndev, u8 num_tc) +{ + int i; + + if (num_tc != SPX5_PRIOS) { + netdev_err(ndev, "Only %d traffic classes supported\n", + SPX5_PRIOS); + return -EINVAL; + } + + netdev_set_num_tc(ndev, num_tc); + + for (i = 0; i < num_tc; i++) + netdev_set_tc_queue(ndev, i, 1, i); + + netdev_dbg(ndev, "dev->num_tc %u dev->real_num_tx_queues %u\n", + ndev->num_tc, ndev->real_num_tx_queues); + + return 0; +} + +int sparx5_tc_mqprio_del(struct net_device *ndev) +{ + netdev_reset_tc(ndev); + + netdev_dbg(ndev, "dev->num_tc %u dev->real_num_tx_queues %u\n", + ndev->num_tc, ndev->real_num_tx_queues); + + return 0; +} + +int sparx5_tc_tbf_add(struct sparx5_port *port, + struct tc_tbf_qopt_offload_replace_params *params, + u32 layer, u32 idx) +{ + struct sparx5_shaper sh = { + .mode = SPX5_SE_MODE_DATARATE, + .rate = div_u64(params->rate.rate_bytes_ps, 1000) * 8, + .burst = params->max_size, + }; + struct sparx5_lg *lg; + u32 group; + + /* Find suitable group for this se */ + if (sparx5_lg_get_group_by_rate(layer, sh.rate, &group) < 0) { + pr_debug("Could not find leak group for se with rate: %d", + sh.rate); + return -EINVAL; + } + + lg = &layers[layer].leak_groups[group]; + + pr_debug("Found matching group (speed: %d)\n", lg->max_rate); + + if (sh.rate < SPX5_SE_RATE_MIN || sh.burst < SPX5_SE_BURST_MIN) + return -EINVAL; + + /* Calculate committed rate and burst */ + sh.rate = DIV_ROUND_UP(sh.rate, lg->resolution); + sh.burst = DIV_ROUND_UP(sh.burst, SPX5_SE_BURST_UNIT); + + if (sh.rate > SPX5_SE_RATE_MAX || sh.burst > SPX5_SE_BURST_MAX) + return -EINVAL; + + return sparx5_shaper_conf_set(port, &sh, layer, idx, group); +} + +int sparx5_tc_tbf_del(struct sparx5_port *port, u32 layer, u32 idx) +{ + struct sparx5_shaper sh = {0}; + u32 group; + + sparx5_lg_get_group_by_index(port->sparx5, layer, idx, &group); + + return sparx5_shaper_conf_set(port, &sh, layer, idx, group); +} + +int sparx5_tc_ets_add(struct sparx5_port *port, + struct tc_ets_qopt_offload_replace_params *params) +{ + struct sparx5_dwrr dwrr = {0}; + /* Minimum weight for each iteration */ + unsigned int w_min = 100; + int i; + + /* Find minimum weight for all dwrr bands */ + for (i = 0; i < SPX5_PRIOS; i++) { + if (params->quanta[i] == 0) + continue; + w_min = min(w_min, params->weights[i]); + } + + for (i = 0; i < SPX5_PRIOS; i++) { + /* Strict band; skip */ + if (params->quanta[i] == 0) + continue; + + dwrr.count++; + + /* On the sparx5, bands with higher indexes are preferred and + * arbitrated strict. Strict bands are put in the lower indexes, + * by tc, so we reverse the bands here. + * + * Also convert the weight to something the hardware + * understands. + */ + dwrr.cost[SPX5_PRIOS - i - 1] = + sparx5_weight_to_hw_cost(w_min, params->weights[i]); + } + + return sparx5_dwrr_conf_set(port, &dwrr); +} + +int sparx5_tc_ets_del(struct sparx5_port *port) +{ + struct sparx5_dwrr dwrr = {0}; + + return sparx5_dwrr_conf_set(port, &dwrr); +} diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_qos.h b/drivers/net/ethernet/microchip/sparx5/sparx5_qos.h new file mode 100644 index 000000000000..ced35033a6c5 --- /dev/null +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_qos.h @@ -0,0 +1,82 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* Microchip Sparx5 Switch driver + * + * Copyright (c) 2022 Microchip Technology Inc. and its subsidiaries. + */ + +#ifndef __SPARX5_QOS_H__ +#define __SPARX5_QOS_H__ + +#include <linux/netdevice.h> + +/* Number of Layers */ +#define SPX5_HSCH_LAYER_CNT 3 + +/* Scheduling elements per layer */ +#define SPX5_HSCH_L0_SE_CNT 5040 +#define SPX5_HSCH_L1_SE_CNT 64 +#define SPX5_HSCH_L2_SE_CNT 64 + +/* Calculate Layer 0 Scheduler Element when using normal hierarchy */ +#define SPX5_HSCH_L0_GET_IDX(port, queue) ((64 * (port)) + (8 * (queue))) + +/* Number of leak groups */ +#define SPX5_HSCH_LEAK_GRP_CNT 4 + +/* Scheduler modes */ +#define SPX5_SE_MODE_LINERATE 0 +#define SPX5_SE_MODE_DATARATE 1 + +/* Rate and burst */ +#define SPX5_SE_RATE_MAX 262143 +#define SPX5_SE_BURST_MAX 127 +#define SPX5_SE_RATE_MIN 1 +#define SPX5_SE_BURST_MIN 1 +#define SPX5_SE_BURST_UNIT 4096 + +/* Dwrr */ +#define SPX5_DWRR_COST_MAX 63 + +struct sparx5_shaper { + u32 mode; + u32 rate; + u32 burst; +}; + +struct sparx5_lg { + u32 max_rate; + u32 resolution; + u32 leak_time; + u32 max_ses; +}; + +struct sparx5_layer { + struct sparx5_lg leak_groups[SPX5_HSCH_LEAK_GRP_CNT]; +}; + +struct sparx5_dwrr { + u32 count; /* Number of inputs running dwrr */ + u8 cost[SPX5_PRIOS]; +}; + +int sparx5_qos_init(struct sparx5 *sparx5); + +/* Multi-Queue Priority */ +int sparx5_tc_mqprio_add(struct net_device *ndev, u8 num_tc); +int sparx5_tc_mqprio_del(struct net_device *ndev); + +/* Token Bucket Filter */ +struct tc_tbf_qopt_offload_replace_params; +int sparx5_tc_tbf_add(struct sparx5_port *port, + struct tc_tbf_qopt_offload_replace_params *params, + u32 layer, u32 idx); +int sparx5_tc_tbf_del(struct sparx5_port *port, u32 layer, u32 idx); + +/* Enhanced Transmission Selection */ +struct tc_ets_qopt_offload_replace_params; +int sparx5_tc_ets_add(struct sparx5_port *port, + struct tc_ets_qopt_offload_replace_params *params); + +int sparx5_tc_ets_del(struct sparx5_port *port); + +#endif /* __SPARX5_QOS_H__ */ diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_switchdev.c b/drivers/net/ethernet/microchip/sparx5/sparx5_switchdev.c index ec07f7d0528c..4af85d108a06 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_switchdev.c +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_switchdev.c @@ -29,14 +29,23 @@ static int sparx5_port_attr_pre_bridge_flags(struct sparx5_port *port, return 0; } +static void sparx5_port_update_mcast_ip_flood(struct sparx5_port *port, bool flood_flag) +{ + bool should_flood = flood_flag || port->is_mrouter; + int pgid; + + for (pgid = PGID_IPV4_MC_DATA; pgid <= PGID_IPV6_MC_CTRL; pgid++) + sparx5_pgid_update_mask(port, pgid, should_flood); +} + static void sparx5_port_attr_bridge_flags(struct sparx5_port *port, struct switchdev_brport_flags flags) { - int pgid; + if (flags.mask & BR_MCAST_FLOOD) { + sparx5_pgid_update_mask(port, PGID_MC_FLOOD, !!(flags.val & BR_MCAST_FLOOD)); + sparx5_port_update_mcast_ip_flood(port, !!(flags.val & BR_MCAST_FLOOD)); + } - if (flags.mask & BR_MCAST_FLOOD) - for (pgid = PGID_MC_FLOOD; pgid <= PGID_IPV6_MC_CTRL; pgid++) - sparx5_pgid_update_mask(port, pgid, !!(flags.val & BR_MCAST_FLOOD)); if (flags.mask & BR_FLOOD) sparx5_pgid_update_mask(port, PGID_UC_FLOOD, !!(flags.val & BR_FLOOD)); if (flags.mask & BR_BCAST_FLOOD) @@ -82,6 +91,37 @@ static void sparx5_port_attr_ageing_set(struct sparx5_port *port, sparx5_set_ageing(port->sparx5, ageing_time); } +static void sparx5_port_attr_mrouter_set(struct sparx5_port *port, + struct net_device *orig_dev, + bool enable) +{ + struct sparx5 *sparx5 = port->sparx5; + struct sparx5_mdb_entry *e; + bool flood_flag; + + if ((enable && port->is_mrouter) || (!enable && !port->is_mrouter)) + return; + + /* Add/del mrouter port on all active mdb entries in HW. + * Don't change entry port mask, since that represents + * ports that actually joined that group. + */ + mutex_lock(&sparx5->mdb_lock); + list_for_each_entry(e, &sparx5->mdb_entries, list) { + if (!test_bit(port->portno, e->port_mask) && + ether_addr_is_ip_mcast(e->addr)) + sparx5_pgid_update_mask(port, e->pgid_idx, enable); + } + mutex_unlock(&sparx5->mdb_lock); + + /* Enable/disable flooding depending on if port is mrouter port + * or if mcast flood is enabled. + */ + port->is_mrouter = enable; + flood_flag = br_port_flag_is_set(port->ndev, BR_MCAST_FLOOD); + sparx5_port_update_mcast_ip_flood(port, flood_flag); +} + static int sparx5_port_attr_set(struct net_device *dev, const void *ctx, const struct switchdev_attr *attr, struct netlink_ext_ack *extack) @@ -110,6 +150,11 @@ static int sparx5_port_attr_set(struct net_device *dev, const void *ctx, port->vlan_aware = attr->u.vlan_filtering; sparx5_vlan_port_apply(port->sparx5, port); break; + case SWITCHDEV_ATTR_ID_PORT_MROUTER: + sparx5_port_attr_mrouter_set(port, + attr->orig_dev, + attr->u.mrouter); + break; default: return -EOPNOTSUPP; } @@ -386,16 +431,95 @@ static int sparx5_handle_port_vlan_add(struct net_device *dev, v->flags & BRIDGE_VLAN_INFO_UNTAGGED); } +static int sparx5_alloc_mdb_entry(struct sparx5 *sparx5, + const unsigned char *addr, + u16 vid, + struct sparx5_mdb_entry **entry_out) +{ + struct sparx5_mdb_entry *entry; + u16 pgid_idx; + int err; + + entry = kzalloc(sizeof(*entry), GFP_KERNEL); + if (!entry) + return -ENOMEM; + + err = sparx5_pgid_alloc_mcast(sparx5, &pgid_idx); + if (err) { + kfree(entry); + return err; + } + + memcpy(entry->addr, addr, ETH_ALEN); + entry->vid = vid; + entry->pgid_idx = pgid_idx; + + mutex_lock(&sparx5->mdb_lock); + list_add_tail(&entry->list, &sparx5->mdb_entries); + mutex_unlock(&sparx5->mdb_lock); + + *entry_out = entry; + return 0; +} + +static void sparx5_free_mdb_entry(struct sparx5 *sparx5, + const unsigned char *addr, + u16 vid) +{ + struct sparx5_mdb_entry *entry, *tmp; + + mutex_lock(&sparx5->mdb_lock); + list_for_each_entry_safe(entry, tmp, &sparx5->mdb_entries, list) { + if ((vid == 0 || entry->vid == vid) && + ether_addr_equal(addr, entry->addr)) { + list_del(&entry->list); + + sparx5_pgid_free(sparx5, entry->pgid_idx); + kfree(entry); + goto out; + } + } + +out: + mutex_unlock(&sparx5->mdb_lock); +} + +static struct sparx5_mdb_entry *sparx5_mdb_get_entry(struct sparx5 *sparx5, + const unsigned char *addr, + u16 vid) +{ + struct sparx5_mdb_entry *e, *found = NULL; + + mutex_lock(&sparx5->mdb_lock); + list_for_each_entry(e, &sparx5->mdb_entries, list) { + if (ether_addr_equal(e->addr, addr) && e->vid == vid) { + found = e; + goto out; + } + } + +out: + mutex_unlock(&sparx5->mdb_lock); + return found; +} + +static void sparx5_cpu_copy_ena(struct sparx5 *spx5, u16 pgid, bool enable) +{ + spx5_rmw(ANA_AC_PGID_MISC_CFG_PGID_CPU_COPY_ENA_SET(enable), + ANA_AC_PGID_MISC_CFG_PGID_CPU_COPY_ENA, spx5, + ANA_AC_PGID_MISC_CFG(pgid)); +} + static int sparx5_handle_port_mdb_add(struct net_device *dev, struct notifier_block *nb, const struct switchdev_obj_port_mdb *v) { struct sparx5_port *port = netdev_priv(dev); struct sparx5 *spx5 = port->sparx5; - u16 pgid_idx, vid; - u32 mact_entry; - bool is_host; - int res, err; + struct sparx5_mdb_entry *entry; + bool is_host, is_new; + int err, i; + u16 vid; if (!sparx5_netdevice_check(dev)) return -EOPNOTSUPP; @@ -410,66 +534,36 @@ static int sparx5_handle_port_mdb_add(struct net_device *dev, else vid = v->vid; - res = sparx5_mact_find(spx5, v->addr, vid, &mact_entry); - - if (res == 0) { - pgid_idx = LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_ADDR_GET(mact_entry); - - /* MC_IDX starts after the port masks in the PGID table */ - pgid_idx += SPX5_PORTS; - - if (is_host) - spx5_rmw(ANA_AC_PGID_MISC_CFG_PGID_CPU_COPY_ENA_SET(1), - ANA_AC_PGID_MISC_CFG_PGID_CPU_COPY_ENA, spx5, - ANA_AC_PGID_MISC_CFG(pgid_idx)); - else - sparx5_pgid_update_mask(port, pgid_idx, true); - - } else { - err = sparx5_pgid_alloc_mcast(spx5, &pgid_idx); - if (err) { - netdev_warn(dev, "multicast pgid table full\n"); + is_new = false; + entry = sparx5_mdb_get_entry(spx5, v->addr, vid); + if (!entry) { + err = sparx5_alloc_mdb_entry(spx5, v->addr, vid, &entry); + is_new = true; + if (err) return err; - } - - if (is_host) - spx5_rmw(ANA_AC_PGID_MISC_CFG_PGID_CPU_COPY_ENA_SET(1), - ANA_AC_PGID_MISC_CFG_PGID_CPU_COPY_ENA, spx5, - ANA_AC_PGID_MISC_CFG(pgid_idx)); - else - sparx5_pgid_update_mask(port, pgid_idx, true); - - err = sparx5_mact_learn(spx5, pgid_idx, v->addr, vid); - - if (err) { - netdev_warn(dev, "could not learn mac address %pM\n", v->addr); - sparx5_pgid_free(spx5, pgid_idx); - sparx5_pgid_update_mask(port, pgid_idx, false); - return err; - } } - return 0; -} + mutex_lock(&spx5->mdb_lock); + + /* Add any mrouter ports to the new entry */ + if (is_new && ether_addr_is_ip_mcast(v->addr)) + for (i = 0; i < SPX5_PORTS; i++) + if (spx5->ports[i] && spx5->ports[i]->is_mrouter) + sparx5_pgid_update_mask(spx5->ports[i], + entry->pgid_idx, + true); + + if (is_host && !entry->cpu_copy) { + sparx5_cpu_copy_ena(spx5, entry->pgid_idx, true); + entry->cpu_copy = true; + } else if (!is_host) { + sparx5_pgid_update_mask(port, entry->pgid_idx, true); + set_bit(port->portno, entry->port_mask); + } + mutex_unlock(&spx5->mdb_lock); -static int sparx5_mdb_del_entry(struct net_device *dev, - struct sparx5 *spx5, - const unsigned char mac[ETH_ALEN], - const u16 vid, - u16 pgid_idx) -{ - int err; + sparx5_mact_learn(spx5, entry->pgid_idx, entry->addr, entry->vid); - err = sparx5_mact_forget(spx5, mac, vid); - if (err) { - netdev_warn(dev, "could not forget mac address %pM", mac); - return err; - } - err = sparx5_pgid_free(spx5, pgid_idx); - if (err) { - netdev_err(dev, "attempted to free already freed pgid\n"); - return err; - } return 0; } @@ -479,42 +573,45 @@ static int sparx5_handle_port_mdb_del(struct net_device *dev, { struct sparx5_port *port = netdev_priv(dev); struct sparx5 *spx5 = port->sparx5; - u16 pgid_idx, vid; - u32 mact_entry, res, pgid_entry[3], misc_cfg; - bool host_ena; + struct sparx5_mdb_entry *entry; + bool is_host; + u16 vid; if (!sparx5_netdevice_check(dev)) return -EOPNOTSUPP; + is_host = netif_is_bridge_master(v->obj.orig_dev); + if (!br_vlan_enabled(spx5->hw_bridge_dev)) vid = 1; else vid = v->vid; - res = sparx5_mact_find(spx5, v->addr, vid, &mact_entry); - - if (res == 0) { - pgid_idx = LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_ADDR_GET(mact_entry); - - /* MC_IDX starts after the port masks in the PGID table */ - pgid_idx += SPX5_PORTS; - - if (netif_is_bridge_master(v->obj.orig_dev)) - spx5_rmw(ANA_AC_PGID_MISC_CFG_PGID_CPU_COPY_ENA_SET(0), - ANA_AC_PGID_MISC_CFG_PGID_CPU_COPY_ENA, spx5, - ANA_AC_PGID_MISC_CFG(pgid_idx)); - else - sparx5_pgid_update_mask(port, pgid_idx, false); + entry = sparx5_mdb_get_entry(spx5, v->addr, vid); + if (!entry) + return 0; - misc_cfg = spx5_rd(spx5, ANA_AC_PGID_MISC_CFG(pgid_idx)); - host_ena = ANA_AC_PGID_MISC_CFG_PGID_CPU_COPY_ENA_GET(misc_cfg); + mutex_lock(&spx5->mdb_lock); + if (is_host && entry->cpu_copy) { + sparx5_cpu_copy_ena(spx5, entry->pgid_idx, false); + entry->cpu_copy = false; + } else if (!is_host) { + clear_bit(port->portno, entry->port_mask); - sparx5_pgid_read_mask(spx5, pgid_idx, pgid_entry); - if (bitmap_empty((unsigned long *)pgid_entry, SPX5_PORTS) && !host_ena) - /* No ports or CPU are in MC group. Remove entry */ - return sparx5_mdb_del_entry(dev, spx5, v->addr, vid, pgid_idx); + /* Port not mrouter port or addr is L2 mcast, remove port from mask. */ + if (!port->is_mrouter || !ether_addr_is_ip_mcast(v->addr)) + sparx5_pgid_update_mask(port, entry->pgid_idx, false); + } + mutex_unlock(&spx5->mdb_lock); + + if (bitmap_empty(entry->port_mask, SPX5_PORTS) && !entry->cpu_copy) { + /* Clear pgid in case mrouter ports exists + * that are not part of the group. + */ + sparx5_pgid_clear(spx5, entry->pgid_idx); + sparx5_mact_forget(spx5, entry->addr, entry->vid); + sparx5_free_mdb_entry(spx5, entry->addr, entry->vid); } - return 0; } diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_tc.c b/drivers/net/ethernet/microchip/sparx5/sparx5_tc.c new file mode 100644 index 000000000000..e05429c751ee --- /dev/null +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_tc.c @@ -0,0 +1,125 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* Microchip Sparx5 Switch driver + * + * Copyright (c) 2022 Microchip Technology Inc. and its subsidiaries. + */ + +#include <net/pkt_cls.h> + +#include "sparx5_tc.h" +#include "sparx5_main.h" +#include "sparx5_qos.h" + +static void sparx5_tc_get_layer_and_idx(u32 parent, u32 portno, u32 *layer, + u32 *idx) +{ + if (parent == TC_H_ROOT) { + *layer = 2; + *idx = portno; + } else { + u32 queue = TC_H_MIN(parent) - 1; + *layer = 0; + *idx = SPX5_HSCH_L0_GET_IDX(portno, queue); + } +} + +static int sparx5_tc_setup_qdisc_mqprio(struct net_device *ndev, + struct tc_mqprio_qopt_offload *m) +{ + m->qopt.hw = TC_MQPRIO_HW_OFFLOAD_TCS; + + if (m->qopt.num_tc == 0) + return sparx5_tc_mqprio_del(ndev); + else + return sparx5_tc_mqprio_add(ndev, m->qopt.num_tc); +} + +static int sparx5_tc_setup_qdisc_tbf(struct net_device *ndev, + struct tc_tbf_qopt_offload *qopt) +{ + struct sparx5_port *port = netdev_priv(ndev); + u32 layer, se_idx; + + sparx5_tc_get_layer_and_idx(qopt->parent, port->portno, &layer, + &se_idx); + + switch (qopt->command) { + case TC_TBF_REPLACE: + return sparx5_tc_tbf_add(port, &qopt->replace_params, layer, + se_idx); + case TC_TBF_DESTROY: + return sparx5_tc_tbf_del(port, layer, se_idx); + case TC_TBF_STATS: + return -EOPNOTSUPP; + default: + return -EOPNOTSUPP; + } + + return -EOPNOTSUPP; +} + +static int sparx5_tc_setup_qdisc_ets(struct net_device *ndev, + struct tc_ets_qopt_offload *qopt) +{ + struct tc_ets_qopt_offload_replace_params *params = + &qopt->replace_params; + struct sparx5_port *port = netdev_priv(ndev); + int i; + + /* Only allow ets on ports */ + if (qopt->parent != TC_H_ROOT) + return -EOPNOTSUPP; + + switch (qopt->command) { + case TC_ETS_REPLACE: + + /* We support eight priorities */ + if (params->bands != SPX5_PRIOS) + return -EOPNOTSUPP; + + /* Sanity checks */ + for (i = 0; i < SPX5_PRIOS; ++i) { + /* Priority map is *always* reverse e.g: 7 6 5 .. 0 */ + if (params->priomap[i] != (7 - i)) + return -EOPNOTSUPP; + /* Throw an error if we receive zero weights by tc */ + if (params->quanta[i] && params->weights[i] == 0) { + pr_err("Invalid ets configuration; band %d has weight zero", + i); + return -EINVAL; + } + } + + sparx5_tc_ets_add(port, params); + break; + case TC_ETS_DESTROY: + + sparx5_tc_ets_del(port); + + break; + case TC_ETS_GRAFT: + return -EOPNOTSUPP; + + default: + return -EOPNOTSUPP; + } + + return -EOPNOTSUPP; +} + +int sparx5_port_setup_tc(struct net_device *ndev, enum tc_setup_type type, + void *type_data) +{ + switch (type) { + case TC_SETUP_QDISC_MQPRIO: + return sparx5_tc_setup_qdisc_mqprio(ndev, type_data); + case TC_SETUP_QDISC_TBF: + return sparx5_tc_setup_qdisc_tbf(ndev, type_data); + case TC_SETUP_QDISC_ETS: + return sparx5_tc_setup_qdisc_ets(ndev, type_data); + default: + return -EOPNOTSUPP; + } + + return 0; +} diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_tc.h b/drivers/net/ethernet/microchip/sparx5/sparx5_tc.h new file mode 100644 index 000000000000..5b55e11b77e1 --- /dev/null +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_tc.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* Microchip Sparx5 Switch driver + * + * Copyright (c) 2022 Microchip Technology Inc. and its subsidiaries. + */ + +#ifndef __SPARX5_TC_H__ +#define __SPARX5_TC_H__ + +#include <linux/netdevice.h> + +int sparx5_port_setup_tc(struct net_device *ndev, enum tc_setup_type type, + void *type_data); + +#endif /* __SPARX5_TC_H__ */ diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_vlan.c b/drivers/net/ethernet/microchip/sparx5/sparx5_vlan.c index 37e4ac965849..34f954bbf815 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_vlan.c +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_vlan.c @@ -138,6 +138,13 @@ void sparx5_pgid_update_mask(struct sparx5_port *port, int pgid, bool enable) } } +void sparx5_pgid_clear(struct sparx5 *spx5, int pgid) +{ + spx5_wr(0, spx5, ANA_AC_PGID_CFG(pgid)); + spx5_wr(0, spx5, ANA_AC_PGID_CFG1(pgid)); + spx5_wr(0, spx5, ANA_AC_PGID_CFG2(pgid)); +} + void sparx5_pgid_read_mask(struct sparx5 *spx5, int pgid, u32 portmask[3]) { portmask[0] = spx5_rd(spx5, ANA_AC_PGID_CFG(pgid)); diff --git a/drivers/net/ethernet/moxa/moxart_ether.c b/drivers/net/ethernet/moxa/moxart_ether.c index 9e57d23e57bf..3da99b62797d 100644 --- a/drivers/net/ethernet/moxa/moxart_ether.c +++ b/drivers/net/ethernet/moxa/moxart_ether.c @@ -29,12 +29,12 @@ #include "moxart_ether.h" -static inline void moxart_desc_write(u32 data, u32 *desc) +static inline void moxart_desc_write(u32 data, __le32 *desc) { *desc = cpu_to_le32(data); } -static inline u32 moxart_desc_read(u32 *desc) +static inline u32 moxart_desc_read(__le32 *desc) { return le32_to_cpu(*desc); } diff --git a/drivers/net/ethernet/mscc/Makefile b/drivers/net/ethernet/mscc/Makefile index 41b34a509308..5d435a565d4c 100644 --- a/drivers/net/ethernet/mscc/Makefile +++ b/drivers/net/ethernet/mscc/Makefile @@ -2,16 +2,17 @@ obj-$(CONFIG_MSCC_OCELOT_SWITCH_LIB) += mscc_ocelot_switch_lib.o mscc_ocelot_switch_lib-y := \ ocelot.o \ + ocelot_devlink.o \ + ocelot_flower.o \ ocelot_io.o \ ocelot_police.o \ - ocelot_vcap.o \ - ocelot_flower.o \ ocelot_ptp.o \ - ocelot_devlink.o \ + ocelot_stats.o \ + ocelot_vcap.o \ vsc7514_regs.o mscc_ocelot_switch_lib-$(CONFIG_BRIDGE_MRP) += ocelot_mrp.o obj-$(CONFIG_MSCC_OCELOT_SWITCH) += mscc_ocelot.o mscc_ocelot-y := \ ocelot_fdma.o \ - ocelot_vsc7514.o \ - ocelot_net.o + ocelot_net.o \ + ocelot_vsc7514.o diff --git a/drivers/net/ethernet/mscc/ocelot.c b/drivers/net/ethernet/mscc/ocelot.c index 8f8005bc6eea..13b14110a060 100644 --- a/drivers/net/ethernet/mscc/ocelot.c +++ b/drivers/net/ethernet/mscc/ocelot.c @@ -6,7 +6,6 @@ */ #include <linux/dsa/ocelot.h> #include <linux/if_bridge.h> -#include <linux/ptp_classify.h> #include <soc/mscc/ocelot_vcap.h> #include "ocelot.h" #include "ocelot_vcap.h" @@ -917,211 +916,6 @@ void ocelot_phylink_mac_link_up(struct ocelot *ocelot, int port, } EXPORT_SYMBOL_GPL(ocelot_phylink_mac_link_up); -static int ocelot_port_add_txtstamp_skb(struct ocelot *ocelot, int port, - struct sk_buff *clone) -{ - struct ocelot_port *ocelot_port = ocelot->ports[port]; - unsigned long flags; - - spin_lock_irqsave(&ocelot->ts_id_lock, flags); - - if (ocelot_port->ptp_skbs_in_flight == OCELOT_MAX_PTP_ID || - ocelot->ptp_skbs_in_flight == OCELOT_PTP_FIFO_SIZE) { - spin_unlock_irqrestore(&ocelot->ts_id_lock, flags); - return -EBUSY; - } - - skb_shinfo(clone)->tx_flags |= SKBTX_IN_PROGRESS; - /* Store timestamp ID in OCELOT_SKB_CB(clone)->ts_id */ - OCELOT_SKB_CB(clone)->ts_id = ocelot_port->ts_id; - - ocelot_port->ts_id++; - if (ocelot_port->ts_id == OCELOT_MAX_PTP_ID) - ocelot_port->ts_id = 0; - - ocelot_port->ptp_skbs_in_flight++; - ocelot->ptp_skbs_in_flight++; - - skb_queue_tail(&ocelot_port->tx_skbs, clone); - - spin_unlock_irqrestore(&ocelot->ts_id_lock, flags); - - return 0; -} - -static bool ocelot_ptp_is_onestep_sync(struct sk_buff *skb, - unsigned int ptp_class) -{ - struct ptp_header *hdr; - u8 msgtype, twostep; - - hdr = ptp_parse_header(skb, ptp_class); - if (!hdr) - return false; - - msgtype = ptp_get_msgtype(hdr, ptp_class); - twostep = hdr->flag_field[0] & 0x2; - - if (msgtype == PTP_MSGTYPE_SYNC && twostep == 0) - return true; - - return false; -} - -int ocelot_port_txtstamp_request(struct ocelot *ocelot, int port, - struct sk_buff *skb, - struct sk_buff **clone) -{ - struct ocelot_port *ocelot_port = ocelot->ports[port]; - u8 ptp_cmd = ocelot_port->ptp_cmd; - unsigned int ptp_class; - int err; - - /* Don't do anything if PTP timestamping not enabled */ - if (!ptp_cmd) - return 0; - - ptp_class = ptp_classify_raw(skb); - if (ptp_class == PTP_CLASS_NONE) - return -EINVAL; - - /* Store ptp_cmd in OCELOT_SKB_CB(skb)->ptp_cmd */ - if (ptp_cmd == IFH_REW_OP_ORIGIN_PTP) { - if (ocelot_ptp_is_onestep_sync(skb, ptp_class)) { - OCELOT_SKB_CB(skb)->ptp_cmd = ptp_cmd; - return 0; - } - - /* Fall back to two-step timestamping */ - ptp_cmd = IFH_REW_OP_TWO_STEP_PTP; - } - - if (ptp_cmd == IFH_REW_OP_TWO_STEP_PTP) { - *clone = skb_clone_sk(skb); - if (!(*clone)) - return -ENOMEM; - - err = ocelot_port_add_txtstamp_skb(ocelot, port, *clone); - if (err) - return err; - - OCELOT_SKB_CB(skb)->ptp_cmd = ptp_cmd; - OCELOT_SKB_CB(*clone)->ptp_class = ptp_class; - } - - return 0; -} -EXPORT_SYMBOL(ocelot_port_txtstamp_request); - -static void ocelot_get_hwtimestamp(struct ocelot *ocelot, - struct timespec64 *ts) -{ - unsigned long flags; - u32 val; - - spin_lock_irqsave(&ocelot->ptp_clock_lock, flags); - - /* Read current PTP time to get seconds */ - val = ocelot_read_rix(ocelot, PTP_PIN_CFG, TOD_ACC_PIN); - - val &= ~(PTP_PIN_CFG_SYNC | PTP_PIN_CFG_ACTION_MASK | PTP_PIN_CFG_DOM); - val |= PTP_PIN_CFG_ACTION(PTP_PIN_ACTION_SAVE); - ocelot_write_rix(ocelot, val, PTP_PIN_CFG, TOD_ACC_PIN); - ts->tv_sec = ocelot_read_rix(ocelot, PTP_PIN_TOD_SEC_LSB, TOD_ACC_PIN); - - /* Read packet HW timestamp from FIFO */ - val = ocelot_read(ocelot, SYS_PTP_TXSTAMP); - ts->tv_nsec = SYS_PTP_TXSTAMP_PTP_TXSTAMP(val); - - /* Sec has incremented since the ts was registered */ - if ((ts->tv_sec & 0x1) != !!(val & SYS_PTP_TXSTAMP_PTP_TXSTAMP_SEC)) - ts->tv_sec--; - - spin_unlock_irqrestore(&ocelot->ptp_clock_lock, flags); -} - -static bool ocelot_validate_ptp_skb(struct sk_buff *clone, u16 seqid) -{ - struct ptp_header *hdr; - - hdr = ptp_parse_header(clone, OCELOT_SKB_CB(clone)->ptp_class); - if (WARN_ON(!hdr)) - return false; - - return seqid == ntohs(hdr->sequence_id); -} - -void ocelot_get_txtstamp(struct ocelot *ocelot) -{ - int budget = OCELOT_PTP_QUEUE_SZ; - - while (budget--) { - struct sk_buff *skb, *skb_tmp, *skb_match = NULL; - struct skb_shared_hwtstamps shhwtstamps; - u32 val, id, seqid, txport; - struct ocelot_port *port; - struct timespec64 ts; - unsigned long flags; - - val = ocelot_read(ocelot, SYS_PTP_STATUS); - - /* Check if a timestamp can be retrieved */ - if (!(val & SYS_PTP_STATUS_PTP_MESS_VLD)) - break; - - WARN_ON(val & SYS_PTP_STATUS_PTP_OVFL); - - /* Retrieve the ts ID and Tx port */ - id = SYS_PTP_STATUS_PTP_MESS_ID_X(val); - txport = SYS_PTP_STATUS_PTP_MESS_TXPORT_X(val); - seqid = SYS_PTP_STATUS_PTP_MESS_SEQ_ID(val); - - port = ocelot->ports[txport]; - - spin_lock(&ocelot->ts_id_lock); - port->ptp_skbs_in_flight--; - ocelot->ptp_skbs_in_flight--; - spin_unlock(&ocelot->ts_id_lock); - - /* Retrieve its associated skb */ -try_again: - spin_lock_irqsave(&port->tx_skbs.lock, flags); - - skb_queue_walk_safe(&port->tx_skbs, skb, skb_tmp) { - if (OCELOT_SKB_CB(skb)->ts_id != id) - continue; - __skb_unlink(skb, &port->tx_skbs); - skb_match = skb; - break; - } - - spin_unlock_irqrestore(&port->tx_skbs.lock, flags); - - if (WARN_ON(!skb_match)) - continue; - - if (!ocelot_validate_ptp_skb(skb_match, seqid)) { - dev_err_ratelimited(ocelot->dev, - "port %d received stale TX timestamp for seqid %d, discarding\n", - txport, seqid); - dev_kfree_skb_any(skb); - goto try_again; - } - - /* Get the h/w timestamp */ - ocelot_get_hwtimestamp(ocelot, &ts); - - /* Set the timestamp into the skb */ - memset(&shhwtstamps, 0, sizeof(shhwtstamps)); - shhwtstamps.hwtstamp = ktime_set(ts.tv_sec, ts.tv_nsec); - skb_complete_tx_timestamp(skb_match, &shhwtstamps); - - /* Next ts */ - ocelot_write(ocelot, SYS_PTP_NXT_PTP_NXT, SYS_PTP_NXT); - } -} -EXPORT_SYMBOL(ocelot_get_txtstamp); - static int ocelot_rx_frame_word(struct ocelot *ocelot, u8 grp, bool ifh, u32 *rval) { @@ -1373,50 +1167,6 @@ int ocelot_fdb_del(struct ocelot *ocelot, int port, const unsigned char *addr, } EXPORT_SYMBOL(ocelot_fdb_del); -int ocelot_port_fdb_do_dump(const unsigned char *addr, u16 vid, - bool is_static, void *data) -{ - struct ocelot_dump_ctx *dump = data; - u32 portid = NETLINK_CB(dump->cb->skb).portid; - u32 seq = dump->cb->nlh->nlmsg_seq; - struct nlmsghdr *nlh; - struct ndmsg *ndm; - - if (dump->idx < dump->cb->args[2]) - goto skip; - - nlh = nlmsg_put(dump->skb, portid, seq, RTM_NEWNEIGH, - sizeof(*ndm), NLM_F_MULTI); - if (!nlh) - return -EMSGSIZE; - - ndm = nlmsg_data(nlh); - ndm->ndm_family = AF_BRIDGE; - ndm->ndm_pad1 = 0; - ndm->ndm_pad2 = 0; - ndm->ndm_flags = NTF_SELF; - ndm->ndm_type = 0; - ndm->ndm_ifindex = dump->dev->ifindex; - ndm->ndm_state = is_static ? NUD_NOARP : NUD_REACHABLE; - - if (nla_put(dump->skb, NDA_LLADDR, ETH_ALEN, addr)) - goto nla_put_failure; - - if (vid && nla_put_u16(dump->skb, NDA_VLAN, vid)) - goto nla_put_failure; - - nlmsg_end(dump->skb, nlh); - -skip: - dump->idx++; - return 0; - -nla_put_failure: - nlmsg_cancel(dump->skb, nlh); - return -EMSGSIZE; -} -EXPORT_SYMBOL(ocelot_port_fdb_do_dump); - /* Caller must hold &ocelot->mact_lock */ static int ocelot_mact_read(struct ocelot *ocelot, int port, int row, int col, struct ocelot_mact_entry *entry) @@ -1548,53 +1298,6 @@ int ocelot_fdb_dump(struct ocelot *ocelot, int port, } EXPORT_SYMBOL(ocelot_fdb_dump); -static void ocelot_populate_l2_ptp_trap_key(struct ocelot_vcap_filter *trap) -{ - trap->key_type = OCELOT_VCAP_KEY_ETYPE; - *(__be16 *)trap->key.etype.etype.value = htons(ETH_P_1588); - *(__be16 *)trap->key.etype.etype.mask = htons(0xffff); -} - -static void -ocelot_populate_ipv4_ptp_event_trap_key(struct ocelot_vcap_filter *trap) -{ - trap->key_type = OCELOT_VCAP_KEY_IPV4; - trap->key.ipv4.proto.value[0] = IPPROTO_UDP; - trap->key.ipv4.proto.mask[0] = 0xff; - trap->key.ipv4.dport.value = PTP_EV_PORT; - trap->key.ipv4.dport.mask = 0xffff; -} - -static void -ocelot_populate_ipv6_ptp_event_trap_key(struct ocelot_vcap_filter *trap) -{ - trap->key_type = OCELOT_VCAP_KEY_IPV6; - trap->key.ipv4.proto.value[0] = IPPROTO_UDP; - trap->key.ipv4.proto.mask[0] = 0xff; - trap->key.ipv6.dport.value = PTP_EV_PORT; - trap->key.ipv6.dport.mask = 0xffff; -} - -static void -ocelot_populate_ipv4_ptp_general_trap_key(struct ocelot_vcap_filter *trap) -{ - trap->key_type = OCELOT_VCAP_KEY_IPV4; - trap->key.ipv4.proto.value[0] = IPPROTO_UDP; - trap->key.ipv4.proto.mask[0] = 0xff; - trap->key.ipv4.dport.value = PTP_GEN_PORT; - trap->key.ipv4.dport.mask = 0xffff; -} - -static void -ocelot_populate_ipv6_ptp_general_trap_key(struct ocelot_vcap_filter *trap) -{ - trap->key_type = OCELOT_VCAP_KEY_IPV6; - trap->key.ipv4.proto.value[0] = IPPROTO_UDP; - trap->key.ipv4.proto.mask[0] = 0xff; - trap->key.ipv6.dport.value = PTP_GEN_PORT; - trap->key.ipv6.dport.mask = 0xffff; -} - int ocelot_trap_add(struct ocelot *ocelot, int port, unsigned long cookie, bool take_ts, void (*populate)(struct ocelot_vcap_filter *f)) @@ -1663,381 +1366,6 @@ int ocelot_trap_del(struct ocelot *ocelot, int port, unsigned long cookie) return ocelot_vcap_filter_replace(ocelot, trap); } -static int ocelot_l2_ptp_trap_add(struct ocelot *ocelot, int port) -{ - unsigned long l2_cookie = OCELOT_VCAP_IS2_L2_PTP_TRAP(ocelot); - - return ocelot_trap_add(ocelot, port, l2_cookie, true, - ocelot_populate_l2_ptp_trap_key); -} - -static int ocelot_l2_ptp_trap_del(struct ocelot *ocelot, int port) -{ - unsigned long l2_cookie = OCELOT_VCAP_IS2_L2_PTP_TRAP(ocelot); - - return ocelot_trap_del(ocelot, port, l2_cookie); -} - -static int ocelot_ipv4_ptp_trap_add(struct ocelot *ocelot, int port) -{ - unsigned long ipv4_gen_cookie = OCELOT_VCAP_IS2_IPV4_GEN_PTP_TRAP(ocelot); - unsigned long ipv4_ev_cookie = OCELOT_VCAP_IS2_IPV4_EV_PTP_TRAP(ocelot); - int err; - - err = ocelot_trap_add(ocelot, port, ipv4_ev_cookie, true, - ocelot_populate_ipv4_ptp_event_trap_key); - if (err) - return err; - - err = ocelot_trap_add(ocelot, port, ipv4_gen_cookie, false, - ocelot_populate_ipv4_ptp_general_trap_key); - if (err) - ocelot_trap_del(ocelot, port, ipv4_ev_cookie); - - return err; -} - -static int ocelot_ipv4_ptp_trap_del(struct ocelot *ocelot, int port) -{ - unsigned long ipv4_gen_cookie = OCELOT_VCAP_IS2_IPV4_GEN_PTP_TRAP(ocelot); - unsigned long ipv4_ev_cookie = OCELOT_VCAP_IS2_IPV4_EV_PTP_TRAP(ocelot); - int err; - - err = ocelot_trap_del(ocelot, port, ipv4_ev_cookie); - err |= ocelot_trap_del(ocelot, port, ipv4_gen_cookie); - return err; -} - -static int ocelot_ipv6_ptp_trap_add(struct ocelot *ocelot, int port) -{ - unsigned long ipv6_gen_cookie = OCELOT_VCAP_IS2_IPV6_GEN_PTP_TRAP(ocelot); - unsigned long ipv6_ev_cookie = OCELOT_VCAP_IS2_IPV6_EV_PTP_TRAP(ocelot); - int err; - - err = ocelot_trap_add(ocelot, port, ipv6_ev_cookie, true, - ocelot_populate_ipv6_ptp_event_trap_key); - if (err) - return err; - - err = ocelot_trap_add(ocelot, port, ipv6_gen_cookie, false, - ocelot_populate_ipv6_ptp_general_trap_key); - if (err) - ocelot_trap_del(ocelot, port, ipv6_ev_cookie); - - return err; -} - -static int ocelot_ipv6_ptp_trap_del(struct ocelot *ocelot, int port) -{ - unsigned long ipv6_gen_cookie = OCELOT_VCAP_IS2_IPV6_GEN_PTP_TRAP(ocelot); - unsigned long ipv6_ev_cookie = OCELOT_VCAP_IS2_IPV6_EV_PTP_TRAP(ocelot); - int err; - - err = ocelot_trap_del(ocelot, port, ipv6_ev_cookie); - err |= ocelot_trap_del(ocelot, port, ipv6_gen_cookie); - return err; -} - -static int ocelot_setup_ptp_traps(struct ocelot *ocelot, int port, - bool l2, bool l4) -{ - int err; - - if (l2) - err = ocelot_l2_ptp_trap_add(ocelot, port); - else - err = ocelot_l2_ptp_trap_del(ocelot, port); - if (err) - return err; - - if (l4) { - err = ocelot_ipv4_ptp_trap_add(ocelot, port); - if (err) - goto err_ipv4; - - err = ocelot_ipv6_ptp_trap_add(ocelot, port); - if (err) - goto err_ipv6; - } else { - err = ocelot_ipv4_ptp_trap_del(ocelot, port); - - err |= ocelot_ipv6_ptp_trap_del(ocelot, port); - } - if (err) - return err; - - return 0; - -err_ipv6: - ocelot_ipv4_ptp_trap_del(ocelot, port); -err_ipv4: - if (l2) - ocelot_l2_ptp_trap_del(ocelot, port); - return err; -} - -int ocelot_hwstamp_get(struct ocelot *ocelot, int port, struct ifreq *ifr) -{ - return copy_to_user(ifr->ifr_data, &ocelot->hwtstamp_config, - sizeof(ocelot->hwtstamp_config)) ? -EFAULT : 0; -} -EXPORT_SYMBOL(ocelot_hwstamp_get); - -int ocelot_hwstamp_set(struct ocelot *ocelot, int port, struct ifreq *ifr) -{ - struct ocelot_port *ocelot_port = ocelot->ports[port]; - bool l2 = false, l4 = false; - struct hwtstamp_config cfg; - int err; - - if (copy_from_user(&cfg, ifr->ifr_data, sizeof(cfg))) - return -EFAULT; - - /* Tx type sanity check */ - switch (cfg.tx_type) { - case HWTSTAMP_TX_ON: - ocelot_port->ptp_cmd = IFH_REW_OP_TWO_STEP_PTP; - break; - case HWTSTAMP_TX_ONESTEP_SYNC: - /* IFH_REW_OP_ONE_STEP_PTP updates the correctional field, we - * need to update the origin time. - */ - ocelot_port->ptp_cmd = IFH_REW_OP_ORIGIN_PTP; - break; - case HWTSTAMP_TX_OFF: - ocelot_port->ptp_cmd = 0; - break; - default: - return -ERANGE; - } - - mutex_lock(&ocelot->ptp_lock); - - switch (cfg.rx_filter) { - case HWTSTAMP_FILTER_NONE: - break; - case HWTSTAMP_FILTER_PTP_V2_L4_EVENT: - case HWTSTAMP_FILTER_PTP_V2_L4_SYNC: - case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ: - l4 = true; - break; - case HWTSTAMP_FILTER_PTP_V2_L2_EVENT: - case HWTSTAMP_FILTER_PTP_V2_L2_SYNC: - case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ: - l2 = true; - break; - case HWTSTAMP_FILTER_PTP_V2_EVENT: - case HWTSTAMP_FILTER_PTP_V2_SYNC: - case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ: - l2 = true; - l4 = true; - break; - default: - mutex_unlock(&ocelot->ptp_lock); - return -ERANGE; - } - - err = ocelot_setup_ptp_traps(ocelot, port, l2, l4); - if (err) { - mutex_unlock(&ocelot->ptp_lock); - return err; - } - - if (l2 && l4) - cfg.rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT; - else if (l2) - cfg.rx_filter = HWTSTAMP_FILTER_PTP_V2_L2_EVENT; - else if (l4) - cfg.rx_filter = HWTSTAMP_FILTER_PTP_V2_L4_EVENT; - else - cfg.rx_filter = HWTSTAMP_FILTER_NONE; - - /* Commit back the result & save it */ - memcpy(&ocelot->hwtstamp_config, &cfg, sizeof(cfg)); - mutex_unlock(&ocelot->ptp_lock); - - return copy_to_user(ifr->ifr_data, &cfg, sizeof(cfg)) ? -EFAULT : 0; -} -EXPORT_SYMBOL(ocelot_hwstamp_set); - -void ocelot_get_strings(struct ocelot *ocelot, int port, u32 sset, u8 *data) -{ - int i; - - if (sset != ETH_SS_STATS) - return; - - for (i = 0; i < OCELOT_NUM_STATS; i++) { - if (ocelot->stats_layout[i].name[0] == '\0') - continue; - - memcpy(data + i * ETH_GSTRING_LEN, ocelot->stats_layout[i].name, - ETH_GSTRING_LEN); - } -} -EXPORT_SYMBOL(ocelot_get_strings); - -/* Caller must hold &ocelot->stats_lock */ -static int ocelot_port_update_stats(struct ocelot *ocelot, int port) -{ - unsigned int idx = port * OCELOT_NUM_STATS; - struct ocelot_stats_region *region; - int err, j; - - /* Configure the port to read the stats from */ - ocelot_write(ocelot, SYS_STAT_CFG_STAT_VIEW(port), SYS_STAT_CFG); - - list_for_each_entry(region, &ocelot->stats_regions, node) { - err = ocelot_bulk_read(ocelot, region->base, region->buf, - region->count); - if (err) - return err; - - for (j = 0; j < region->count; j++) { - u64 *stat = &ocelot->stats[idx + j]; - u64 val = region->buf[j]; - - if (val < (*stat & U32_MAX)) - *stat += (u64)1 << 32; - - *stat = (*stat & ~(u64)U32_MAX) + val; - } - - idx += region->count; - } - - return err; -} - -static void ocelot_check_stats_work(struct work_struct *work) -{ - struct delayed_work *del_work = to_delayed_work(work); - struct ocelot *ocelot = container_of(del_work, struct ocelot, - stats_work); - int i, err; - - spin_lock(&ocelot->stats_lock); - for (i = 0; i < ocelot->num_phys_ports; i++) { - err = ocelot_port_update_stats(ocelot, i); - if (err) - break; - } - spin_unlock(&ocelot->stats_lock); - - if (err) - dev_err(ocelot->dev, "Error %d updating ethtool stats\n", err); - - queue_delayed_work(ocelot->stats_queue, &ocelot->stats_work, - OCELOT_STATS_CHECK_DELAY); -} - -void ocelot_get_ethtool_stats(struct ocelot *ocelot, int port, u64 *data) -{ - int i, err; - - spin_lock(&ocelot->stats_lock); - - /* check and update now */ - err = ocelot_port_update_stats(ocelot, port); - - /* Copy all supported counters */ - for (i = 0; i < OCELOT_NUM_STATS; i++) { - int index = port * OCELOT_NUM_STATS + i; - - if (ocelot->stats_layout[i].name[0] == '\0') - continue; - - *data++ = ocelot->stats[index]; - } - - spin_unlock(&ocelot->stats_lock); - - if (err) - dev_err(ocelot->dev, "Error %d updating ethtool stats\n", err); -} -EXPORT_SYMBOL(ocelot_get_ethtool_stats); - -int ocelot_get_sset_count(struct ocelot *ocelot, int port, int sset) -{ - int i, num_stats = 0; - - if (sset != ETH_SS_STATS) - return -EOPNOTSUPP; - - for (i = 0; i < OCELOT_NUM_STATS; i++) - if (ocelot->stats_layout[i].name[0] != '\0') - num_stats++; - - return num_stats; -} -EXPORT_SYMBOL(ocelot_get_sset_count); - -static int ocelot_prepare_stats_regions(struct ocelot *ocelot) -{ - struct ocelot_stats_region *region = NULL; - unsigned int last; - int i; - - INIT_LIST_HEAD(&ocelot->stats_regions); - - for (i = 0; i < OCELOT_NUM_STATS; i++) { - if (ocelot->stats_layout[i].name[0] == '\0') - continue; - - if (region && ocelot->stats_layout[i].reg == last + 4) { - region->count++; - } else { - region = devm_kzalloc(ocelot->dev, sizeof(*region), - GFP_KERNEL); - if (!region) - return -ENOMEM; - - region->base = ocelot->stats_layout[i].reg; - region->count = 1; - list_add_tail(®ion->node, &ocelot->stats_regions); - } - - last = ocelot->stats_layout[i].reg; - } - - list_for_each_entry(region, &ocelot->stats_regions, node) { - region->buf = devm_kcalloc(ocelot->dev, region->count, - sizeof(*region->buf), GFP_KERNEL); - if (!region->buf) - return -ENOMEM; - } - - return 0; -} - -int ocelot_get_ts_info(struct ocelot *ocelot, int port, - struct ethtool_ts_info *info) -{ - info->phc_index = ocelot->ptp_clock ? - ptp_clock_index(ocelot->ptp_clock) : -1; - if (info->phc_index == -1) { - info->so_timestamping |= SOF_TIMESTAMPING_TX_SOFTWARE | - SOF_TIMESTAMPING_RX_SOFTWARE | - SOF_TIMESTAMPING_SOFTWARE; - return 0; - } - info->so_timestamping |= SOF_TIMESTAMPING_TX_SOFTWARE | - SOF_TIMESTAMPING_RX_SOFTWARE | - SOF_TIMESTAMPING_SOFTWARE | - SOF_TIMESTAMPING_TX_HARDWARE | - SOF_TIMESTAMPING_RX_HARDWARE | - SOF_TIMESTAMPING_RAW_HARDWARE; - info->tx_types = BIT(HWTSTAMP_TX_OFF) | BIT(HWTSTAMP_TX_ON) | - BIT(HWTSTAMP_TX_ONESTEP_SYNC); - info->rx_filters = BIT(HWTSTAMP_FILTER_NONE) | - BIT(HWTSTAMP_FILTER_PTP_V2_EVENT) | - BIT(HWTSTAMP_FILTER_PTP_V2_L2_EVENT) | - BIT(HWTSTAMP_FILTER_PTP_V2_L4_EVENT); - - return 0; -} -EXPORT_SYMBOL(ocelot_get_ts_info); - static u32 ocelot_get_bond_mask(struct ocelot *ocelot, struct net_device *bond) { u32 mask = 0; @@ -2061,7 +1389,7 @@ static u32 ocelot_get_bond_mask(struct ocelot *ocelot, struct net_device *bond) /* The logical port number of a LAG is equal to the lowest numbered physical * port ID present in that LAG. It may change if that port ever leaves the LAG. */ -static int ocelot_bond_get_id(struct ocelot *ocelot, struct net_device *bond) +int ocelot_bond_get_id(struct ocelot *ocelot, struct net_device *bond) { int bond_mask = ocelot_get_bond_mask(ocelot, bond); @@ -2070,7 +1398,18 @@ static int ocelot_bond_get_id(struct ocelot *ocelot, struct net_device *bond) return __ffs(bond_mask); } +EXPORT_SYMBOL_GPL(ocelot_bond_get_id); +/* Returns the mask of user ports assigned to this DSA tag_8021q CPU port. + * Note that when CPU ports are in a LAG, the user ports are assigned to the + * 'primary' CPU port, the one whose physical port number gives the logical + * port number of the LAG. + * + * We leave PGID_SRC poorly configured for the 'secondary' CPU port in the LAG + * (to which no user port is assigned), but it appears that forwarding from + * this secondary CPU port looks at the PGID_SRC associated with the logical + * port ID that it's assigned to, which *is* configured properly. + */ static u32 ocelot_dsa_8021q_cpu_assigned_ports(struct ocelot *ocelot, struct ocelot_port *cpu) { @@ -2087,9 +1426,15 @@ static u32 ocelot_dsa_8021q_cpu_assigned_ports(struct ocelot *ocelot, mask |= BIT(port); } + if (cpu->bond) + mask &= ~ocelot_get_bond_mask(ocelot, cpu->bond); + return mask; } +/* Returns the DSA tag_8021q CPU port that the given port is assigned to, + * or the bit mask of CPU ports if said CPU port is in a LAG. + */ u32 ocelot_port_assigned_dsa_8021q_cpu_mask(struct ocelot *ocelot, int port) { struct ocelot_port *ocelot_port = ocelot->ports[port]; @@ -2098,6 +1443,9 @@ u32 ocelot_port_assigned_dsa_8021q_cpu_mask(struct ocelot *ocelot, int port) if (!cpu_port) return 0; + if (cpu_port->bond) + return ocelot_get_bond_mask(ocelot, cpu_port->bond); + return BIT(cpu_port->index); } EXPORT_SYMBOL_GPL(ocelot_port_assigned_dsa_8021q_cpu_mask); @@ -2221,61 +1569,61 @@ static void ocelot_update_pgid_cpu(struct ocelot *ocelot) ocelot_write_rix(ocelot, pgid_cpu, ANA_PGID_PGID, PGID_CPU); } -void ocelot_port_assign_dsa_8021q_cpu(struct ocelot *ocelot, int port, - int cpu) +void ocelot_port_setup_dsa_8021q_cpu(struct ocelot *ocelot, int cpu) { struct ocelot_port *cpu_port = ocelot->ports[cpu]; u16 vid; mutex_lock(&ocelot->fwd_domain_lock); - ocelot->ports[port]->dsa_8021q_cpu = cpu_port; - - if (!cpu_port->is_dsa_8021q_cpu) { - cpu_port->is_dsa_8021q_cpu = true; + cpu_port->is_dsa_8021q_cpu = true; - for (vid = OCELOT_RSV_VLAN_RANGE_START; vid < VLAN_N_VID; vid++) - ocelot_vlan_member_add(ocelot, cpu, vid, true); + for (vid = OCELOT_RSV_VLAN_RANGE_START; vid < VLAN_N_VID; vid++) + ocelot_vlan_member_add(ocelot, cpu, vid, true); - ocelot_update_pgid_cpu(ocelot); - } - - ocelot_apply_bridge_fwd_mask(ocelot, true); + ocelot_update_pgid_cpu(ocelot); mutex_unlock(&ocelot->fwd_domain_lock); } -EXPORT_SYMBOL_GPL(ocelot_port_assign_dsa_8021q_cpu); +EXPORT_SYMBOL_GPL(ocelot_port_setup_dsa_8021q_cpu); -void ocelot_port_unassign_dsa_8021q_cpu(struct ocelot *ocelot, int port) +void ocelot_port_teardown_dsa_8021q_cpu(struct ocelot *ocelot, int cpu) { - struct ocelot_port *cpu_port = ocelot->ports[port]->dsa_8021q_cpu; - bool keep = false; + struct ocelot_port *cpu_port = ocelot->ports[cpu]; u16 vid; - int p; mutex_lock(&ocelot->fwd_domain_lock); - ocelot->ports[port]->dsa_8021q_cpu = NULL; + cpu_port->is_dsa_8021q_cpu = false; - for (p = 0; p < ocelot->num_phys_ports; p++) { - if (!ocelot->ports[p]) - continue; + for (vid = OCELOT_RSV_VLAN_RANGE_START; vid < VLAN_N_VID; vid++) + ocelot_vlan_member_del(ocelot, cpu_port->index, vid); - if (ocelot->ports[p]->dsa_8021q_cpu == cpu_port) { - keep = true; - break; - } - } + ocelot_update_pgid_cpu(ocelot); - if (!keep) { - cpu_port->is_dsa_8021q_cpu = false; + mutex_unlock(&ocelot->fwd_domain_lock); +} +EXPORT_SYMBOL_GPL(ocelot_port_teardown_dsa_8021q_cpu); - for (vid = OCELOT_RSV_VLAN_RANGE_START; vid < VLAN_N_VID; vid++) - ocelot_vlan_member_del(ocelot, cpu_port->index, vid); +void ocelot_port_assign_dsa_8021q_cpu(struct ocelot *ocelot, int port, + int cpu) +{ + struct ocelot_port *cpu_port = ocelot->ports[cpu]; - ocelot_update_pgid_cpu(ocelot); - } + mutex_lock(&ocelot->fwd_domain_lock); + + ocelot->ports[port]->dsa_8021q_cpu = cpu_port; + ocelot_apply_bridge_fwd_mask(ocelot, true); + + mutex_unlock(&ocelot->fwd_domain_lock); +} +EXPORT_SYMBOL_GPL(ocelot_port_assign_dsa_8021q_cpu); + +void ocelot_port_unassign_dsa_8021q_cpu(struct ocelot *ocelot, int port) +{ + mutex_lock(&ocelot->fwd_domain_lock); + ocelot->ports[port]->dsa_8021q_cpu = NULL; ocelot_apply_bridge_fwd_mask(ocelot, true); mutex_unlock(&ocelot->fwd_domain_lock); @@ -2792,10 +2140,14 @@ static void ocelot_migrate_lag_fdbs(struct ocelot *ocelot, int ocelot_port_lag_join(struct ocelot *ocelot, int port, struct net_device *bond, - struct netdev_lag_upper_info *info) + struct netdev_lag_upper_info *info, + struct netlink_ext_ack *extack) { - if (info->tx_type != NETDEV_LAG_TX_TYPE_HASH) + if (info->tx_type != NETDEV_LAG_TX_TYPE_HASH) { + NL_SET_ERR_MSG_MOD(extack, + "Can only offload LAG using hash TX type"); return -EOPNOTSUPP; + } mutex_lock(&ocelot->fwd_domain_lock); @@ -3365,7 +2717,6 @@ static void ocelot_detect_features(struct ocelot *ocelot) int ocelot_init(struct ocelot *ocelot) { - char queue_name[32]; int i, ret; u32 port; @@ -3377,29 +2728,21 @@ int ocelot_init(struct ocelot *ocelot) } } - ocelot->stats = devm_kcalloc(ocelot->dev, - ocelot->num_phys_ports * OCELOT_NUM_STATS, - sizeof(u64), GFP_KERNEL); - if (!ocelot->stats) - return -ENOMEM; - - spin_lock_init(&ocelot->stats_lock); mutex_init(&ocelot->ptp_lock); mutex_init(&ocelot->mact_lock); mutex_init(&ocelot->fwd_domain_lock); mutex_init(&ocelot->tas_lock); spin_lock_init(&ocelot->ptp_clock_lock); spin_lock_init(&ocelot->ts_id_lock); - snprintf(queue_name, sizeof(queue_name), "%s-stats", - dev_name(ocelot->dev)); - ocelot->stats_queue = create_singlethread_workqueue(queue_name); - if (!ocelot->stats_queue) - return -ENOMEM; ocelot->owq = alloc_ordered_workqueue("ocelot-owq", 0); - if (!ocelot->owq) { - destroy_workqueue(ocelot->stats_queue); + if (!ocelot->owq) return -ENOMEM; + + ret = ocelot_stats_init(ocelot); + if (ret) { + destroy_workqueue(ocelot->owq); + return ret; } INIT_LIST_HEAD(&ocelot->multicast); @@ -3511,25 +2854,13 @@ int ocelot_init(struct ocelot *ocelot) ANA_CPUQ_8021_CFG_CPUQ_BPDU_VAL(6), ANA_CPUQ_8021_CFG, i); - ret = ocelot_prepare_stats_regions(ocelot); - if (ret) { - destroy_workqueue(ocelot->stats_queue); - destroy_workqueue(ocelot->owq); - return ret; - } - - INIT_DELAYED_WORK(&ocelot->stats_work, ocelot_check_stats_work); - queue_delayed_work(ocelot->stats_queue, &ocelot->stats_work, - OCELOT_STATS_CHECK_DELAY); - return 0; } EXPORT_SYMBOL(ocelot_init); void ocelot_deinit(struct ocelot *ocelot) { - cancel_delayed_work(&ocelot->stats_work); - destroy_workqueue(ocelot->stats_queue); + ocelot_stats_deinit(ocelot); destroy_workqueue(ocelot->owq); } EXPORT_SYMBOL(ocelot_deinit); diff --git a/drivers/net/ethernet/mscc/ocelot.h b/drivers/net/ethernet/mscc/ocelot.h index 6d65cc87d757..70dbd9c4e512 100644 --- a/drivers/net/ethernet/mscc/ocelot.h +++ b/drivers/net/ethernet/mscc/ocelot.h @@ -51,13 +51,6 @@ struct ocelot_port_private { struct ocelot_port_tc tc; }; -struct ocelot_dump_ctx { - struct net_device *dev; - struct sk_buff *skb; - struct netlink_callback *cb; - int idx; -}; - /* A (PGID) port mask structure, encoding the 2^ocelot->num_phys_ports * possibilities of egress port masks for L2 multicast traffic. * For a switch with 9 user ports, there are 512 possible port masks, but the @@ -84,8 +77,6 @@ struct ocelot_multicast { int ocelot_bridge_num_find(struct ocelot *ocelot, const struct net_device *bridge); -int ocelot_port_fdb_do_dump(const unsigned char *addr, u16 vid, - bool is_static, void *data); int ocelot_mact_learn(struct ocelot *ocelot, int port, const unsigned char mac[ETH_ALEN], unsigned int vid, enum macaccess_entry_type type); @@ -115,6 +106,9 @@ struct ocelot_mirror *ocelot_mirror_get(struct ocelot *ocelot, int to, struct netlink_ext_ack *extack); void ocelot_mirror_put(struct ocelot *ocelot); +int ocelot_stats_init(struct ocelot *ocelot); +void ocelot_stats_deinit(struct ocelot *ocelot); + extern struct notifier_block ocelot_netdevice_nb; extern struct notifier_block ocelot_switchdev_nb; extern struct notifier_block ocelot_switchdev_blocking_nb; diff --git a/drivers/net/ethernet/mscc/ocelot_net.c b/drivers/net/ethernet/mscc/ocelot_net.c index 330d30841cdc..50858cc10fef 100644 --- a/drivers/net/ethernet/mscc/ocelot_net.c +++ b/drivers/net/ethernet/mscc/ocelot_net.c @@ -20,6 +20,13 @@ #define OCELOT_MAC_QUIRKS OCELOT_QUIRK_QSGMII_PORTS_MUST_BE_UP +struct ocelot_dump_ctx { + struct net_device *dev; + struct sk_buff *skb; + struct netlink_callback *cb; + int idx; +}; + static bool ocelot_netdevice_dev_check(const struct net_device *dev); static struct ocelot *devlink_port_to_ocelot(struct devlink_port *dlp) @@ -725,42 +732,8 @@ static void ocelot_get_stats64(struct net_device *dev, struct ocelot_port_private *priv = netdev_priv(dev); struct ocelot *ocelot = priv->port.ocelot; int port = priv->port.index; - u64 *s; - - spin_lock(&ocelot->stats_lock); - - s = &ocelot->stats[port * OCELOT_NUM_STATS]; - - /* Get Rx stats */ - stats->rx_bytes = s[OCELOT_STAT_RX_OCTETS]; - stats->rx_packets = s[OCELOT_STAT_RX_SHORTS] + - s[OCELOT_STAT_RX_FRAGMENTS] + - s[OCELOT_STAT_RX_JABBERS] + - s[OCELOT_STAT_RX_LONGS] + - s[OCELOT_STAT_RX_64] + - s[OCELOT_STAT_RX_65_127] + - s[OCELOT_STAT_RX_128_255] + - s[OCELOT_STAT_RX_256_511] + - s[OCELOT_STAT_RX_512_1023] + - s[OCELOT_STAT_RX_1024_1526] + - s[OCELOT_STAT_RX_1527_MAX]; - stats->multicast = s[OCELOT_STAT_RX_MULTICAST]; - stats->rx_dropped = dev->stats.rx_dropped; - - /* Get Tx stats */ - stats->tx_bytes = s[OCELOT_STAT_TX_OCTETS]; - stats->tx_packets = s[OCELOT_STAT_TX_64] + - s[OCELOT_STAT_TX_65_127] + - s[OCELOT_STAT_TX_128_255] + - s[OCELOT_STAT_TX_256_511] + - s[OCELOT_STAT_TX_512_1023] + - s[OCELOT_STAT_TX_1024_1526] + - s[OCELOT_STAT_TX_1527_MAX]; - stats->tx_dropped = s[OCELOT_STAT_TX_DROPS] + - s[OCELOT_STAT_TX_AGED]; - stats->collisions = s[OCELOT_STAT_TX_COLLISION]; - - spin_unlock(&ocelot->stats_lock); + + return ocelot_port_get_stats64(ocelot, port, stats); } static int ocelot_port_fdb_add(struct ndmsg *ndm, struct nlattr *tb[], @@ -790,6 +763,49 @@ static int ocelot_port_fdb_del(struct ndmsg *ndm, struct nlattr *tb[], return ocelot_fdb_del(ocelot, port, addr, vid, ocelot_port->bridge); } +static int ocelot_port_fdb_do_dump(const unsigned char *addr, u16 vid, + bool is_static, void *data) +{ + struct ocelot_dump_ctx *dump = data; + u32 portid = NETLINK_CB(dump->cb->skb).portid; + u32 seq = dump->cb->nlh->nlmsg_seq; + struct nlmsghdr *nlh; + struct ndmsg *ndm; + + if (dump->idx < dump->cb->args[2]) + goto skip; + + nlh = nlmsg_put(dump->skb, portid, seq, RTM_NEWNEIGH, + sizeof(*ndm), NLM_F_MULTI); + if (!nlh) + return -EMSGSIZE; + + ndm = nlmsg_data(nlh); + ndm->ndm_family = AF_BRIDGE; + ndm->ndm_pad1 = 0; + ndm->ndm_pad2 = 0; + ndm->ndm_flags = NTF_SELF; + ndm->ndm_type = 0; + ndm->ndm_ifindex = dump->dev->ifindex; + ndm->ndm_state = is_static ? NUD_NOARP : NUD_REACHABLE; + + if (nla_put(dump->skb, NDA_LLADDR, ETH_ALEN, addr)) + goto nla_put_failure; + + if (vid && nla_put_u16(dump->skb, NDA_VLAN, vid)) + goto nla_put_failure; + + nlmsg_end(dump->skb, nlh); + +skip: + dump->idx++; + return 0; + +nla_put_failure: + nlmsg_cancel(dump->skb, nlh); + return -EMSGSIZE; +} + static int ocelot_port_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb, struct net_device *dev, @@ -1396,11 +1412,10 @@ static int ocelot_netdevice_lag_join(struct net_device *dev, int port = priv->port.index; int err; - err = ocelot_port_lag_join(ocelot, port, bond, info); - if (err == -EOPNOTSUPP) { - NL_SET_ERR_MSG_MOD(extack, "Offloading not supported"); + err = ocelot_port_lag_join(ocelot, port, bond, info, extack); + if (err == -EOPNOTSUPP) + /* Offloading not supported, fall back to software LAG */ return 0; - } bridge_dev = netdev_master_upper_dev_get(bond); if (!bridge_dev || !netif_is_bridge_master(bridge_dev)) diff --git a/drivers/net/ethernet/mscc/ocelot_ptp.c b/drivers/net/ethernet/mscc/ocelot_ptp.c index 09c703efe946..1a82f10c8853 100644 --- a/drivers/net/ethernet/mscc/ocelot_ptp.c +++ b/drivers/net/ethernet/mscc/ocelot_ptp.c @@ -6,9 +6,13 @@ */ #include <linux/time64.h> +#include <linux/dsa/ocelot.h> +#include <linux/ptp_classify.h> #include <soc/mscc/ocelot_ptp.h> #include <soc/mscc/ocelot_sys.h> +#include <soc/mscc/ocelot_vcap.h> #include <soc/mscc/ocelot.h> +#include "ocelot.h" int ocelot_ptp_gettime64(struct ptp_clock_info *ptp, struct timespec64 *ts) { @@ -310,6 +314,483 @@ int ocelot_ptp_enable(struct ptp_clock_info *ptp, } EXPORT_SYMBOL(ocelot_ptp_enable); +static void ocelot_populate_l2_ptp_trap_key(struct ocelot_vcap_filter *trap) +{ + trap->key_type = OCELOT_VCAP_KEY_ETYPE; + *(__be16 *)trap->key.etype.etype.value = htons(ETH_P_1588); + *(__be16 *)trap->key.etype.etype.mask = htons(0xffff); +} + +static void +ocelot_populate_ipv4_ptp_event_trap_key(struct ocelot_vcap_filter *trap) +{ + trap->key_type = OCELOT_VCAP_KEY_IPV4; + trap->key.ipv4.proto.value[0] = IPPROTO_UDP; + trap->key.ipv4.proto.mask[0] = 0xff; + trap->key.ipv4.dport.value = PTP_EV_PORT; + trap->key.ipv4.dport.mask = 0xffff; +} + +static void +ocelot_populate_ipv6_ptp_event_trap_key(struct ocelot_vcap_filter *trap) +{ + trap->key_type = OCELOT_VCAP_KEY_IPV6; + trap->key.ipv4.proto.value[0] = IPPROTO_UDP; + trap->key.ipv4.proto.mask[0] = 0xff; + trap->key.ipv6.dport.value = PTP_EV_PORT; + trap->key.ipv6.dport.mask = 0xffff; +} + +static void +ocelot_populate_ipv4_ptp_general_trap_key(struct ocelot_vcap_filter *trap) +{ + trap->key_type = OCELOT_VCAP_KEY_IPV4; + trap->key.ipv4.proto.value[0] = IPPROTO_UDP; + trap->key.ipv4.proto.mask[0] = 0xff; + trap->key.ipv4.dport.value = PTP_GEN_PORT; + trap->key.ipv4.dport.mask = 0xffff; +} + +static void +ocelot_populate_ipv6_ptp_general_trap_key(struct ocelot_vcap_filter *trap) +{ + trap->key_type = OCELOT_VCAP_KEY_IPV6; + trap->key.ipv4.proto.value[0] = IPPROTO_UDP; + trap->key.ipv4.proto.mask[0] = 0xff; + trap->key.ipv6.dport.value = PTP_GEN_PORT; + trap->key.ipv6.dport.mask = 0xffff; +} + +static int ocelot_l2_ptp_trap_add(struct ocelot *ocelot, int port) +{ + unsigned long l2_cookie = OCELOT_VCAP_IS2_L2_PTP_TRAP(ocelot); + + return ocelot_trap_add(ocelot, port, l2_cookie, true, + ocelot_populate_l2_ptp_trap_key); +} + +static int ocelot_l2_ptp_trap_del(struct ocelot *ocelot, int port) +{ + unsigned long l2_cookie = OCELOT_VCAP_IS2_L2_PTP_TRAP(ocelot); + + return ocelot_trap_del(ocelot, port, l2_cookie); +} + +static int ocelot_ipv4_ptp_trap_add(struct ocelot *ocelot, int port) +{ + unsigned long ipv4_gen_cookie = OCELOT_VCAP_IS2_IPV4_GEN_PTP_TRAP(ocelot); + unsigned long ipv4_ev_cookie = OCELOT_VCAP_IS2_IPV4_EV_PTP_TRAP(ocelot); + int err; + + err = ocelot_trap_add(ocelot, port, ipv4_ev_cookie, true, + ocelot_populate_ipv4_ptp_event_trap_key); + if (err) + return err; + + err = ocelot_trap_add(ocelot, port, ipv4_gen_cookie, false, + ocelot_populate_ipv4_ptp_general_trap_key); + if (err) + ocelot_trap_del(ocelot, port, ipv4_ev_cookie); + + return err; +} + +static int ocelot_ipv4_ptp_trap_del(struct ocelot *ocelot, int port) +{ + unsigned long ipv4_gen_cookie = OCELOT_VCAP_IS2_IPV4_GEN_PTP_TRAP(ocelot); + unsigned long ipv4_ev_cookie = OCELOT_VCAP_IS2_IPV4_EV_PTP_TRAP(ocelot); + int err; + + err = ocelot_trap_del(ocelot, port, ipv4_ev_cookie); + err |= ocelot_trap_del(ocelot, port, ipv4_gen_cookie); + return err; +} + +static int ocelot_ipv6_ptp_trap_add(struct ocelot *ocelot, int port) +{ + unsigned long ipv6_gen_cookie = OCELOT_VCAP_IS2_IPV6_GEN_PTP_TRAP(ocelot); + unsigned long ipv6_ev_cookie = OCELOT_VCAP_IS2_IPV6_EV_PTP_TRAP(ocelot); + int err; + + err = ocelot_trap_add(ocelot, port, ipv6_ev_cookie, true, + ocelot_populate_ipv6_ptp_event_trap_key); + if (err) + return err; + + err = ocelot_trap_add(ocelot, port, ipv6_gen_cookie, false, + ocelot_populate_ipv6_ptp_general_trap_key); + if (err) + ocelot_trap_del(ocelot, port, ipv6_ev_cookie); + + return err; +} + +static int ocelot_ipv6_ptp_trap_del(struct ocelot *ocelot, int port) +{ + unsigned long ipv6_gen_cookie = OCELOT_VCAP_IS2_IPV6_GEN_PTP_TRAP(ocelot); + unsigned long ipv6_ev_cookie = OCELOT_VCAP_IS2_IPV6_EV_PTP_TRAP(ocelot); + int err; + + err = ocelot_trap_del(ocelot, port, ipv6_ev_cookie); + err |= ocelot_trap_del(ocelot, port, ipv6_gen_cookie); + return err; +} + +static int ocelot_setup_ptp_traps(struct ocelot *ocelot, int port, + bool l2, bool l4) +{ + int err; + + if (l2) + err = ocelot_l2_ptp_trap_add(ocelot, port); + else + err = ocelot_l2_ptp_trap_del(ocelot, port); + if (err) + return err; + + if (l4) { + err = ocelot_ipv4_ptp_trap_add(ocelot, port); + if (err) + goto err_ipv4; + + err = ocelot_ipv6_ptp_trap_add(ocelot, port); + if (err) + goto err_ipv6; + } else { + err = ocelot_ipv4_ptp_trap_del(ocelot, port); + + err |= ocelot_ipv6_ptp_trap_del(ocelot, port); + } + if (err) + return err; + + return 0; + +err_ipv6: + ocelot_ipv4_ptp_trap_del(ocelot, port); +err_ipv4: + if (l2) + ocelot_l2_ptp_trap_del(ocelot, port); + return err; +} + +int ocelot_hwstamp_get(struct ocelot *ocelot, int port, struct ifreq *ifr) +{ + return copy_to_user(ifr->ifr_data, &ocelot->hwtstamp_config, + sizeof(ocelot->hwtstamp_config)) ? -EFAULT : 0; +} +EXPORT_SYMBOL(ocelot_hwstamp_get); + +int ocelot_hwstamp_set(struct ocelot *ocelot, int port, struct ifreq *ifr) +{ + struct ocelot_port *ocelot_port = ocelot->ports[port]; + bool l2 = false, l4 = false; + struct hwtstamp_config cfg; + int err; + + if (copy_from_user(&cfg, ifr->ifr_data, sizeof(cfg))) + return -EFAULT; + + /* Tx type sanity check */ + switch (cfg.tx_type) { + case HWTSTAMP_TX_ON: + ocelot_port->ptp_cmd = IFH_REW_OP_TWO_STEP_PTP; + break; + case HWTSTAMP_TX_ONESTEP_SYNC: + /* IFH_REW_OP_ONE_STEP_PTP updates the correctional field, we + * need to update the origin time. + */ + ocelot_port->ptp_cmd = IFH_REW_OP_ORIGIN_PTP; + break; + case HWTSTAMP_TX_OFF: + ocelot_port->ptp_cmd = 0; + break; + default: + return -ERANGE; + } + + mutex_lock(&ocelot->ptp_lock); + + switch (cfg.rx_filter) { + case HWTSTAMP_FILTER_NONE: + break; + case HWTSTAMP_FILTER_PTP_V2_L4_EVENT: + case HWTSTAMP_FILTER_PTP_V2_L4_SYNC: + case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ: + l4 = true; + break; + case HWTSTAMP_FILTER_PTP_V2_L2_EVENT: + case HWTSTAMP_FILTER_PTP_V2_L2_SYNC: + case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ: + l2 = true; + break; + case HWTSTAMP_FILTER_PTP_V2_EVENT: + case HWTSTAMP_FILTER_PTP_V2_SYNC: + case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ: + l2 = true; + l4 = true; + break; + default: + mutex_unlock(&ocelot->ptp_lock); + return -ERANGE; + } + + err = ocelot_setup_ptp_traps(ocelot, port, l2, l4); + if (err) { + mutex_unlock(&ocelot->ptp_lock); + return err; + } + + if (l2 && l4) + cfg.rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT; + else if (l2) + cfg.rx_filter = HWTSTAMP_FILTER_PTP_V2_L2_EVENT; + else if (l4) + cfg.rx_filter = HWTSTAMP_FILTER_PTP_V2_L4_EVENT; + else + cfg.rx_filter = HWTSTAMP_FILTER_NONE; + + /* Commit back the result & save it */ + memcpy(&ocelot->hwtstamp_config, &cfg, sizeof(cfg)); + mutex_unlock(&ocelot->ptp_lock); + + return copy_to_user(ifr->ifr_data, &cfg, sizeof(cfg)) ? -EFAULT : 0; +} +EXPORT_SYMBOL(ocelot_hwstamp_set); + +int ocelot_get_ts_info(struct ocelot *ocelot, int port, + struct ethtool_ts_info *info) +{ + info->phc_index = ocelot->ptp_clock ? + ptp_clock_index(ocelot->ptp_clock) : -1; + if (info->phc_index == -1) { + info->so_timestamping |= SOF_TIMESTAMPING_TX_SOFTWARE | + SOF_TIMESTAMPING_RX_SOFTWARE | + SOF_TIMESTAMPING_SOFTWARE; + return 0; + } + info->so_timestamping |= SOF_TIMESTAMPING_TX_SOFTWARE | + SOF_TIMESTAMPING_RX_SOFTWARE | + SOF_TIMESTAMPING_SOFTWARE | + SOF_TIMESTAMPING_TX_HARDWARE | + SOF_TIMESTAMPING_RX_HARDWARE | + SOF_TIMESTAMPING_RAW_HARDWARE; + info->tx_types = BIT(HWTSTAMP_TX_OFF) | BIT(HWTSTAMP_TX_ON) | + BIT(HWTSTAMP_TX_ONESTEP_SYNC); + info->rx_filters = BIT(HWTSTAMP_FILTER_NONE) | + BIT(HWTSTAMP_FILTER_PTP_V2_EVENT) | + BIT(HWTSTAMP_FILTER_PTP_V2_L2_EVENT) | + BIT(HWTSTAMP_FILTER_PTP_V2_L4_EVENT); + + return 0; +} +EXPORT_SYMBOL(ocelot_get_ts_info); + +static int ocelot_port_add_txtstamp_skb(struct ocelot *ocelot, int port, + struct sk_buff *clone) +{ + struct ocelot_port *ocelot_port = ocelot->ports[port]; + unsigned long flags; + + spin_lock_irqsave(&ocelot->ts_id_lock, flags); + + if (ocelot_port->ptp_skbs_in_flight == OCELOT_MAX_PTP_ID || + ocelot->ptp_skbs_in_flight == OCELOT_PTP_FIFO_SIZE) { + spin_unlock_irqrestore(&ocelot->ts_id_lock, flags); + return -EBUSY; + } + + skb_shinfo(clone)->tx_flags |= SKBTX_IN_PROGRESS; + /* Store timestamp ID in OCELOT_SKB_CB(clone)->ts_id */ + OCELOT_SKB_CB(clone)->ts_id = ocelot_port->ts_id; + + ocelot_port->ts_id++; + if (ocelot_port->ts_id == OCELOT_MAX_PTP_ID) + ocelot_port->ts_id = 0; + + ocelot_port->ptp_skbs_in_flight++; + ocelot->ptp_skbs_in_flight++; + + skb_queue_tail(&ocelot_port->tx_skbs, clone); + + spin_unlock_irqrestore(&ocelot->ts_id_lock, flags); + + return 0; +} + +static bool ocelot_ptp_is_onestep_sync(struct sk_buff *skb, + unsigned int ptp_class) +{ + struct ptp_header *hdr; + u8 msgtype, twostep; + + hdr = ptp_parse_header(skb, ptp_class); + if (!hdr) + return false; + + msgtype = ptp_get_msgtype(hdr, ptp_class); + twostep = hdr->flag_field[0] & 0x2; + + if (msgtype == PTP_MSGTYPE_SYNC && twostep == 0) + return true; + + return false; +} + +int ocelot_port_txtstamp_request(struct ocelot *ocelot, int port, + struct sk_buff *skb, + struct sk_buff **clone) +{ + struct ocelot_port *ocelot_port = ocelot->ports[port]; + u8 ptp_cmd = ocelot_port->ptp_cmd; + unsigned int ptp_class; + int err; + + /* Don't do anything if PTP timestamping not enabled */ + if (!ptp_cmd) + return 0; + + ptp_class = ptp_classify_raw(skb); + if (ptp_class == PTP_CLASS_NONE) + return -EINVAL; + + /* Store ptp_cmd in OCELOT_SKB_CB(skb)->ptp_cmd */ + if (ptp_cmd == IFH_REW_OP_ORIGIN_PTP) { + if (ocelot_ptp_is_onestep_sync(skb, ptp_class)) { + OCELOT_SKB_CB(skb)->ptp_cmd = ptp_cmd; + return 0; + } + + /* Fall back to two-step timestamping */ + ptp_cmd = IFH_REW_OP_TWO_STEP_PTP; + } + + if (ptp_cmd == IFH_REW_OP_TWO_STEP_PTP) { + *clone = skb_clone_sk(skb); + if (!(*clone)) + return -ENOMEM; + + err = ocelot_port_add_txtstamp_skb(ocelot, port, *clone); + if (err) + return err; + + OCELOT_SKB_CB(skb)->ptp_cmd = ptp_cmd; + OCELOT_SKB_CB(*clone)->ptp_class = ptp_class; + } + + return 0; +} +EXPORT_SYMBOL(ocelot_port_txtstamp_request); + +static void ocelot_get_hwtimestamp(struct ocelot *ocelot, + struct timespec64 *ts) +{ + unsigned long flags; + u32 val; + + spin_lock_irqsave(&ocelot->ptp_clock_lock, flags); + + /* Read current PTP time to get seconds */ + val = ocelot_read_rix(ocelot, PTP_PIN_CFG, TOD_ACC_PIN); + + val &= ~(PTP_PIN_CFG_SYNC | PTP_PIN_CFG_ACTION_MASK | PTP_PIN_CFG_DOM); + val |= PTP_PIN_CFG_ACTION(PTP_PIN_ACTION_SAVE); + ocelot_write_rix(ocelot, val, PTP_PIN_CFG, TOD_ACC_PIN); + ts->tv_sec = ocelot_read_rix(ocelot, PTP_PIN_TOD_SEC_LSB, TOD_ACC_PIN); + + /* Read packet HW timestamp from FIFO */ + val = ocelot_read(ocelot, SYS_PTP_TXSTAMP); + ts->tv_nsec = SYS_PTP_TXSTAMP_PTP_TXSTAMP(val); + + /* Sec has incremented since the ts was registered */ + if ((ts->tv_sec & 0x1) != !!(val & SYS_PTP_TXSTAMP_PTP_TXSTAMP_SEC)) + ts->tv_sec--; + + spin_unlock_irqrestore(&ocelot->ptp_clock_lock, flags); +} + +static bool ocelot_validate_ptp_skb(struct sk_buff *clone, u16 seqid) +{ + struct ptp_header *hdr; + + hdr = ptp_parse_header(clone, OCELOT_SKB_CB(clone)->ptp_class); + if (WARN_ON(!hdr)) + return false; + + return seqid == ntohs(hdr->sequence_id); +} + +void ocelot_get_txtstamp(struct ocelot *ocelot) +{ + int budget = OCELOT_PTP_QUEUE_SZ; + + while (budget--) { + struct sk_buff *skb, *skb_tmp, *skb_match = NULL; + struct skb_shared_hwtstamps shhwtstamps; + u32 val, id, seqid, txport; + struct ocelot_port *port; + struct timespec64 ts; + unsigned long flags; + + val = ocelot_read(ocelot, SYS_PTP_STATUS); + + /* Check if a timestamp can be retrieved */ + if (!(val & SYS_PTP_STATUS_PTP_MESS_VLD)) + break; + + WARN_ON(val & SYS_PTP_STATUS_PTP_OVFL); + + /* Retrieve the ts ID and Tx port */ + id = SYS_PTP_STATUS_PTP_MESS_ID_X(val); + txport = SYS_PTP_STATUS_PTP_MESS_TXPORT_X(val); + seqid = SYS_PTP_STATUS_PTP_MESS_SEQ_ID(val); + + port = ocelot->ports[txport]; + + spin_lock(&ocelot->ts_id_lock); + port->ptp_skbs_in_flight--; + ocelot->ptp_skbs_in_flight--; + spin_unlock(&ocelot->ts_id_lock); + + /* Retrieve its associated skb */ +try_again: + spin_lock_irqsave(&port->tx_skbs.lock, flags); + + skb_queue_walk_safe(&port->tx_skbs, skb, skb_tmp) { + if (OCELOT_SKB_CB(skb)->ts_id != id) + continue; + __skb_unlink(skb, &port->tx_skbs); + skb_match = skb; + break; + } + + spin_unlock_irqrestore(&port->tx_skbs.lock, flags); + + if (WARN_ON(!skb_match)) + continue; + + if (!ocelot_validate_ptp_skb(skb_match, seqid)) { + dev_err_ratelimited(ocelot->dev, + "port %d received stale TX timestamp for seqid %d, discarding\n", + txport, seqid); + dev_kfree_skb_any(skb); + goto try_again; + } + + /* Get the h/w timestamp */ + ocelot_get_hwtimestamp(ocelot, &ts); + + /* Set the timestamp into the skb */ + memset(&shhwtstamps, 0, sizeof(shhwtstamps)); + shhwtstamps.hwtstamp = ktime_set(ts.tv_sec, ts.tv_nsec); + skb_complete_tx_timestamp(skb_match, &shhwtstamps); + + /* Next ts */ + ocelot_write(ocelot, SYS_PTP_NXT_PTP_NXT, SYS_PTP_NXT); + } +} +EXPORT_SYMBOL(ocelot_get_txtstamp); + int ocelot_init_timestamp(struct ocelot *ocelot, const struct ptp_clock_info *info) { diff --git a/drivers/net/ethernet/mscc/ocelot_stats.c b/drivers/net/ethernet/mscc/ocelot_stats.c new file mode 100644 index 000000000000..dbd20b125cea --- /dev/null +++ b/drivers/net/ethernet/mscc/ocelot_stats.c @@ -0,0 +1,458 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/* Statistics for Ocelot switch family + * + * Copyright (c) 2017 Microsemi Corporation + * Copyright 2022 NXP + */ +#include <linux/spinlock.h> +#include <linux/mutex.h> +#include <linux/workqueue.h> +#include "ocelot.h" + +/* Read the counters from hardware and keep them in region->buf. + * Caller must hold &ocelot->stat_view_lock. + */ +static int ocelot_port_update_stats(struct ocelot *ocelot, int port) +{ + struct ocelot_stats_region *region; + int err; + + /* Configure the port to read the stats from */ + ocelot_write(ocelot, SYS_STAT_CFG_STAT_VIEW(port), SYS_STAT_CFG); + + list_for_each_entry(region, &ocelot->stats_regions, node) { + err = ocelot_bulk_read(ocelot, region->base, region->buf, + region->count); + if (err) + return err; + } + + return 0; +} + +/* Transfer the counters from region->buf to ocelot->stats. + * Caller must hold &ocelot->stat_view_lock and &ocelot->stats_lock. + */ +static void ocelot_port_transfer_stats(struct ocelot *ocelot, int port) +{ + unsigned int idx = port * OCELOT_NUM_STATS; + struct ocelot_stats_region *region; + int j; + + list_for_each_entry(region, &ocelot->stats_regions, node) { + for (j = 0; j < region->count; j++) { + u64 *stat = &ocelot->stats[idx + j]; + u64 val = region->buf[j]; + + if (val < (*stat & U32_MAX)) + *stat += (u64)1 << 32; + + *stat = (*stat & ~(u64)U32_MAX) + val; + } + + idx += region->count; + } +} + +static void ocelot_check_stats_work(struct work_struct *work) +{ + struct delayed_work *del_work = to_delayed_work(work); + struct ocelot *ocelot = container_of(del_work, struct ocelot, + stats_work); + int port, err; + + mutex_lock(&ocelot->stat_view_lock); + + for (port = 0; port < ocelot->num_phys_ports; port++) { + err = ocelot_port_update_stats(ocelot, port); + if (err) + break; + + spin_lock(&ocelot->stats_lock); + ocelot_port_transfer_stats(ocelot, port); + spin_unlock(&ocelot->stats_lock); + } + + if (!err && ocelot->ops->update_stats) + ocelot->ops->update_stats(ocelot); + + mutex_unlock(&ocelot->stat_view_lock); + + if (err) + dev_err(ocelot->dev, "Error %d updating ethtool stats\n", err); + + queue_delayed_work(ocelot->stats_queue, &ocelot->stats_work, + OCELOT_STATS_CHECK_DELAY); +} + +void ocelot_get_strings(struct ocelot *ocelot, int port, u32 sset, u8 *data) +{ + int i; + + if (sset != ETH_SS_STATS) + return; + + for (i = 0; i < OCELOT_NUM_STATS; i++) { + if (ocelot->stats_layout[i].name[0] == '\0') + continue; + + memcpy(data + i * ETH_GSTRING_LEN, ocelot->stats_layout[i].name, + ETH_GSTRING_LEN); + } +} +EXPORT_SYMBOL(ocelot_get_strings); + +/* Update ocelot->stats for the given port and run the given callback */ +static void ocelot_port_stats_run(struct ocelot *ocelot, int port, void *priv, + void (*cb)(struct ocelot *ocelot, int port, + void *priv)) +{ + int err; + + mutex_lock(&ocelot->stat_view_lock); + + err = ocelot_port_update_stats(ocelot, port); + if (err) { + dev_err(ocelot->dev, "Failed to update port %d stats: %pe\n", + port, ERR_PTR(err)); + goto out_unlock; + } + + spin_lock(&ocelot->stats_lock); + + ocelot_port_transfer_stats(ocelot, port); + cb(ocelot, port, priv); + + spin_unlock(&ocelot->stats_lock); + +out_unlock: + mutex_unlock(&ocelot->stat_view_lock); +} + +int ocelot_get_sset_count(struct ocelot *ocelot, int port, int sset) +{ + int i, num_stats = 0; + + if (sset != ETH_SS_STATS) + return -EOPNOTSUPP; + + for (i = 0; i < OCELOT_NUM_STATS; i++) + if (ocelot->stats_layout[i].name[0] != '\0') + num_stats++; + + return num_stats; +} +EXPORT_SYMBOL(ocelot_get_sset_count); + +static void ocelot_port_ethtool_stats_cb(struct ocelot *ocelot, int port, + void *priv) +{ + u64 *data = priv; + int i; + + /* Copy all supported counters */ + for (i = 0; i < OCELOT_NUM_STATS; i++) { + int index = port * OCELOT_NUM_STATS + i; + + if (ocelot->stats_layout[i].name[0] == '\0') + continue; + + *data++ = ocelot->stats[index]; + } +} + +void ocelot_get_ethtool_stats(struct ocelot *ocelot, int port, u64 *data) +{ + ocelot_port_stats_run(ocelot, port, data, ocelot_port_ethtool_stats_cb); +} +EXPORT_SYMBOL(ocelot_get_ethtool_stats); + +static void ocelot_port_pause_stats_cb(struct ocelot *ocelot, int port, void *priv) +{ + u64 *s = &ocelot->stats[port * OCELOT_NUM_STATS]; + struct ethtool_pause_stats *pause_stats = priv; + + pause_stats->tx_pause_frames = s[OCELOT_STAT_TX_PAUSE]; + pause_stats->rx_pause_frames = s[OCELOT_STAT_RX_PAUSE]; +} + +void ocelot_port_get_pause_stats(struct ocelot *ocelot, int port, + struct ethtool_pause_stats *pause_stats) +{ + ocelot_port_stats_run(ocelot, port, pause_stats, + ocelot_port_pause_stats_cb); +} +EXPORT_SYMBOL_GPL(ocelot_port_get_pause_stats); + +static const struct ethtool_rmon_hist_range ocelot_rmon_ranges[] = { + { 64, 64 }, + { 65, 127 }, + { 128, 255 }, + { 256, 511 }, + { 512, 1023 }, + { 1024, 1526 }, + { 1527, 65535 }, + {}, +}; + +static void ocelot_port_rmon_stats_cb(struct ocelot *ocelot, int port, void *priv) +{ + u64 *s = &ocelot->stats[port * OCELOT_NUM_STATS]; + struct ethtool_rmon_stats *rmon_stats = priv; + + rmon_stats->undersize_pkts = s[OCELOT_STAT_RX_SHORTS]; + rmon_stats->oversize_pkts = s[OCELOT_STAT_RX_LONGS]; + rmon_stats->fragments = s[OCELOT_STAT_RX_FRAGMENTS]; + rmon_stats->jabbers = s[OCELOT_STAT_RX_JABBERS]; + + rmon_stats->hist[0] = s[OCELOT_STAT_RX_64]; + rmon_stats->hist[1] = s[OCELOT_STAT_RX_65_127]; + rmon_stats->hist[2] = s[OCELOT_STAT_RX_128_255]; + rmon_stats->hist[3] = s[OCELOT_STAT_RX_256_511]; + rmon_stats->hist[4] = s[OCELOT_STAT_RX_512_1023]; + rmon_stats->hist[5] = s[OCELOT_STAT_RX_1024_1526]; + rmon_stats->hist[6] = s[OCELOT_STAT_RX_1527_MAX]; + + rmon_stats->hist_tx[0] = s[OCELOT_STAT_TX_64]; + rmon_stats->hist_tx[1] = s[OCELOT_STAT_TX_65_127]; + rmon_stats->hist_tx[2] = s[OCELOT_STAT_TX_128_255]; + rmon_stats->hist_tx[3] = s[OCELOT_STAT_TX_128_255]; + rmon_stats->hist_tx[4] = s[OCELOT_STAT_TX_256_511]; + rmon_stats->hist_tx[5] = s[OCELOT_STAT_TX_512_1023]; + rmon_stats->hist_tx[6] = s[OCELOT_STAT_TX_1024_1526]; +} + +void ocelot_port_get_rmon_stats(struct ocelot *ocelot, int port, + struct ethtool_rmon_stats *rmon_stats, + const struct ethtool_rmon_hist_range **ranges) +{ + *ranges = ocelot_rmon_ranges; + + ocelot_port_stats_run(ocelot, port, rmon_stats, + ocelot_port_rmon_stats_cb); +} +EXPORT_SYMBOL_GPL(ocelot_port_get_rmon_stats); + +static void ocelot_port_ctrl_stats_cb(struct ocelot *ocelot, int port, void *priv) +{ + u64 *s = &ocelot->stats[port * OCELOT_NUM_STATS]; + struct ethtool_eth_ctrl_stats *ctrl_stats = priv; + + ctrl_stats->MACControlFramesReceived = s[OCELOT_STAT_RX_CONTROL]; +} + +void ocelot_port_get_eth_ctrl_stats(struct ocelot *ocelot, int port, + struct ethtool_eth_ctrl_stats *ctrl_stats) +{ + ocelot_port_stats_run(ocelot, port, ctrl_stats, + ocelot_port_ctrl_stats_cb); +} +EXPORT_SYMBOL_GPL(ocelot_port_get_eth_ctrl_stats); + +static void ocelot_port_mac_stats_cb(struct ocelot *ocelot, int port, void *priv) +{ + u64 *s = &ocelot->stats[port * OCELOT_NUM_STATS]; + struct ethtool_eth_mac_stats *mac_stats = priv; + + mac_stats->OctetsTransmittedOK = s[OCELOT_STAT_TX_OCTETS]; + mac_stats->FramesTransmittedOK = s[OCELOT_STAT_TX_64] + + s[OCELOT_STAT_TX_65_127] + + s[OCELOT_STAT_TX_128_255] + + s[OCELOT_STAT_TX_256_511] + + s[OCELOT_STAT_TX_512_1023] + + s[OCELOT_STAT_TX_1024_1526] + + s[OCELOT_STAT_TX_1527_MAX]; + mac_stats->OctetsReceivedOK = s[OCELOT_STAT_RX_OCTETS]; + mac_stats->FramesReceivedOK = s[OCELOT_STAT_RX_GREEN_PRIO_0] + + s[OCELOT_STAT_RX_GREEN_PRIO_1] + + s[OCELOT_STAT_RX_GREEN_PRIO_2] + + s[OCELOT_STAT_RX_GREEN_PRIO_3] + + s[OCELOT_STAT_RX_GREEN_PRIO_4] + + s[OCELOT_STAT_RX_GREEN_PRIO_5] + + s[OCELOT_STAT_RX_GREEN_PRIO_6] + + s[OCELOT_STAT_RX_GREEN_PRIO_7] + + s[OCELOT_STAT_RX_YELLOW_PRIO_0] + + s[OCELOT_STAT_RX_YELLOW_PRIO_1] + + s[OCELOT_STAT_RX_YELLOW_PRIO_2] + + s[OCELOT_STAT_RX_YELLOW_PRIO_3] + + s[OCELOT_STAT_RX_YELLOW_PRIO_4] + + s[OCELOT_STAT_RX_YELLOW_PRIO_5] + + s[OCELOT_STAT_RX_YELLOW_PRIO_6] + + s[OCELOT_STAT_RX_YELLOW_PRIO_7]; + mac_stats->MulticastFramesXmittedOK = s[OCELOT_STAT_TX_MULTICAST]; + mac_stats->BroadcastFramesXmittedOK = s[OCELOT_STAT_TX_BROADCAST]; + mac_stats->MulticastFramesReceivedOK = s[OCELOT_STAT_RX_MULTICAST]; + mac_stats->BroadcastFramesReceivedOK = s[OCELOT_STAT_RX_BROADCAST]; + mac_stats->FrameTooLongErrors = s[OCELOT_STAT_RX_LONGS]; + /* Sadly, C_RX_CRC is the sum of FCS and alignment errors, they are not + * counted individually. + */ + mac_stats->FrameCheckSequenceErrors = s[OCELOT_STAT_RX_CRC_ALIGN_ERRS]; + mac_stats->AlignmentErrors = s[OCELOT_STAT_RX_CRC_ALIGN_ERRS]; +} + +void ocelot_port_get_eth_mac_stats(struct ocelot *ocelot, int port, + struct ethtool_eth_mac_stats *mac_stats) +{ + ocelot_port_stats_run(ocelot, port, mac_stats, + ocelot_port_mac_stats_cb); +} +EXPORT_SYMBOL_GPL(ocelot_port_get_eth_mac_stats); + +static void ocelot_port_phy_stats_cb(struct ocelot *ocelot, int port, void *priv) +{ + u64 *s = &ocelot->stats[port * OCELOT_NUM_STATS]; + struct ethtool_eth_phy_stats *phy_stats = priv; + + phy_stats->SymbolErrorDuringCarrier = s[OCELOT_STAT_RX_SYM_ERRS]; +} + +void ocelot_port_get_eth_phy_stats(struct ocelot *ocelot, int port, + struct ethtool_eth_phy_stats *phy_stats) +{ + ocelot_port_stats_run(ocelot, port, phy_stats, + ocelot_port_phy_stats_cb); +} +EXPORT_SYMBOL_GPL(ocelot_port_get_eth_phy_stats); + +void ocelot_port_get_stats64(struct ocelot *ocelot, int port, + struct rtnl_link_stats64 *stats) +{ + u64 *s = &ocelot->stats[port * OCELOT_NUM_STATS]; + + spin_lock(&ocelot->stats_lock); + + /* Get Rx stats */ + stats->rx_bytes = s[OCELOT_STAT_RX_OCTETS]; + stats->rx_packets = s[OCELOT_STAT_RX_SHORTS] + + s[OCELOT_STAT_RX_FRAGMENTS] + + s[OCELOT_STAT_RX_JABBERS] + + s[OCELOT_STAT_RX_LONGS] + + s[OCELOT_STAT_RX_64] + + s[OCELOT_STAT_RX_65_127] + + s[OCELOT_STAT_RX_128_255] + + s[OCELOT_STAT_RX_256_511] + + s[OCELOT_STAT_RX_512_1023] + + s[OCELOT_STAT_RX_1024_1526] + + s[OCELOT_STAT_RX_1527_MAX]; + stats->multicast = s[OCELOT_STAT_RX_MULTICAST]; + stats->rx_missed_errors = s[OCELOT_STAT_DROP_TAIL]; + stats->rx_dropped = s[OCELOT_STAT_RX_RED_PRIO_0] + + s[OCELOT_STAT_RX_RED_PRIO_1] + + s[OCELOT_STAT_RX_RED_PRIO_2] + + s[OCELOT_STAT_RX_RED_PRIO_3] + + s[OCELOT_STAT_RX_RED_PRIO_4] + + s[OCELOT_STAT_RX_RED_PRIO_5] + + s[OCELOT_STAT_RX_RED_PRIO_6] + + s[OCELOT_STAT_RX_RED_PRIO_7] + + s[OCELOT_STAT_DROP_LOCAL] + + s[OCELOT_STAT_DROP_YELLOW_PRIO_0] + + s[OCELOT_STAT_DROP_YELLOW_PRIO_1] + + s[OCELOT_STAT_DROP_YELLOW_PRIO_2] + + s[OCELOT_STAT_DROP_YELLOW_PRIO_3] + + s[OCELOT_STAT_DROP_YELLOW_PRIO_4] + + s[OCELOT_STAT_DROP_YELLOW_PRIO_5] + + s[OCELOT_STAT_DROP_YELLOW_PRIO_6] + + s[OCELOT_STAT_DROP_YELLOW_PRIO_7] + + s[OCELOT_STAT_DROP_GREEN_PRIO_0] + + s[OCELOT_STAT_DROP_GREEN_PRIO_1] + + s[OCELOT_STAT_DROP_GREEN_PRIO_2] + + s[OCELOT_STAT_DROP_GREEN_PRIO_3] + + s[OCELOT_STAT_DROP_GREEN_PRIO_4] + + s[OCELOT_STAT_DROP_GREEN_PRIO_5] + + s[OCELOT_STAT_DROP_GREEN_PRIO_6] + + s[OCELOT_STAT_DROP_GREEN_PRIO_7]; + + /* Get Tx stats */ + stats->tx_bytes = s[OCELOT_STAT_TX_OCTETS]; + stats->tx_packets = s[OCELOT_STAT_TX_64] + + s[OCELOT_STAT_TX_65_127] + + s[OCELOT_STAT_TX_128_255] + + s[OCELOT_STAT_TX_256_511] + + s[OCELOT_STAT_TX_512_1023] + + s[OCELOT_STAT_TX_1024_1526] + + s[OCELOT_STAT_TX_1527_MAX]; + stats->tx_dropped = s[OCELOT_STAT_TX_DROPS] + + s[OCELOT_STAT_TX_AGED]; + stats->collisions = s[OCELOT_STAT_TX_COLLISION]; + + spin_unlock(&ocelot->stats_lock); +} +EXPORT_SYMBOL(ocelot_port_get_stats64); + +static int ocelot_prepare_stats_regions(struct ocelot *ocelot) +{ + struct ocelot_stats_region *region = NULL; + unsigned int last; + int i; + + INIT_LIST_HEAD(&ocelot->stats_regions); + + for (i = 0; i < OCELOT_NUM_STATS; i++) { + if (!ocelot->stats_layout[i].reg) + continue; + + if (region && ocelot->stats_layout[i].reg == last + 4) { + region->count++; + } else { + region = devm_kzalloc(ocelot->dev, sizeof(*region), + GFP_KERNEL); + if (!region) + return -ENOMEM; + + region->base = ocelot->stats_layout[i].reg; + region->count = 1; + list_add_tail(®ion->node, &ocelot->stats_regions); + } + + last = ocelot->stats_layout[i].reg; + } + + list_for_each_entry(region, &ocelot->stats_regions, node) { + region->buf = devm_kcalloc(ocelot->dev, region->count, + sizeof(*region->buf), GFP_KERNEL); + if (!region->buf) + return -ENOMEM; + } + + return 0; +} + +int ocelot_stats_init(struct ocelot *ocelot) +{ + char queue_name[32]; + int ret; + + ocelot->stats = devm_kcalloc(ocelot->dev, + ocelot->num_phys_ports * OCELOT_NUM_STATS, + sizeof(u64), GFP_KERNEL); + if (!ocelot->stats) + return -ENOMEM; + + snprintf(queue_name, sizeof(queue_name), "%s-stats", + dev_name(ocelot->dev)); + ocelot->stats_queue = create_singlethread_workqueue(queue_name); + if (!ocelot->stats_queue) + return -ENOMEM; + + spin_lock_init(&ocelot->stats_lock); + mutex_init(&ocelot->stat_view_lock); + + ret = ocelot_prepare_stats_regions(ocelot); + if (ret) { + destroy_workqueue(ocelot->stats_queue); + return ret; + } + + INIT_DELAYED_WORK(&ocelot->stats_work, ocelot_check_stats_work); + queue_delayed_work(ocelot->stats_queue, &ocelot->stats_work, + OCELOT_STATS_CHECK_DELAY); + + return 0; +} + +void ocelot_stats_deinit(struct ocelot *ocelot) +{ + cancel_delayed_work(&ocelot->stats_work); + destroy_workqueue(ocelot->stats_queue); +} diff --git a/drivers/net/ethernet/mscc/ocelot_vsc7514.c b/drivers/net/ethernet/mscc/ocelot_vsc7514.c index 9c488953f541..6f22aea08a64 100644 --- a/drivers/net/ethernet/mscc/ocelot_vsc7514.c +++ b/drivers/net/ethernet/mscc/ocelot_vsc7514.c @@ -6,6 +6,7 @@ */ #include <linux/dsa/ocelot.h> #include <linux/interrupt.h> +#include <linux/iopoll.h> #include <linux/module.h> #include <linux/of_net.h> #include <linux/netdevice.h> @@ -25,6 +26,9 @@ #define VSC7514_VCAP_POLICER_BASE 128 #define VSC7514_VCAP_POLICER_MAX 191 +#define MEM_INIT_SLEEP_US 1000 +#define MEM_INIT_TIMEOUT_US 100000 + static const u32 *ocelot_regmap[TARGET_MAX] = { [ANA] = vsc7514_ana_regmap, [QS] = vsc7514_qs_regmap, @@ -97,378 +101,7 @@ static const struct reg_field ocelot_regfields[REGFIELD_MAX] = { }; static const struct ocelot_stat_layout ocelot_stats_layout[OCELOT_NUM_STATS] = { - [OCELOT_STAT_RX_OCTETS] = { - .name = "rx_octets", - .reg = SYS_COUNT_RX_OCTETS, - }, - [OCELOT_STAT_RX_UNICAST] = { - .name = "rx_unicast", - .reg = SYS_COUNT_RX_UNICAST, - }, - [OCELOT_STAT_RX_MULTICAST] = { - .name = "rx_multicast", - .reg = SYS_COUNT_RX_MULTICAST, - }, - [OCELOT_STAT_RX_BROADCAST] = { - .name = "rx_broadcast", - .reg = SYS_COUNT_RX_BROADCAST, - }, - [OCELOT_STAT_RX_SHORTS] = { - .name = "rx_shorts", - .reg = SYS_COUNT_RX_SHORTS, - }, - [OCELOT_STAT_RX_FRAGMENTS] = { - .name = "rx_fragments", - .reg = SYS_COUNT_RX_FRAGMENTS, - }, - [OCELOT_STAT_RX_JABBERS] = { - .name = "rx_jabbers", - .reg = SYS_COUNT_RX_JABBERS, - }, - [OCELOT_STAT_RX_CRC_ALIGN_ERRS] = { - .name = "rx_crc_align_errs", - .reg = SYS_COUNT_RX_CRC_ALIGN_ERRS, - }, - [OCELOT_STAT_RX_SYM_ERRS] = { - .name = "rx_sym_errs", - .reg = SYS_COUNT_RX_SYM_ERRS, - }, - [OCELOT_STAT_RX_64] = { - .name = "rx_frames_below_65_octets", - .reg = SYS_COUNT_RX_64, - }, - [OCELOT_STAT_RX_65_127] = { - .name = "rx_frames_65_to_127_octets", - .reg = SYS_COUNT_RX_65_127, - }, - [OCELOT_STAT_RX_128_255] = { - .name = "rx_frames_128_to_255_octets", - .reg = SYS_COUNT_RX_128_255, - }, - [OCELOT_STAT_RX_256_511] = { - .name = "rx_frames_256_to_511_octets", - .reg = SYS_COUNT_RX_256_511, - }, - [OCELOT_STAT_RX_512_1023] = { - .name = "rx_frames_512_to_1023_octets", - .reg = SYS_COUNT_RX_512_1023, - }, - [OCELOT_STAT_RX_1024_1526] = { - .name = "rx_frames_1024_to_1526_octets", - .reg = SYS_COUNT_RX_1024_1526, - }, - [OCELOT_STAT_RX_1527_MAX] = { - .name = "rx_frames_over_1526_octets", - .reg = SYS_COUNT_RX_1527_MAX, - }, - [OCELOT_STAT_RX_PAUSE] = { - .name = "rx_pause", - .reg = SYS_COUNT_RX_PAUSE, - }, - [OCELOT_STAT_RX_CONTROL] = { - .name = "rx_control", - .reg = SYS_COUNT_RX_CONTROL, - }, - [OCELOT_STAT_RX_LONGS] = { - .name = "rx_longs", - .reg = SYS_COUNT_RX_LONGS, - }, - [OCELOT_STAT_RX_CLASSIFIED_DROPS] = { - .name = "rx_classified_drops", - .reg = SYS_COUNT_RX_CLASSIFIED_DROPS, - }, - [OCELOT_STAT_RX_RED_PRIO_0] = { - .name = "rx_red_prio_0", - .reg = SYS_COUNT_RX_RED_PRIO_0, - }, - [OCELOT_STAT_RX_RED_PRIO_1] = { - .name = "rx_red_prio_1", - .reg = SYS_COUNT_RX_RED_PRIO_1, - }, - [OCELOT_STAT_RX_RED_PRIO_2] = { - .name = "rx_red_prio_2", - .reg = SYS_COUNT_RX_RED_PRIO_2, - }, - [OCELOT_STAT_RX_RED_PRIO_3] = { - .name = "rx_red_prio_3", - .reg = SYS_COUNT_RX_RED_PRIO_3, - }, - [OCELOT_STAT_RX_RED_PRIO_4] = { - .name = "rx_red_prio_4", - .reg = SYS_COUNT_RX_RED_PRIO_4, - }, - [OCELOT_STAT_RX_RED_PRIO_5] = { - .name = "rx_red_prio_5", - .reg = SYS_COUNT_RX_RED_PRIO_5, - }, - [OCELOT_STAT_RX_RED_PRIO_6] = { - .name = "rx_red_prio_6", - .reg = SYS_COUNT_RX_RED_PRIO_6, - }, - [OCELOT_STAT_RX_RED_PRIO_7] = { - .name = "rx_red_prio_7", - .reg = SYS_COUNT_RX_RED_PRIO_7, - }, - [OCELOT_STAT_RX_YELLOW_PRIO_0] = { - .name = "rx_yellow_prio_0", - .reg = SYS_COUNT_RX_YELLOW_PRIO_0, - }, - [OCELOT_STAT_RX_YELLOW_PRIO_1] = { - .name = "rx_yellow_prio_1", - .reg = SYS_COUNT_RX_YELLOW_PRIO_1, - }, - [OCELOT_STAT_RX_YELLOW_PRIO_2] = { - .name = "rx_yellow_prio_2", - .reg = SYS_COUNT_RX_YELLOW_PRIO_2, - }, - [OCELOT_STAT_RX_YELLOW_PRIO_3] = { - .name = "rx_yellow_prio_3", - .reg = SYS_COUNT_RX_YELLOW_PRIO_3, - }, - [OCELOT_STAT_RX_YELLOW_PRIO_4] = { - .name = "rx_yellow_prio_4", - .reg = SYS_COUNT_RX_YELLOW_PRIO_4, - }, - [OCELOT_STAT_RX_YELLOW_PRIO_5] = { - .name = "rx_yellow_prio_5", - .reg = SYS_COUNT_RX_YELLOW_PRIO_5, - }, - [OCELOT_STAT_RX_YELLOW_PRIO_6] = { - .name = "rx_yellow_prio_6", - .reg = SYS_COUNT_RX_YELLOW_PRIO_6, - }, - [OCELOT_STAT_RX_YELLOW_PRIO_7] = { - .name = "rx_yellow_prio_7", - .reg = SYS_COUNT_RX_YELLOW_PRIO_7, - }, - [OCELOT_STAT_RX_GREEN_PRIO_0] = { - .name = "rx_green_prio_0", - .reg = SYS_COUNT_RX_GREEN_PRIO_0, - }, - [OCELOT_STAT_RX_GREEN_PRIO_1] = { - .name = "rx_green_prio_1", - .reg = SYS_COUNT_RX_GREEN_PRIO_1, - }, - [OCELOT_STAT_RX_GREEN_PRIO_2] = { - .name = "rx_green_prio_2", - .reg = SYS_COUNT_RX_GREEN_PRIO_2, - }, - [OCELOT_STAT_RX_GREEN_PRIO_3] = { - .name = "rx_green_prio_3", - .reg = SYS_COUNT_RX_GREEN_PRIO_3, - }, - [OCELOT_STAT_RX_GREEN_PRIO_4] = { - .name = "rx_green_prio_4", - .reg = SYS_COUNT_RX_GREEN_PRIO_4, - }, - [OCELOT_STAT_RX_GREEN_PRIO_5] = { - .name = "rx_green_prio_5", - .reg = SYS_COUNT_RX_GREEN_PRIO_5, - }, - [OCELOT_STAT_RX_GREEN_PRIO_6] = { - .name = "rx_green_prio_6", - .reg = SYS_COUNT_RX_GREEN_PRIO_6, - }, - [OCELOT_STAT_RX_GREEN_PRIO_7] = { - .name = "rx_green_prio_7", - .reg = SYS_COUNT_RX_GREEN_PRIO_7, - }, - [OCELOT_STAT_TX_OCTETS] = { - .name = "tx_octets", - .reg = SYS_COUNT_TX_OCTETS, - }, - [OCELOT_STAT_TX_UNICAST] = { - .name = "tx_unicast", - .reg = SYS_COUNT_TX_UNICAST, - }, - [OCELOT_STAT_TX_MULTICAST] = { - .name = "tx_multicast", - .reg = SYS_COUNT_TX_MULTICAST, - }, - [OCELOT_STAT_TX_BROADCAST] = { - .name = "tx_broadcast", - .reg = SYS_COUNT_TX_BROADCAST, - }, - [OCELOT_STAT_TX_COLLISION] = { - .name = "tx_collision", - .reg = SYS_COUNT_TX_COLLISION, - }, - [OCELOT_STAT_TX_DROPS] = { - .name = "tx_drops", - .reg = SYS_COUNT_TX_DROPS, - }, - [OCELOT_STAT_TX_PAUSE] = { - .name = "tx_pause", - .reg = SYS_COUNT_TX_PAUSE, - }, - [OCELOT_STAT_TX_64] = { - .name = "tx_frames_below_65_octets", - .reg = SYS_COUNT_TX_64, - }, - [OCELOT_STAT_TX_65_127] = { - .name = "tx_frames_65_to_127_octets", - .reg = SYS_COUNT_TX_65_127, - }, - [OCELOT_STAT_TX_128_255] = { - .name = "tx_frames_128_255_octets", - .reg = SYS_COUNT_TX_128_255, - }, - [OCELOT_STAT_TX_256_511] = { - .name = "tx_frames_256_511_octets", - .reg = SYS_COUNT_TX_256_511, - }, - [OCELOT_STAT_TX_512_1023] = { - .name = "tx_frames_512_1023_octets", - .reg = SYS_COUNT_TX_512_1023, - }, - [OCELOT_STAT_TX_1024_1526] = { - .name = "tx_frames_1024_1526_octets", - .reg = SYS_COUNT_TX_1024_1526, - }, - [OCELOT_STAT_TX_1527_MAX] = { - .name = "tx_frames_over_1526_octets", - .reg = SYS_COUNT_TX_1527_MAX, - }, - [OCELOT_STAT_TX_YELLOW_PRIO_0] = { - .name = "tx_yellow_prio_0", - .reg = SYS_COUNT_TX_YELLOW_PRIO_0, - }, - [OCELOT_STAT_TX_YELLOW_PRIO_1] = { - .name = "tx_yellow_prio_1", - .reg = SYS_COUNT_TX_YELLOW_PRIO_1, - }, - [OCELOT_STAT_TX_YELLOW_PRIO_2] = { - .name = "tx_yellow_prio_2", - .reg = SYS_COUNT_TX_YELLOW_PRIO_2, - }, - [OCELOT_STAT_TX_YELLOW_PRIO_3] = { - .name = "tx_yellow_prio_3", - .reg = SYS_COUNT_TX_YELLOW_PRIO_3, - }, - [OCELOT_STAT_TX_YELLOW_PRIO_4] = { - .name = "tx_yellow_prio_4", - .reg = SYS_COUNT_TX_YELLOW_PRIO_4, - }, - [OCELOT_STAT_TX_YELLOW_PRIO_5] = { - .name = "tx_yellow_prio_5", - .reg = SYS_COUNT_TX_YELLOW_PRIO_5, - }, - [OCELOT_STAT_TX_YELLOW_PRIO_6] = { - .name = "tx_yellow_prio_6", - .reg = SYS_COUNT_TX_YELLOW_PRIO_6, - }, - [OCELOT_STAT_TX_YELLOW_PRIO_7] = { - .name = "tx_yellow_prio_7", - .reg = SYS_COUNT_TX_YELLOW_PRIO_7, - }, - [OCELOT_STAT_TX_GREEN_PRIO_0] = { - .name = "tx_green_prio_0", - .reg = SYS_COUNT_TX_GREEN_PRIO_0, - }, - [OCELOT_STAT_TX_GREEN_PRIO_1] = { - .name = "tx_green_prio_1", - .reg = SYS_COUNT_TX_GREEN_PRIO_1, - }, - [OCELOT_STAT_TX_GREEN_PRIO_2] = { - .name = "tx_green_prio_2", - .reg = SYS_COUNT_TX_GREEN_PRIO_2, - }, - [OCELOT_STAT_TX_GREEN_PRIO_3] = { - .name = "tx_green_prio_3", - .reg = SYS_COUNT_TX_GREEN_PRIO_3, - }, - [OCELOT_STAT_TX_GREEN_PRIO_4] = { - .name = "tx_green_prio_4", - .reg = SYS_COUNT_TX_GREEN_PRIO_4, - }, - [OCELOT_STAT_TX_GREEN_PRIO_5] = { - .name = "tx_green_prio_5", - .reg = SYS_COUNT_TX_GREEN_PRIO_5, - }, - [OCELOT_STAT_TX_GREEN_PRIO_6] = { - .name = "tx_green_prio_6", - .reg = SYS_COUNT_TX_GREEN_PRIO_6, - }, - [OCELOT_STAT_TX_GREEN_PRIO_7] = { - .name = "tx_green_prio_7", - .reg = SYS_COUNT_TX_GREEN_PRIO_7, - }, - [OCELOT_STAT_TX_AGED] = { - .name = "tx_aged", - .reg = SYS_COUNT_TX_AGING, - }, - [OCELOT_STAT_DROP_LOCAL] = { - .name = "drop_local", - .reg = SYS_COUNT_DROP_LOCAL, - }, - [OCELOT_STAT_DROP_TAIL] = { - .name = "drop_tail", - .reg = SYS_COUNT_DROP_TAIL, - }, - [OCELOT_STAT_DROP_YELLOW_PRIO_0] = { - .name = "drop_yellow_prio_0", - .reg = SYS_COUNT_DROP_YELLOW_PRIO_0, - }, - [OCELOT_STAT_DROP_YELLOW_PRIO_1] = { - .name = "drop_yellow_prio_1", - .reg = SYS_COUNT_DROP_YELLOW_PRIO_1, - }, - [OCELOT_STAT_DROP_YELLOW_PRIO_2] = { - .name = "drop_yellow_prio_2", - .reg = SYS_COUNT_DROP_YELLOW_PRIO_2, - }, - [OCELOT_STAT_DROP_YELLOW_PRIO_3] = { - .name = "drop_yellow_prio_3", - .reg = SYS_COUNT_DROP_YELLOW_PRIO_3, - }, - [OCELOT_STAT_DROP_YELLOW_PRIO_4] = { - .name = "drop_yellow_prio_4", - .reg = SYS_COUNT_DROP_YELLOW_PRIO_4, - }, - [OCELOT_STAT_DROP_YELLOW_PRIO_5] = { - .name = "drop_yellow_prio_5", - .reg = SYS_COUNT_DROP_YELLOW_PRIO_5, - }, - [OCELOT_STAT_DROP_YELLOW_PRIO_6] = { - .name = "drop_yellow_prio_6", - .reg = SYS_COUNT_DROP_YELLOW_PRIO_6, - }, - [OCELOT_STAT_DROP_YELLOW_PRIO_7] = { - .name = "drop_yellow_prio_7", - .reg = SYS_COUNT_DROP_YELLOW_PRIO_7, - }, - [OCELOT_STAT_DROP_GREEN_PRIO_0] = { - .name = "drop_green_prio_0", - .reg = SYS_COUNT_DROP_GREEN_PRIO_0, - }, - [OCELOT_STAT_DROP_GREEN_PRIO_1] = { - .name = "drop_green_prio_1", - .reg = SYS_COUNT_DROP_GREEN_PRIO_1, - }, - [OCELOT_STAT_DROP_GREEN_PRIO_2] = { - .name = "drop_green_prio_2", - .reg = SYS_COUNT_DROP_GREEN_PRIO_2, - }, - [OCELOT_STAT_DROP_GREEN_PRIO_3] = { - .name = "drop_green_prio_3", - .reg = SYS_COUNT_DROP_GREEN_PRIO_3, - }, - [OCELOT_STAT_DROP_GREEN_PRIO_4] = { - .name = "drop_green_prio_4", - .reg = SYS_COUNT_DROP_GREEN_PRIO_4, - }, - [OCELOT_STAT_DROP_GREEN_PRIO_5] = { - .name = "drop_green_prio_5", - .reg = SYS_COUNT_DROP_GREEN_PRIO_5, - }, - [OCELOT_STAT_DROP_GREEN_PRIO_6] = { - .name = "drop_green_prio_6", - .reg = SYS_COUNT_DROP_GREEN_PRIO_6, - }, - [OCELOT_STAT_DROP_GREEN_PRIO_7] = { - .name = "drop_green_prio_7", - .reg = SYS_COUNT_DROP_GREEN_PRIO_7, - }, + OCELOT_COMMON_STATS, }; static void ocelot_pll5_init(struct ocelot *ocelot) @@ -562,27 +195,43 @@ static const struct of_device_id mscc_ocelot_match[] = { }; MODULE_DEVICE_TABLE(of, mscc_ocelot_match); +static int ocelot_mem_init_status(struct ocelot *ocelot) +{ + unsigned int val; + int err; + + err = regmap_field_read(ocelot->regfields[SYS_RESET_CFG_MEM_INIT], + &val); + + return err ?: val; +} + static int ocelot_reset(struct ocelot *ocelot) { - int retries = 100; + int err; u32 val; - regmap_field_write(ocelot->regfields[SYS_RESET_CFG_MEM_INIT], 1); - regmap_field_write(ocelot->regfields[SYS_RESET_CFG_MEM_ENA], 1); + err = regmap_field_write(ocelot->regfields[SYS_RESET_CFG_MEM_INIT], 1); + if (err) + return err; - do { - msleep(1); - regmap_field_read(ocelot->regfields[SYS_RESET_CFG_MEM_INIT], - &val); - } while (val && --retries); + err = regmap_field_write(ocelot->regfields[SYS_RESET_CFG_MEM_ENA], 1); + if (err) + return err; - if (!retries) - return -ETIMEDOUT; + /* MEM_INIT is a self-clearing bit. Wait for it to be cleared (should be + * 100us) before enabling the switch core. + */ + err = readx_poll_timeout(ocelot_mem_init_status, ocelot, val, !val, + MEM_INIT_SLEEP_US, MEM_INIT_TIMEOUT_US); + if (err) + return err; - regmap_field_write(ocelot->regfields[SYS_RESET_CFG_MEM_ENA], 1); - regmap_field_write(ocelot->regfields[SYS_RESET_CFG_CORE_ENA], 1); + err = regmap_field_write(ocelot->regfields[SYS_RESET_CFG_MEM_ENA], 1); + if (err) + return err; - return 0; + return regmap_field_write(ocelot->regfields[SYS_RESET_CFG_CORE_ENA], 1); } /* Watermark encode diff --git a/drivers/net/ethernet/mscc/vsc7514_regs.c b/drivers/net/ethernet/mscc/vsc7514_regs.c index 9cf82ecf191c..9d2d3e13cacf 100644 --- a/drivers/net/ethernet/mscc/vsc7514_regs.c +++ b/drivers/net/ethernet/mscc/vsc7514_regs.c @@ -242,7 +242,7 @@ const u32 vsc7514_sys_regmap[] = { REG(SYS_COUNT_TX_GREEN_PRIO_5, 0x00016c), REG(SYS_COUNT_TX_GREEN_PRIO_6, 0x000170), REG(SYS_COUNT_TX_GREEN_PRIO_7, 0x000174), - REG(SYS_COUNT_TX_AGING, 0x000178), + REG(SYS_COUNT_TX_AGED, 0x000178), REG(SYS_COUNT_DROP_LOCAL, 0x000200), REG(SYS_COUNT_DROP_TAIL, 0x000204), REG(SYS_COUNT_DROP_YELLOW_PRIO_0, 0x000208), @@ -283,7 +283,6 @@ const u32 vsc7514_sys_regmap[] = { REG(SYS_MMGT_FAST, 0x0006a0), REG(SYS_EVENTS_DIF, 0x0006a4), REG(SYS_EVENTS_CORE, 0x0006b4), - REG(SYS_CNT, 0x000000), REG(SYS_PTP_STATUS, 0x0006b8), REG(SYS_PTP_TXSTAMP, 0x0006bc), REG(SYS_PTP_NXT, 0x0006c0), diff --git a/drivers/net/ethernet/myricom/myri10ge/myri10ge.c b/drivers/net/ethernet/myricom/myri10ge/myri10ge.c index 971dde8c3286..9063e2e22cd5 100644 --- a/drivers/net/ethernet/myricom/myri10ge/myri10ge.c +++ b/drivers/net/ethernet/myricom/myri10ge/myri10ge.c @@ -1647,10 +1647,10 @@ myri10ge_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *info) { struct myri10ge_priv *mgp = netdev_priv(netdev); - strlcpy(info->driver, "myri10ge", sizeof(info->driver)); - strlcpy(info->version, MYRI10GE_VERSION_STR, sizeof(info->version)); - strlcpy(info->fw_version, mgp->fw_version, sizeof(info->fw_version)); - strlcpy(info->bus_info, pci_name(mgp->pdev), sizeof(info->bus_info)); + strscpy(info->driver, "myri10ge", sizeof(info->driver)); + strscpy(info->version, MYRI10GE_VERSION_STR, sizeof(info->version)); + strscpy(info->fw_version, mgp->fw_version, sizeof(info->fw_version)); + strscpy(info->bus_info, pci_name(mgp->pdev), sizeof(info->bus_info)); } static int myri10ge_get_coalesce(struct net_device *netdev, diff --git a/drivers/net/ethernet/natsemi/natsemi.c b/drivers/net/ethernet/natsemi/natsemi.c index 9aae7f1eb5d2..650a5a166070 100644 --- a/drivers/net/ethernet/natsemi/natsemi.c +++ b/drivers/net/ethernet/natsemi/natsemi.c @@ -869,7 +869,7 @@ static int natsemi_probe1(struct pci_dev *pdev, const struct pci_device_id *ent) np = netdev_priv(dev); np->ioaddr = ioaddr; - netif_napi_add(dev, &np->napi, natsemi_poll, 64); + netif_napi_add(dev, &np->napi, natsemi_poll); np->dev = dev; np->pci_dev = pdev; @@ -2564,9 +2564,9 @@ static void set_rx_mode(struct net_device *dev) static void get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { struct netdev_private *np = netdev_priv(dev); - strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); - strlcpy(info->version, DRV_VERSION, sizeof(info->version)); - strlcpy(info->bus_info, pci_name(np->pci_dev), sizeof(info->bus_info)); + strscpy(info->driver, DRV_NAME, sizeof(info->driver)); + strscpy(info->version, DRV_VERSION, sizeof(info->version)); + strscpy(info->bus_info, pci_name(np->pci_dev), sizeof(info->bus_info)); } static int get_regs_len(struct net_device *dev) diff --git a/drivers/net/ethernet/natsemi/ns83820.c b/drivers/net/ethernet/natsemi/ns83820.c index 49ea130c9067..998586872599 100644 --- a/drivers/net/ethernet/natsemi/ns83820.c +++ b/drivers/net/ethernet/natsemi/ns83820.c @@ -1351,9 +1351,9 @@ static int ns83820_set_link_ksettings(struct net_device *ndev, static void ns83820_get_drvinfo(struct net_device *ndev, struct ethtool_drvinfo *info) { struct ns83820 *dev = PRIV(ndev); - strlcpy(info->driver, "ns83820", sizeof(info->driver)); - strlcpy(info->version, VERSION, sizeof(info->version)); - strlcpy(info->bus_info, pci_name(dev->pci_dev), sizeof(info->bus_info)); + strscpy(info->driver, "ns83820", sizeof(info->driver)); + strscpy(info->version, VERSION, sizeof(info->version)); + strscpy(info->bus_info, pci_name(dev->pci_dev), sizeof(info->bus_info)); } static u32 ns83820_get_link(struct net_device *ndev) diff --git a/drivers/net/ethernet/neterion/s2io.c b/drivers/net/ethernet/neterion/s2io.c index 30f955efa830..dcf8212119f9 100644 --- a/drivers/net/ethernet/neterion/s2io.c +++ b/drivers/net/ethernet/neterion/s2io.c @@ -5348,9 +5348,9 @@ static void s2io_ethtool_gdrvinfo(struct net_device *dev, { struct s2io_nic *sp = netdev_priv(dev); - strlcpy(info->driver, s2io_driver_name, sizeof(info->driver)); - strlcpy(info->version, s2io_driver_version, sizeof(info->version)); - strlcpy(info->bus_info, pci_name(sp->pdev), sizeof(info->bus_info)); + strscpy(info->driver, s2io_driver_name, sizeof(info->driver)); + strscpy(info->version, s2io_driver_version, sizeof(info->version)); + strscpy(info->bus_info, pci_name(sp->pdev), sizeof(info->bus_info)); } /** @@ -7359,10 +7359,9 @@ static int rx_osm_handler(struct ring_info *ring_data, struct RxD_t * rxdp) int get_off = ring_data->rx_curr_get_info.offset; int buf0_len = RXD_GET_BUFFER0_SIZE_3(rxdp->Control_2); int buf2_len = RXD_GET_BUFFER2_SIZE_3(rxdp->Control_2); - unsigned char *buff = skb_push(skb, buf0_len); struct buffAdd *ba = &ring_data->ba[get_block][get_off]; - memcpy(buff, ba->ba_0, buf0_len); + skb_put_data(skb, ba->ba_0, buf0_len); skb_put(skb, buf2_len); } @@ -7905,10 +7904,10 @@ s2io_init_nic(struct pci_dev *pdev, const struct pci_device_id *pre) for (i = 0; i < config->rx_ring_num ; i++) { struct ring_info *ring = &mac_control->rings[i]; - netif_napi_add(dev, &ring->napi, s2io_poll_msix, 64); + netif_napi_add(dev, &ring->napi, s2io_poll_msix); } } else { - netif_napi_add(dev, &sp->napi, s2io_poll_inta, 64); + netif_napi_add(dev, &sp->napi, s2io_poll_inta); } /* Not needed for Herc */ diff --git a/drivers/net/ethernet/netronome/nfp/crypto/tls.c b/drivers/net/ethernet/netronome/nfp/crypto/tls.c index 78368e71ce83..f80f1a6953fa 100644 --- a/drivers/net/ethernet/netronome/nfp/crypto/tls.c +++ b/drivers/net/ethernet/netronome/nfp/crypto/tls.c @@ -474,6 +474,7 @@ int nfp_net_tls_rx_resync_req(struct net_device *netdev, { struct nfp_net *nn = netdev_priv(netdev); struct nfp_net_tls_offload_ctx *ntls; + struct net *net = dev_net(netdev); struct ipv6hdr *ipv6h; struct tcphdr *th; struct iphdr *iph; @@ -494,13 +495,13 @@ int nfp_net_tls_rx_resync_req(struct net_device *netdev, switch (ipv6h->version) { case 4: - sk = inet_lookup_established(dev_net(netdev), &tcp_hashinfo, + sk = inet_lookup_established(net, net->ipv4.tcp_death_row.hashinfo, iph->saddr, th->source, iph->daddr, th->dest, netdev->ifindex); break; #if IS_ENABLED(CONFIG_IPV6) case 6: - sk = __inet6_lookup_established(dev_net(netdev), &tcp_hashinfo, + sk = __inet6_lookup_established(net, net->ipv4.tcp_death_row.hashinfo, &ipv6h->saddr, th->source, &ipv6h->daddr, ntohs(th->dest), netdev->ifindex, 0); diff --git a/drivers/net/ethernet/netronome/nfp/flower/conntrack.c b/drivers/net/ethernet/netronome/nfp/flower/conntrack.c index b3b2a23b8d89..f693119541d5 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/conntrack.c +++ b/drivers/net/ethernet/netronome/nfp/flower/conntrack.c @@ -1,6 +1,9 @@ // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) /* Copyright (C) 2021 Corigine, Inc. */ +#include <net/tc_act/tc_csum.h> +#include <net/tc_act/tc_ct.h> + #include "conntrack.h" #include "../nfp_port.h" @@ -56,9 +59,17 @@ bool is_pre_ct_flow(struct flow_cls_offload *flow) int i; flow_action_for_each(i, act, &flow->rule->action) { - if (act->id == FLOW_ACTION_CT && !act->ct.action) - return true; + if (act->id == FLOW_ACTION_CT) { + /* The pre_ct rule only have the ct or ct nat action, cannot + * contains other ct action e.g ct commit and so on. + */ + if ((!act->ct.action || act->ct.action == TCA_CT_ACT_NAT)) + return true; + else + return false; + } } + return false; } @@ -66,13 +77,37 @@ bool is_post_ct_flow(struct flow_cls_offload *flow) { struct flow_rule *rule = flow_cls_offload_flow_rule(flow); struct flow_dissector *dissector = rule->match.dissector; + struct flow_action_entry *act; + bool exist_ct_clear = false; struct flow_match_ct ct; + int i; + + /* post ct entry cannot contains any ct action except ct_clear. */ + flow_action_for_each(i, act, &flow->rule->action) { + if (act->id == FLOW_ACTION_CT) { + /* ignore ct clear action. */ + if (act->ct.action == TCA_CT_ACT_CLEAR) { + exist_ct_clear = true; + continue; + } + + return false; + } + } if (dissector->used_keys & BIT(FLOW_DISSECTOR_KEY_CT)) { flow_rule_match_ct(rule, &ct); if (ct.key->ct_state & TCA_FLOWER_KEY_CT_FLAGS_ESTABLISHED) return true; + } else { + /* when do nat with ct, the post ct entry ignore the ct status, + * will match the nat field(sip/dip) instead. In this situation, + * the flow chain index is not zero and contains ct clear action. + */ + if (flow->common.chain_index && exist_ct_clear) + return true; } + return false; } @@ -168,6 +203,20 @@ static void *get_mangled_tos_ttl(struct flow_rule *rule, void *buf, return buf; } +/* Note entry1 and entry2 are not swappable. only skip ip and + * tport merge check for pre_ct and post_ct when pre_ct do nat. + */ +static bool nfp_ct_merge_check_cannot_skip(struct nfp_fl_ct_flow_entry *entry1, + struct nfp_fl_ct_flow_entry *entry2) +{ + /* only pre_ct have NFP_FL_ACTION_DO_NAT flag. */ + if ((entry1->flags & NFP_FL_ACTION_DO_NAT) && + entry2->type == CT_TYPE_POST_CT) + return false; + + return true; +} + /* Note entry1 and entry2 are not swappable, entry1 should be * the former flow whose mangle action need be taken into account * if existed, and entry2 should be the latter flow whose action @@ -225,7 +274,12 @@ static int nfp_ct_merge_check(struct nfp_fl_ct_flow_entry *entry1, goto check_failed; } - if (ovlp_keys & BIT(FLOW_DISSECTOR_KEY_IPV4_ADDRS)) { + /* if pre ct entry do nat, the nat ip exists in nft entry, + * will be do merge check when do nft and post ct merge, + * so skip this ip merge check here. + */ + if ((ovlp_keys & BIT(FLOW_DISSECTOR_KEY_IPV4_ADDRS)) && + nfp_ct_merge_check_cannot_skip(entry1, entry2)) { struct flow_match_ipv4_addrs match1, match2; flow_rule_match_ipv4_addrs(entry1->rule, &match1); @@ -242,7 +296,12 @@ static int nfp_ct_merge_check(struct nfp_fl_ct_flow_entry *entry1, goto check_failed; } - if (ovlp_keys & BIT(FLOW_DISSECTOR_KEY_IPV6_ADDRS)) { + /* if pre ct entry do nat, the nat ip exists in nft entry, + * will be do merge check when do nft and post ct merge, + * so skip this ip merge check here. + */ + if ((ovlp_keys & BIT(FLOW_DISSECTOR_KEY_IPV6_ADDRS)) && + nfp_ct_merge_check_cannot_skip(entry1, entry2)) { struct flow_match_ipv6_addrs match1, match2; flow_rule_match_ipv6_addrs(entry1->rule, &match1); @@ -259,7 +318,12 @@ static int nfp_ct_merge_check(struct nfp_fl_ct_flow_entry *entry1, goto check_failed; } - if (ovlp_keys & BIT(FLOW_DISSECTOR_KEY_PORTS)) { + /* if pre ct entry do nat, the nat tport exists in nft entry, + * will be do merge check when do nft and post ct merge, + * so skip this tport merge check here. + */ + if ((ovlp_keys & BIT(FLOW_DISSECTOR_KEY_PORTS)) && + nfp_ct_merge_check_cannot_skip(entry1, entry2)) { enum flow_action_mangle_base htype = FLOW_ACT_MANGLE_UNSPEC; struct flow_match_ports match1, match2; @@ -404,12 +468,55 @@ check_failed: return -EINVAL; } +static int nfp_ct_check_vlan_merge(struct flow_action_entry *a_in, + struct flow_rule *rule) +{ + struct flow_match_vlan match; + + if (unlikely(flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_CVLAN))) + return -EOPNOTSUPP; + + /* post_ct does not match VLAN KEY, can be merged. */ + if (likely(!flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_VLAN))) + return 0; + + switch (a_in->id) { + /* pre_ct has pop vlan, post_ct cannot match VLAN KEY, cannot be merged. */ + case FLOW_ACTION_VLAN_POP: + return -EOPNOTSUPP; + + case FLOW_ACTION_VLAN_PUSH: + case FLOW_ACTION_VLAN_MANGLE: + flow_rule_match_vlan(rule, &match); + /* different vlan id, cannot be merged. */ + if ((match.key->vlan_id & match.mask->vlan_id) ^ + (a_in->vlan.vid & match.mask->vlan_id)) + return -EOPNOTSUPP; + + /* different tpid, cannot be merged. */ + if ((match.key->vlan_tpid & match.mask->vlan_tpid) ^ + (a_in->vlan.proto & match.mask->vlan_tpid)) + return -EOPNOTSUPP; + + /* different priority, cannot be merged. */ + if ((match.key->vlan_priority & match.mask->vlan_priority) ^ + (a_in->vlan.prio & match.mask->vlan_priority)) + return -EOPNOTSUPP; + + break; + default: + return -EOPNOTSUPP; + } + + return 0; +} + static int nfp_ct_merge_act_check(struct nfp_fl_ct_flow_entry *pre_ct_entry, struct nfp_fl_ct_flow_entry *post_ct_entry, struct nfp_fl_ct_flow_entry *nft_entry) { struct flow_action_entry *act; - int i; + int i, err; /* Check for pre_ct->action conflicts */ flow_action_for_each(i, act, &pre_ct_entry->rule->action) { @@ -417,6 +524,10 @@ static int nfp_ct_merge_act_check(struct nfp_fl_ct_flow_entry *pre_ct_entry, case FLOW_ACTION_VLAN_PUSH: case FLOW_ACTION_VLAN_POP: case FLOW_ACTION_VLAN_MANGLE: + err = nfp_ct_check_vlan_merge(act, post_ct_entry->rule); + if (err) + return err; + break; case FLOW_ACTION_MPLS_PUSH: case FLOW_ACTION_MPLS_POP: case FLOW_ACTION_MPLS_MANGLE: @@ -468,6 +579,12 @@ static int nfp_ct_check_meta(struct nfp_fl_ct_flow_entry *post_ct_entry, return -EINVAL; return 0; + } else { + /* post_ct with ct clear action will not match the + * ct status when nft is nat entry. + */ + if (nft_entry->flags & NFP_FL_ACTION_DO_MANGLE) + return 0; } return -EINVAL; @@ -537,11 +654,37 @@ nfp_fl_calc_key_layers_sz(struct nfp_fl_key_ls in_key_ls, uint16_t *map) return key_size; } +/* get the csum flag according the ip proto and mangle action. */ +static void nfp_fl_get_csum_flag(struct flow_action_entry *a_in, u8 ip_proto, u32 *csum) +{ + if (a_in->id != FLOW_ACTION_MANGLE) + return; + + switch (a_in->mangle.htype) { + case FLOW_ACT_MANGLE_HDR_TYPE_IP4: + *csum |= TCA_CSUM_UPDATE_FLAG_IPV4HDR; + if (ip_proto == IPPROTO_TCP) + *csum |= TCA_CSUM_UPDATE_FLAG_TCP; + else if (ip_proto == IPPROTO_UDP) + *csum |= TCA_CSUM_UPDATE_FLAG_UDP; + break; + case FLOW_ACT_MANGLE_HDR_TYPE_TCP: + *csum |= TCA_CSUM_UPDATE_FLAG_TCP; + break; + case FLOW_ACT_MANGLE_HDR_TYPE_UDP: + *csum |= TCA_CSUM_UPDATE_FLAG_UDP; + break; + default: + break; + } +} + static int nfp_fl_merge_actions_offload(struct flow_rule **rules, struct nfp_flower_priv *priv, struct net_device *netdev, struct nfp_fl_payload *flow_pay) { + enum flow_action_hw_stats tmp_stats = FLOW_ACTION_HW_STATS_DONT_CARE; struct flow_action_entry *a_in; int i, j, num_actions, id; struct flow_rule *a_rule; @@ -551,15 +694,25 @@ static int nfp_fl_merge_actions_offload(struct flow_rule **rules, rules[CT_TYPE_NFT]->action.num_entries + rules[CT_TYPE_POST_CT]->action.num_entries; - a_rule = flow_rule_alloc(num_actions); + /* Add one action to make sure there is enough room to add an checksum action + * when do nat. + */ + a_rule = flow_rule_alloc(num_actions + 1); if (!a_rule) return -ENOMEM; /* Actions need a BASIC dissector. */ a_rule->match = rules[CT_TYPE_PRE_CT]->match; + /* post_ct entry have one action at least. */ + if (rules[CT_TYPE_POST_CT]->action.num_entries != 0) { + tmp_stats = rules[CT_TYPE_POST_CT]->action.entries[0].hw_stats; + } /* Copy actions */ for (j = 0; j < _CT_TYPE_MAX; j++) { + u32 csum_updated = 0; + u8 ip_proto = 0; + if (flow_rule_match_key(rules[j], FLOW_DISSECTOR_KEY_BASIC)) { struct flow_match_basic match; @@ -571,8 +724,10 @@ static int nfp_fl_merge_actions_offload(struct flow_rule **rules, * through the subflows and assign the proper subflow to a_rule */ flow_rule_match_basic(rules[j], &match); - if (match.mask->ip_proto) + if (match.mask->ip_proto) { a_rule->match = rules[j]->match; + ip_proto = match.key->ip_proto; + } } for (i = 0; i < rules[j]->action.num_entries; i++) { @@ -589,11 +744,32 @@ static int nfp_fl_merge_actions_offload(struct flow_rule **rules, case FLOW_ACTION_CT_METADATA: continue; default: + /* nft entry is generated by tc ct, which mangle action do not care + * the stats, inherit the post entry stats to meet the + * flow_action_hw_stats_check. + */ + if (j == CT_TYPE_NFT) { + if (a_in->hw_stats == FLOW_ACTION_HW_STATS_DONT_CARE) + a_in->hw_stats = tmp_stats; + nfp_fl_get_csum_flag(a_in, ip_proto, &csum_updated); + } memcpy(&a_rule->action.entries[offset++], a_in, sizeof(struct flow_action_entry)); break; } } + /* nft entry have mangle action, but do not have checksum action when do NAT, + * hardware will automatically fix IPv4 and TCP/UDP checksum. so add an csum action + * to meet csum action check. + */ + if (csum_updated) { + struct flow_action_entry *csum_action; + + csum_action = &a_rule->action.entries[offset++]; + csum_action->id = FLOW_ACTION_CSUM; + csum_action->csum_flags = csum_updated; + csum_action->hw_stats = tmp_stats; + } } /* Some actions would have been ignored, so update the num_entries field */ @@ -1191,6 +1367,49 @@ static struct net_device *get_netdev_from_rule(struct flow_rule *rule) return NULL; } +static void nfp_nft_ct_translate_mangle_action(struct flow_action_entry *mangle_action) +{ + if (mangle_action->id != FLOW_ACTION_MANGLE) + return; + + switch (mangle_action->mangle.htype) { + case FLOW_ACT_MANGLE_HDR_TYPE_IP4: + case FLOW_ACT_MANGLE_HDR_TYPE_IP6: + mangle_action->mangle.val = (__force u32)cpu_to_be32(mangle_action->mangle.val); + mangle_action->mangle.mask = (__force u32)cpu_to_be32(mangle_action->mangle.mask); + return; + + case FLOW_ACT_MANGLE_HDR_TYPE_TCP: + case FLOW_ACT_MANGLE_HDR_TYPE_UDP: + mangle_action->mangle.val = (__force u16)cpu_to_be16(mangle_action->mangle.val); + mangle_action->mangle.mask = (__force u16)cpu_to_be16(mangle_action->mangle.mask); + return; + + default: + return; + } +} + +static int nfp_nft_ct_set_flow_flag(struct flow_action_entry *act, + struct nfp_fl_ct_flow_entry *entry) +{ + switch (act->id) { + case FLOW_ACTION_CT: + if (act->ct.action == TCA_CT_ACT_NAT) + entry->flags |= NFP_FL_ACTION_DO_NAT; + break; + + case FLOW_ACTION_MANGLE: + entry->flags |= NFP_FL_ACTION_DO_MANGLE; + break; + + default: + break; + } + + return 0; +} + static struct nfp_fl_ct_flow_entry *nfp_fl_ct_add_flow(struct nfp_fl_ct_zone_entry *zt, struct net_device *netdev, @@ -1257,6 +1476,13 @@ nfp_fl_ct_flow_entry *nfp_fl_ct_add_flow(struct nfp_fl_ct_zone_entry *zt, new_act = &entry->rule->action.entries[i]; memcpy(new_act, act, sizeof(struct flow_action_entry)); + /* nft entry mangle field is host byte order, need translate to + * network byte order. + */ + if (is_nft) + nfp_nft_ct_translate_mangle_action(new_act); + + nfp_nft_ct_set_flow_flag(new_act, entry); /* Entunnel is a special case, need to allocate and copy * tunnel info. */ diff --git a/drivers/net/ethernet/netronome/nfp/flower/conntrack.h b/drivers/net/ethernet/netronome/nfp/flower/conntrack.h index beb6cceff9d8..762c0b36e269 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/conntrack.h +++ b/drivers/net/ethernet/netronome/nfp/flower/conntrack.h @@ -103,6 +103,10 @@ enum nfp_nfp_layer_name { _FLOW_PAY_LAYERS_MAX }; +/* NFP flow entry flags. */ +#define NFP_FL_ACTION_DO_NAT BIT(0) +#define NFP_FL_ACTION_DO_MANGLE BIT(1) + /** * struct nfp_fl_ct_flow_entry - Flow entry containing conntrack flow information * @cookie: Flow cookie, same as original TC flow, used as key @@ -115,6 +119,7 @@ enum nfp_nfp_layer_name { * @rule: Reference to the original TC flow rule * @stats: Used to cache stats for updating * @tun_offset: Used to indicate tunnel action offset in action list + * @flags: Used to indicate flow flag like NAT which used by merge. */ struct nfp_fl_ct_flow_entry { unsigned long cookie; @@ -127,6 +132,7 @@ struct nfp_fl_ct_flow_entry { struct flow_rule *rule; struct flow_stats stats; u8 tun_offset; // Set to NFP_FL_CT_NO_TUN if no tun + u8 flags; }; /** diff --git a/drivers/net/ethernet/netronome/nfp/flower/offload.c b/drivers/net/ethernet/netronome/nfp/flower/offload.c index 83c97154c0c7..3ab3e4536b99 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/offload.c +++ b/drivers/net/ethernet/netronome/nfp/flower/offload.c @@ -1301,9 +1301,14 @@ static bool offload_pre_check(struct flow_cls_offload *flow) { struct flow_rule *rule = flow_cls_offload_flow_rule(flow); struct flow_dissector *dissector = rule->match.dissector; + struct flow_match_ct ct; - if (dissector->used_keys & BIT(FLOW_DISSECTOR_KEY_CT)) - return false; + if (dissector->used_keys & BIT(FLOW_DISSECTOR_KEY_CT)) { + flow_rule_match_ct(rule, &ct); + /* Allow special case where CT match is all 0 */ + if (memchr_inv(ct.key, 0, sizeof(*ct.key))) + return false; + } if (flow->common.chain_index) return false; diff --git a/drivers/net/ethernet/netronome/nfp/flower/qos_conf.c b/drivers/net/ethernet/netronome/nfp/flower/qos_conf.c index 7b92026e1a6f..99052a925d9e 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/qos_conf.c +++ b/drivers/net/ethernet/netronome/nfp/flower/qos_conf.c @@ -119,7 +119,8 @@ int nfp_flower_offload_one_police(struct nfp_app *app, bool ingress, static int nfp_policer_validate(const struct flow_action *action, const struct flow_action_entry *act, - struct netlink_ext_ack *extack) + struct netlink_ext_ack *extack, + bool ingress) { if (act->police.exceed.act_id != FLOW_ACTION_DROP) { NL_SET_ERR_MSG_MOD(extack, @@ -127,12 +128,20 @@ static int nfp_policer_validate(const struct flow_action *action, return -EOPNOTSUPP; } - if (act->police.notexceed.act_id != FLOW_ACTION_CONTINUE && - act->police.notexceed.act_id != FLOW_ACTION_PIPE && - act->police.notexceed.act_id != FLOW_ACTION_ACCEPT) { - NL_SET_ERR_MSG_MOD(extack, - "Offload not supported when conform action is not continue, pipe or ok"); - return -EOPNOTSUPP; + if (ingress) { + if (act->police.notexceed.act_id != FLOW_ACTION_CONTINUE && + act->police.notexceed.act_id != FLOW_ACTION_ACCEPT) { + NL_SET_ERR_MSG_MOD(extack, + "Offload not supported when conform action is not continue or ok"); + return -EOPNOTSUPP; + } + } else { + if (act->police.notexceed.act_id != FLOW_ACTION_PIPE && + act->police.notexceed.act_id != FLOW_ACTION_ACCEPT) { + NL_SET_ERR_MSG_MOD(extack, + "Offload not supported when conform action is not pipe or ok"); + return -EOPNOTSUPP; + } } if (act->police.notexceed.act_id == FLOW_ACTION_ACCEPT && @@ -218,7 +227,7 @@ nfp_flower_install_rate_limiter(struct nfp_app *app, struct net_device *netdev, return -EOPNOTSUPP; } - err = nfp_policer_validate(&flow->rule->action, action, extack); + err = nfp_policer_validate(&flow->rule->action, action, extack, true); if (err) return err; @@ -687,6 +696,7 @@ nfp_act_install_actions(struct nfp_app *app, struct flow_offload_action *fl_act, bool pps_support, pps; bool add = false; u64 rate; + int err; pps_support = !!(fl_priv->flower_ext_feats & NFP_FL_FEATS_QOS_PPS); @@ -698,6 +708,11 @@ nfp_act_install_actions(struct nfp_app *app, struct flow_offload_action *fl_act, "unsupported offload: qos rate limit offload requires police action"); continue; } + + err = nfp_policer_validate(&fl_act->action, action, extack, false); + if (err) + return err; + if (action->police.rate_bytes_ps > 0) { rate = action->police.rate_bytes_ps; burst = action->police.burst; diff --git a/drivers/net/ethernet/netronome/nfp/nfd3/xsk.c b/drivers/net/ethernet/netronome/nfp/nfd3/xsk.c index 65e243168765..5d9db8c2a5b4 100644 --- a/drivers/net/ethernet/netronome/nfp/nfd3/xsk.c +++ b/drivers/net/ethernet/netronome/nfp/nfd3/xsk.c @@ -84,7 +84,7 @@ static void nfp_nfd3_xsk_rx_skb(struct nfp_net_rx_ring *rx_ring, nfp_net_xsk_rx_drop(r_vec, xrxbuf); return; } - memcpy(skb_put(skb, pkt_len), xrxbuf->xdp->data, pkt_len); + skb_put_data(skb, xrxbuf->xdp->data, pkt_len); skb->mark = meta->mark; skb_set_hash(skb, meta->hash, meta->hash_type); diff --git a/drivers/net/ethernet/netronome/nfp/nfp_main.c b/drivers/net/ethernet/netronome/nfp/nfp_main.c index 873429f7a6da..e66e548919d4 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_main.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_main.c @@ -691,6 +691,71 @@ static int nfp_pf_find_rtsyms(struct nfp_pf *pf) return 0; } +int nfp_net_pf_get_app_id(struct nfp_pf *pf) +{ + return nfp_pf_rtsym_read_optional(pf, "_pf%u_net_app_id", + NFP_APP_CORE_NIC); +} + +static u64 nfp_net_pf_get_app_cap(struct nfp_pf *pf) +{ + char name[32]; + int err = 0; + u64 val; + + snprintf(name, sizeof(name), "_pf%u_net_app_cap", nfp_cppcore_pcie_unit(pf->cpp)); + + val = nfp_rtsym_read_le(pf->rtbl, name, &err); + if (err) { + if (err != -ENOENT) + nfp_err(pf->cpp, "Unable to read symbol %s\n", name); + + return 0; + } + + return val; +} + +static int nfp_pf_cfg_hwinfo(struct nfp_pf *pf, bool sp_indiff) +{ + struct nfp_nsp *nsp; + char hwinfo[32]; + int err; + + nsp = nfp_nsp_open(pf->cpp); + if (IS_ERR(nsp)) + return PTR_ERR(nsp); + + snprintf(hwinfo, sizeof(hwinfo), "sp_indiff=%d", sp_indiff); + err = nfp_nsp_hwinfo_set(nsp, hwinfo, sizeof(hwinfo)); + /* Not a fatal error, no need to return error to stop driver from loading */ + if (err) { + nfp_warn(pf->cpp, "HWinfo(sp_indiff=%d) set failed: %d\n", sp_indiff, err); + } else { + /* Need reinit eth_tbl since the eth table state may change + * after sp_indiff is configured. + */ + kfree(pf->eth_tbl); + pf->eth_tbl = __nfp_eth_read_ports(pf->cpp, nsp); + } + + nfp_nsp_close(nsp); + return 0; +} + +static int nfp_pf_nsp_cfg(struct nfp_pf *pf) +{ + bool sp_indiff = (nfp_net_pf_get_app_id(pf) == NFP_APP_FLOWER_NIC) || + (nfp_net_pf_get_app_cap(pf) & NFP_NET_APP_CAP_SP_INDIFF); + + return nfp_pf_cfg_hwinfo(pf, sp_indiff); +} + +static void nfp_pf_nsp_clean(struct nfp_pf *pf) +{ + nfp_pf_cfg_hwinfo(pf, false); +} + static int nfp_pci_probe(struct pci_dev *pdev, const struct pci_device_id *pci_id) { @@ -791,10 +856,14 @@ static int nfp_pci_probe(struct pci_dev *pdev, goto err_fw_unload; } - err = nfp_net_pci_probe(pf); + err = nfp_pf_nsp_cfg(pf); if (err) goto err_fw_unload; + err = nfp_net_pci_probe(pf); + if (err) + goto err_nsp_clean; + err = nfp_hwmon_register(pf); if (err) { dev_err(&pdev->dev, "Failed to register hwmon info\n"); @@ -805,6 +874,8 @@ static int nfp_pci_probe(struct pci_dev *pdev, err_net_remove: nfp_net_pci_remove(pf); +err_nsp_clean: + nfp_pf_nsp_clean(pf); err_fw_unload: kfree(pf->rtbl); nfp_mip_close(pf->mip); @@ -844,6 +915,7 @@ static void __nfp_pci_shutdown(struct pci_dev *pdev, bool unload_fw) nfp_net_pci_remove(pf); + nfp_pf_nsp_clean(pf); vfree(pf->dumpspec); kfree(pf->rtbl); nfp_mip_close(pf->mip); diff --git a/drivers/net/ethernet/netronome/nfp/nfp_main.h b/drivers/net/ethernet/netronome/nfp/nfp_main.h index f56ca11de134..afd3edfa2428 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_main.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_main.h @@ -161,6 +161,7 @@ bool nfp_ctrl_tx(struct nfp_net *nn, struct sk_buff *skb); int nfp_pf_rtsym_read_optional(struct nfp_pf *pf, const char *format, unsigned int default_val); +int nfp_net_pf_get_app_id(struct nfp_pf *pf); u8 __iomem * nfp_pf_map_rtsym(struct nfp_pf *pf, const char *name, const char *sym_fmt, unsigned int min_size, struct nfp_cpp_area **area); @@ -190,4 +191,7 @@ int nfp_shared_buf_pool_set(struct nfp_pf *pf, unsigned int sb, int nfp_devlink_params_register(struct nfp_pf *pf); void nfp_devlink_params_unregister(struct nfp_pf *pf); + +unsigned int nfp_net_lr2speed(unsigned int linkrate); +unsigned int nfp_net_speed2lr(unsigned int speed); #endif /* NFP_MAIN_H */ diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c index 349a2b1a19a2..27f4786ace4f 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c @@ -474,19 +474,22 @@ static void nfp_net_read_link_status(struct nfp_net *nn) { unsigned long flags; bool link_up; - u32 sts; + u16 sts; spin_lock_irqsave(&nn->link_status_lock, flags); - sts = nn_readl(nn, NFP_NET_CFG_STS); + sts = nn_readw(nn, NFP_NET_CFG_STS); link_up = !!(sts & NFP_NET_CFG_STS_LINK); if (nn->link_up == link_up) goto out; nn->link_up = link_up; - if (nn->port) + if (nn->port) { set_bit(NFP_PORT_CHANGED, &nn->port->flags); + if (nn->port->link_cb) + nn->port->link_cb(nn->port); + } if (nn->link_up) { netif_carrier_on(nn->dp.netdev); @@ -768,9 +771,7 @@ nfp_net_napi_add(struct nfp_net_dp *dp, struct nfp_net_r_vector *r_vec, int idx) { if (dp->netdev) netif_napi_add(dp->netdev, &r_vec->napi, - nfp_net_has_xsk_pool_slow(dp, idx) ? - dp->ops->xsk_poll : dp->ops->poll, - NAPI_POLL_WEIGHT); + nfp_net_has_xsk_pool_slow(dp, idx) ? dp->ops->xsk_poll : dp->ops->poll); else tasklet_enable(&r_vec->tasklet); } diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.h b/drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.h index ac05ec34d69e..6714d5e8fdab 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.h @@ -14,6 +14,9 @@ #include <linux/types.h> +/* 64-bit per app capabilities */ +#define NFP_NET_APP_CAP_SP_INDIFF BIT_ULL(0) /* indifferent to port speed */ + /* Configuration BAR size. * * The configuration BAR is 8K in size, but due to @@ -193,6 +196,10 @@ #define NFP_NET_CFG_STS_LINK_RATE_40G 5 #define NFP_NET_CFG_STS_LINK_RATE_50G 6 #define NFP_NET_CFG_STS_LINK_RATE_100G 7 +/* NSP Link rate is a 16-bit word. It's determined by NSP and + * written to CFG BAR by NFP driver. + */ +#define NFP_NET_CFG_STS_NSP_LINK_RATE 0x0036 #define NFP_NET_CFG_CAP 0x0038 #define NFP_NET_CFG_MAX_TXRINGS 0x003c #define NFP_NET_CFG_MAX_RXRINGS 0x0040 diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c index b1b1b648e40c..22a5d2419084 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c @@ -205,7 +205,7 @@ nfp_get_drvinfo(struct nfp_app *app, struct pci_dev *pdev, { char nsp_version[ETHTOOL_FWVERS_LEN] = {}; - strlcpy(drvinfo->driver, dev_driver_string(&pdev->dev), + strscpy(drvinfo->driver, dev_driver_string(&pdev->dev), sizeof(drvinfo->driver)); nfp_net_get_nspinfo(app, nsp_version); snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version), @@ -222,18 +222,49 @@ nfp_net_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *drvinfo) snprintf(vnic_version, sizeof(vnic_version), "%d.%d.%d.%d", nn->fw_ver.extend, nn->fw_ver.class, nn->fw_ver.major, nn->fw_ver.minor); - strlcpy(drvinfo->bus_info, pci_name(nn->pdev), + strscpy(drvinfo->bus_info, pci_name(nn->pdev), sizeof(drvinfo->bus_info)); nfp_get_drvinfo(nn->app, nn->pdev, vnic_version, drvinfo); } +static int +nfp_net_nway_reset(struct net_device *netdev) +{ + struct nfp_eth_table_port *eth_port; + struct nfp_port *port; + int err; + + port = nfp_port_from_netdev(netdev); + eth_port = nfp_port_get_eth_port(port); + if (!eth_port) + return -EOPNOTSUPP; + + if (!netif_running(netdev)) + return 0; + + err = nfp_eth_set_configured(port->app->cpp, eth_port->index, false); + if (err) { + netdev_info(netdev, "Link down failed: %d\n", err); + return err; + } + + err = nfp_eth_set_configured(port->app->cpp, eth_port->index, true); + if (err) { + netdev_info(netdev, "Link up failed: %d\n", err); + return err; + } + + netdev_info(netdev, "Link reset succeeded\n"); + return 0; +} + static void nfp_app_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *drvinfo) { struct nfp_app *app = nfp_app_from_netdev(netdev); - strlcpy(drvinfo->bus_info, pci_name(app->pdev), + strscpy(drvinfo->bus_info, pci_name(app->pdev), sizeof(drvinfo->bus_info)); nfp_get_drvinfo(app, app->pdev, "*", drvinfo); } @@ -273,20 +304,11 @@ static int nfp_net_get_link_ksettings(struct net_device *netdev, struct ethtool_link_ksettings *cmd) { - static const u32 ls_to_ethtool[] = { - [NFP_NET_CFG_STS_LINK_RATE_UNSUPPORTED] = 0, - [NFP_NET_CFG_STS_LINK_RATE_UNKNOWN] = SPEED_UNKNOWN, - [NFP_NET_CFG_STS_LINK_RATE_1G] = SPEED_1000, - [NFP_NET_CFG_STS_LINK_RATE_10G] = SPEED_10000, - [NFP_NET_CFG_STS_LINK_RATE_25G] = SPEED_25000, - [NFP_NET_CFG_STS_LINK_RATE_40G] = SPEED_40000, - [NFP_NET_CFG_STS_LINK_RATE_50G] = SPEED_50000, - [NFP_NET_CFG_STS_LINK_RATE_100G] = SPEED_100000, - }; struct nfp_eth_table_port *eth_port; struct nfp_port *port; struct nfp_net *nn; - u32 sts, ls; + unsigned int speed; + u16 sts; /* Init to unknowns */ ethtool_link_ksettings_add_link_mode(cmd, supported, FIBRE); @@ -299,8 +321,13 @@ nfp_net_get_link_ksettings(struct net_device *netdev, if (eth_port) { ethtool_link_ksettings_add_link_mode(cmd, supported, Pause); ethtool_link_ksettings_add_link_mode(cmd, advertising, Pause); - cmd->base.autoneg = eth_port->aneg != NFP_ANEG_DISABLED ? - AUTONEG_ENABLE : AUTONEG_DISABLE; + if (eth_port->supp_aneg) { + ethtool_link_ksettings_add_link_mode(cmd, supported, Autoneg); + if (eth_port->aneg == NFP_ANEG_AUTO) { + ethtool_link_ksettings_add_link_mode(cmd, advertising, Autoneg); + cmd->base.autoneg = AUTONEG_ENABLE; + } + } nfp_net_set_fec_link_mode(eth_port, cmd); } @@ -319,18 +346,15 @@ nfp_net_get_link_ksettings(struct net_device *netdev, return -EOPNOTSUPP; nn = netdev_priv(netdev); - sts = nn_readl(nn, NFP_NET_CFG_STS); - - ls = FIELD_GET(NFP_NET_CFG_STS_LINK_RATE, sts); - if (ls == NFP_NET_CFG_STS_LINK_RATE_UNSUPPORTED) + sts = nn_readw(nn, NFP_NET_CFG_STS); + speed = nfp_net_lr2speed(FIELD_GET(NFP_NET_CFG_STS_LINK_RATE, sts)); + if (!speed) return -EOPNOTSUPP; - if (ls == NFP_NET_CFG_STS_LINK_RATE_UNKNOWN || - ls >= ARRAY_SIZE(ls_to_ethtool)) - return 0; - - cmd->base.speed = ls_to_ethtool[ls]; - cmd->base.duplex = DUPLEX_FULL; + if (speed != SPEED_UNKNOWN) { + cmd->base.speed = speed; + cmd->base.duplex = DUPLEX_FULL; + } return 0; } @@ -339,6 +363,7 @@ static int nfp_net_set_link_ksettings(struct net_device *netdev, const struct ethtool_link_ksettings *cmd) { + bool req_aneg = (cmd->base.autoneg == AUTONEG_ENABLE); struct nfp_eth_table_port *eth_port; struct nfp_port *port; struct nfp_nsp *nsp; @@ -358,13 +383,25 @@ nfp_net_set_link_ksettings(struct net_device *netdev, if (IS_ERR(nsp)) return PTR_ERR(nsp); - err = __nfp_eth_set_aneg(nsp, cmd->base.autoneg == AUTONEG_ENABLE ? - NFP_ANEG_AUTO : NFP_ANEG_DISABLED); + if (req_aneg && !eth_port->supp_aneg) { + netdev_warn(netdev, "Autoneg is not supported.\n"); + err = -EOPNOTSUPP; + goto err_bad_set; + } + + err = __nfp_eth_set_aneg(nsp, req_aneg ? NFP_ANEG_AUTO : NFP_ANEG_DISABLED); if (err) goto err_bad_set; + if (cmd->base.speed != SPEED_UNKNOWN) { u32 speed = cmd->base.speed / eth_port->lanes; + if (req_aneg) { + netdev_err(netdev, "Speed changing is not allowed when working on autoneg mode.\n"); + err = -EINVAL; + goto err_bad_set; + } + err = __nfp_eth_set_speed(nsp, speed); if (err) goto err_bad_set; @@ -1008,7 +1045,7 @@ nfp_port_get_fecparam(struct net_device *netdev, return 0; param->fec = nfp_port_fec_nsp_to_ethtool(eth_port->fec_modes_supported); - param->active_fec = nfp_port_fec_nsp_to_ethtool(eth_port->fec); + param->active_fec = nfp_port_fec_nsp_to_ethtool(BIT(eth_port->act_fec)); return 0; } @@ -1676,11 +1713,166 @@ static int nfp_net_set_phys_id(struct net_device *netdev, return err; } +#define NFP_EEPROM_LEN ETH_ALEN + +static int +nfp_net_get_eeprom_len(struct net_device *netdev) +{ + struct nfp_eth_table_port *eth_port; + struct nfp_port *port; + + port = nfp_port_from_netdev(netdev); + eth_port = __nfp_port_get_eth_port(port); + if (!eth_port) + return 0; + + return NFP_EEPROM_LEN; +} + +static int +nfp_net_get_nsp_hwindex(struct net_device *netdev, + struct nfp_nsp **nspptr, + u32 *index) +{ + struct nfp_eth_table_port *eth_port; + struct nfp_port *port; + struct nfp_nsp *nsp; + int err; + + port = nfp_port_from_netdev(netdev); + eth_port = __nfp_port_get_eth_port(port); + if (!eth_port) + return -EOPNOTSUPP; + + nsp = nfp_nsp_open(port->app->cpp); + if (IS_ERR(nsp)) { + err = PTR_ERR(nsp); + netdev_err(netdev, "Failed to access the NSP: %d\n", err); + return err; + } + + if (!nfp_nsp_has_hwinfo_lookup(nsp)) { + netdev_err(netdev, "NSP doesn't support PF MAC generation\n"); + nfp_nsp_close(nsp); + return -EOPNOTSUPP; + } + + *nspptr = nsp; + *index = eth_port->eth_index; + + return 0; +} + +static int +nfp_net_get_port_mac_by_hwinfo(struct net_device *netdev, + u8 *mac_addr) +{ + char hwinfo[32] = {}; + struct nfp_nsp *nsp; + u32 index; + int err; + + err = nfp_net_get_nsp_hwindex(netdev, &nsp, &index); + if (err) + return err; + + snprintf(hwinfo, sizeof(hwinfo), "eth%u.mac", index); + err = nfp_nsp_hwinfo_lookup(nsp, hwinfo, sizeof(hwinfo)); + nfp_nsp_close(nsp); + if (err) { + netdev_err(netdev, "Reading persistent MAC address failed: %d\n", + err); + return -EOPNOTSUPP; + } + + if (sscanf(hwinfo, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx", + &mac_addr[0], &mac_addr[1], &mac_addr[2], + &mac_addr[3], &mac_addr[4], &mac_addr[5]) != 6) { + netdev_err(netdev, "Can't parse persistent MAC address (%s)\n", + hwinfo); + return -EOPNOTSUPP; + } + + return 0; +} + +static int +nfp_net_set_port_mac_by_hwinfo(struct net_device *netdev, + u8 *mac_addr) +{ + char hwinfo[32] = {}; + struct nfp_nsp *nsp; + u32 index; + int err; + + err = nfp_net_get_nsp_hwindex(netdev, &nsp, &index); + if (err) + return err; + + snprintf(hwinfo, sizeof(hwinfo), + "eth%u.mac=%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx", + index, mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], + mac_addr[4], mac_addr[5]); + + err = nfp_nsp_hwinfo_set(nsp, hwinfo, sizeof(hwinfo)); + nfp_nsp_close(nsp); + if (err) { + netdev_err(netdev, "HWinfo set failed: %d, hwinfo: %s\n", + err, hwinfo); + return -EOPNOTSUPP; + } + + return 0; +} + +static int +nfp_net_get_eeprom(struct net_device *netdev, + struct ethtool_eeprom *eeprom, u8 *bytes) +{ + struct nfp_net *nn = netdev_priv(netdev); + u8 buf[NFP_EEPROM_LEN] = {}; + + if (eeprom->len == 0) + return -EINVAL; + + if (nfp_net_get_port_mac_by_hwinfo(netdev, buf)) + return -EOPNOTSUPP; + + eeprom->magic = nn->pdev->vendor | (nn->pdev->device << 16); + memcpy(bytes, buf + eeprom->offset, eeprom->len); + + return 0; +} + +static int +nfp_net_set_eeprom(struct net_device *netdev, + struct ethtool_eeprom *eeprom, u8 *bytes) +{ + struct nfp_net *nn = netdev_priv(netdev); + u8 buf[NFP_EEPROM_LEN] = {}; + + if (eeprom->len == 0) + return -EINVAL; + + if (eeprom->magic != (nn->pdev->vendor | nn->pdev->device << 16)) + return -EINVAL; + + if (nfp_net_get_port_mac_by_hwinfo(netdev, buf)) + return -EOPNOTSUPP; + + memcpy(buf + eeprom->offset, bytes, eeprom->len); + if (nfp_net_set_port_mac_by_hwinfo(netdev, buf)) + return -EOPNOTSUPP; + + return 0; +} + static const struct ethtool_ops nfp_net_ethtool_ops = { .supported_coalesce_params = ETHTOOL_COALESCE_USECS | ETHTOOL_COALESCE_MAX_FRAMES | ETHTOOL_COALESCE_USE_ADAPTIVE, .get_drvinfo = nfp_net_get_drvinfo, + .nway_reset = nfp_net_nway_reset, .get_link = ethtool_op_get_link, .get_ringparam = nfp_net_get_ringparam, .set_ringparam = nfp_net_set_ringparam, @@ -1699,6 +1891,9 @@ static const struct ethtool_ops nfp_net_ethtool_ops = { .set_dump = nfp_app_set_dump, .get_dump_flag = nfp_app_get_dump_flag, .get_dump_data = nfp_app_get_dump_data, + .get_eeprom_len = nfp_net_get_eeprom_len, + .get_eeprom = nfp_net_get_eeprom, + .set_eeprom = nfp_net_set_eeprom, .get_module_info = nfp_port_get_module_info, .get_module_eeprom = nfp_port_get_module_eeprom, .get_coalesce = nfp_net_get_coalesce, @@ -1715,6 +1910,7 @@ static const struct ethtool_ops nfp_net_ethtool_ops = { const struct ethtool_ops nfp_port_ethtool_ops = { .get_drvinfo = nfp_app_get_drvinfo, + .nway_reset = nfp_net_nway_reset, .get_link = ethtool_op_get_link, .get_strings = nfp_port_get_strings, .get_ethtool_stats = nfp_port_get_stats, diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_main.c b/drivers/net/ethernet/netronome/nfp/nfp_net_main.c index ca4e05650fe6..3bae92dc899e 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_main.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_main.c @@ -77,12 +77,6 @@ static int nfp_net_pf_get_num_ports(struct nfp_pf *pf) return nfp_pf_rtsym_read_optional(pf, "nfd_cfg_pf%u_num_ports", 1); } -static int nfp_net_pf_get_app_id(struct nfp_pf *pf) -{ - return nfp_pf_rtsym_read_optional(pf, "_pf%u_net_app_id", - NFP_APP_CORE_NIC); -} - static void nfp_net_pf_free_vnic(struct nfp_pf *pf, struct nfp_net *nn) { if (nfp_net_is_data_vnic(nn)) @@ -202,6 +196,9 @@ nfp_net_pf_alloc_vnics(struct nfp_pf *pf, void __iomem *ctrl_bar, goto err_free_prev; } + if (nn->port) + nn->port->link_cb = nfp_net_refresh_port_table; + ctrl_bar += NFP_PF_CSR_SLICE_SIZE; /* Kill the vNIC if app init marked it as invalid */ @@ -523,6 +520,57 @@ err_unmap_ctrl: return err; } +static const unsigned int lr_to_speed[] = { + [NFP_NET_CFG_STS_LINK_RATE_UNSUPPORTED] = 0, + [NFP_NET_CFG_STS_LINK_RATE_UNKNOWN] = SPEED_UNKNOWN, + [NFP_NET_CFG_STS_LINK_RATE_1G] = SPEED_1000, + [NFP_NET_CFG_STS_LINK_RATE_10G] = SPEED_10000, + [NFP_NET_CFG_STS_LINK_RATE_25G] = SPEED_25000, + [NFP_NET_CFG_STS_LINK_RATE_40G] = SPEED_40000, + [NFP_NET_CFG_STS_LINK_RATE_50G] = SPEED_50000, + [NFP_NET_CFG_STS_LINK_RATE_100G] = SPEED_100000, +}; + +unsigned int nfp_net_lr2speed(unsigned int linkrate) +{ + if (linkrate < ARRAY_SIZE(lr_to_speed)) + return lr_to_speed[linkrate]; + + return SPEED_UNKNOWN; +} + +unsigned int nfp_net_speed2lr(unsigned int speed) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(lr_to_speed); i++) { + if (speed == lr_to_speed[i]) + return i; + } + + return NFP_NET_CFG_STS_LINK_RATE_UNKNOWN; +} + +static void nfp_net_notify_port_speed(struct nfp_port *port) +{ + struct net_device *netdev = port->netdev; + struct nfp_net *nn; + u16 sts; + + if (!nfp_netdev_is_nfp_net(netdev)) + return; + + nn = netdev_priv(netdev); + sts = nn_readw(nn, NFP_NET_CFG_STS); + + if (!(sts & NFP_NET_CFG_STS_LINK)) { + nn_writew(nn, NFP_NET_CFG_STS_NSP_LINK_RATE, NFP_NET_CFG_STS_LINK_RATE_UNKNOWN); + return; + } + + nn_writew(nn, NFP_NET_CFG_STS_NSP_LINK_RATE, nfp_net_speed2lr(port->eth_port->speed)); +} + static int nfp_net_eth_port_update(struct nfp_cpp *cpp, struct nfp_port *port, struct nfp_eth_table *eth_table) @@ -544,6 +592,7 @@ nfp_net_eth_port_update(struct nfp_cpp *cpp, struct nfp_port *port, } memcpy(port->eth_port, eth_port, sizeof(*eth_port)); + nfp_net_notify_port_speed(port); return 0; } diff --git a/drivers/net/ethernet/netronome/nfp/nfp_port.h b/drivers/net/ethernet/netronome/nfp/nfp_port.h index d1ebe6c72f7f..6793cdf9ff11 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_port.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_port.h @@ -46,6 +46,7 @@ enum nfp_port_flags { * @tc_offload_cnt: number of active TC offloads, how offloads are counted * is not defined, use as a boolean * @app: backpointer to the app structure + * @link_cb: callback when link status changed * @dl_port: devlink port structure * @eth_id: for %NFP_PORT_PHYS_PORT port ID in NFP enumeration scheme * @eth_forced: for %NFP_PORT_PHYS_PORT port is forced UP or DOWN, don't change @@ -66,6 +67,7 @@ struct nfp_port { unsigned long tc_offload_cnt; struct nfp_app *app; + void (*link_cb)(struct nfp_port *port); struct devlink_port dl_port; diff --git a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.h b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.h index 77d66855be42..992d72ac98d3 100644 --- a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.h +++ b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.h @@ -132,6 +132,7 @@ enum nfp_eth_fec { * @ports.interface: interface (module) plugged in * @ports.media: media type of the @interface * @ports.fec: forward error correction mode + * @ports.act_fec: active forward error correction mode * @ports.aneg: auto negotiation mode * @ports.mac_addr: interface MAC address * @ports.label_port: port id @@ -162,6 +163,7 @@ struct nfp_eth_table { enum nfp_eth_media media; enum nfp_eth_fec fec; + enum nfp_eth_fec act_fec; enum nfp_eth_aneg aneg; u8 mac_addr[ETH_ALEN]; @@ -172,6 +174,7 @@ struct nfp_eth_table { bool enabled; bool tx_enabled; bool rx_enabled; + bool supp_aneg; bool override_changed; diff --git a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.c b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.c index 4cc38799eabc..bb64efec4c46 100644 --- a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.c +++ b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.c @@ -27,6 +27,7 @@ #define NSP_ETH_PORT_PHYLABEL GENMASK_ULL(59, 54) #define NSP_ETH_PORT_FEC_SUPP_BASER BIT_ULL(60) #define NSP_ETH_PORT_FEC_SUPP_RS BIT_ULL(61) +#define NSP_ETH_PORT_SUPP_ANEG BIT_ULL(63) #define NSP_ETH_PORT_LANES_MASK cpu_to_le64(NSP_ETH_PORT_LANES) @@ -40,6 +41,7 @@ #define NSP_ETH_STATE_OVRD_CHNG BIT_ULL(22) #define NSP_ETH_STATE_ANEG GENMASK_ULL(25, 23) #define NSP_ETH_STATE_FEC GENMASK_ULL(27, 26) +#define NSP_ETH_STATE_ACT_FEC GENMASK_ULL(29, 28) #define NSP_ETH_CTRL_CONFIGURED BIT_ULL(0) #define NSP_ETH_CTRL_ENABLED BIT_ULL(1) @@ -170,7 +172,14 @@ nfp_eth_port_translate(struct nfp_nsp *nsp, const union eth_table_entry *src, if (dst->fec_modes_supported) dst->fec_modes_supported |= NFP_FEC_AUTO | NFP_FEC_DISABLED; - dst->fec = 1 << FIELD_GET(NSP_ETH_STATE_FEC, state); + dst->fec = FIELD_GET(NSP_ETH_STATE_FEC, state); + dst->act_fec = dst->fec; + + if (nfp_nsp_get_abi_ver_minor(nsp) < 33) + return; + + dst->act_fec = FIELD_GET(NSP_ETH_STATE_ACT_FEC, state); + dst->supp_aneg = FIELD_GET(NSP_ETH_PORT_SUPP_ANEG, port); } static void diff --git a/drivers/net/ethernet/ni/nixge.c b/drivers/net/ethernet/ni/nixge.c index 4b3482ce90a1..3db4a2431741 100644 --- a/drivers/net/ethernet/ni/nixge.c +++ b/drivers/net/ethernet/ni/nixge.c @@ -990,8 +990,8 @@ static const struct net_device_ops nixge_netdev_ops = { static void nixge_ethtools_get_drvinfo(struct net_device *ndev, struct ethtool_drvinfo *ed) { - strlcpy(ed->driver, "nixge", sizeof(ed->driver)); - strlcpy(ed->bus_info, "platform", sizeof(ed->bus_info)); + strscpy(ed->driver, "nixge", sizeof(ed->driver)); + strscpy(ed->bus_info, "platform", sizeof(ed->bus_info)); } static int @@ -1294,7 +1294,7 @@ static int nixge_probe(struct platform_device *pdev) priv->ndev = ndev; priv->dev = &pdev->dev; - netif_napi_add(ndev, &priv->napi, nixge_poll, NAPI_POLL_WEIGHT); + netif_napi_add(ndev, &priv->napi, nixge_poll); err = nixge_of_get_resources(pdev); if (err) goto free_netdev; diff --git a/drivers/net/ethernet/nvidia/forcedeth.c b/drivers/net/ethernet/nvidia/forcedeth.c index 5116badaf091..daa028729d44 100644 --- a/drivers/net/ethernet/nvidia/forcedeth.c +++ b/drivers/net/ethernet/nvidia/forcedeth.c @@ -4291,9 +4291,9 @@ static void nv_do_stats_poll(struct timer_list *t) static void nv_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { struct fe_priv *np = netdev_priv(dev); - strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); - strlcpy(info->version, FORCEDETH_VERSION, sizeof(info->version)); - strlcpy(info->bus_info, pci_name(np->pci_dev), sizeof(info->bus_info)); + strscpy(info->driver, DRV_NAME, sizeof(info->driver)); + strscpy(info->version, FORCEDETH_VERSION, sizeof(info->version)); + strscpy(info->bus_info, pci_name(np->pci_dev), sizeof(info->bus_info)); } static void nv_get_wol(struct net_device *dev, struct ethtool_wolinfo *wolinfo) @@ -5876,7 +5876,7 @@ static int nv_probe(struct pci_dev *pci_dev, const struct pci_device_id *id) else dev->netdev_ops = &nv_netdev_ops_optimized; - netif_napi_add(dev, &np->napi, nv_napi_poll, NAPI_POLL_WEIGHT); + netif_napi_add(dev, &np->napi, nv_napi_poll); dev->ethtool_ops = &ops; dev->watchdog_timeo = NV_WATCHDOG_TIMEO; diff --git a/drivers/net/ethernet/nxp/lpc_eth.c b/drivers/net/ethernet/nxp/lpc_eth.c index f606d75b33b4..1a4a272f4c5c 100644 --- a/drivers/net/ethernet/nxp/lpc_eth.c +++ b/drivers/net/ethernet/nxp/lpc_eth.c @@ -1184,9 +1184,9 @@ static int lpc_eth_open(struct net_device *ndev) static void lpc_eth_ethtool_getdrvinfo(struct net_device *ndev, struct ethtool_drvinfo *info) { - strlcpy(info->driver, MODNAME, sizeof(info->driver)); - strlcpy(info->version, DRV_VERSION, sizeof(info->version)); - strlcpy(info->bus_info, dev_name(ndev->dev.parent), + strscpy(info->driver, MODNAME, sizeof(info->driver)); + strscpy(info->version, DRV_VERSION, sizeof(info->version)); + strscpy(info->bus_info, dev_name(ndev->dev.parent), sizeof(info->bus_info)); } diff --git a/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_ethtool.c b/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_ethtool.c index 84cc79e928c8..541b8bcd3223 100644 --- a/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_ethtool.c +++ b/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_ethtool.c @@ -169,9 +169,9 @@ static void pch_gbe_get_drvinfo(struct net_device *netdev, { struct pch_gbe_adapter *adapter = netdev_priv(netdev); - strlcpy(drvinfo->driver, KBUILD_MODNAME, sizeof(drvinfo->driver)); - strlcpy(drvinfo->version, pch_driver_version, sizeof(drvinfo->version)); - strlcpy(drvinfo->bus_info, pci_name(adapter->pdev), + strscpy(drvinfo->driver, KBUILD_MODNAME, sizeof(drvinfo->driver)); + strscpy(drvinfo->version, pch_driver_version, sizeof(drvinfo->version)); + strscpy(drvinfo->bus_info, pci_name(adapter->pdev), sizeof(drvinfo->bus_info)); } diff --git a/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c b/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c index 46da937ad27f..3f2c30184752 100644 --- a/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c +++ b/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c @@ -2516,8 +2516,7 @@ static int pch_gbe_probe(struct pci_dev *pdev, netdev->netdev_ops = &pch_gbe_netdev_ops; netdev->watchdog_timeo = PCH_GBE_WATCHDOG_PERIOD; - netif_napi_add(netdev, &adapter->napi, - pch_gbe_napi_poll, NAPI_POLL_WEIGHT); + netif_napi_add(netdev, &adapter->napi, pch_gbe_napi_poll); netdev->hw_features = NETIF_F_RXCSUM | NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM; netdev->features = netdev->hw_features; diff --git a/drivers/net/ethernet/packetengines/hamachi.c b/drivers/net/ethernet/packetengines/hamachi.c index 9c408328be0d..1cc001087193 100644 --- a/drivers/net/ethernet/packetengines/hamachi.c +++ b/drivers/net/ethernet/packetengines/hamachi.c @@ -1819,9 +1819,9 @@ static void hamachi_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo * { struct hamachi_private *np = netdev_priv(dev); - strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); - strlcpy(info->version, DRV_VERSION, sizeof(info->version)); - strlcpy(info->bus_info, pci_name(np->pci_dev), sizeof(info->bus_info)); + strscpy(info->driver, DRV_NAME, sizeof(info->driver)); + strscpy(info->version, DRV_VERSION, sizeof(info->version)); + strscpy(info->bus_info, pci_name(np->pci_dev), sizeof(info->bus_info)); } static int hamachi_get_link_ksettings(struct net_device *dev, diff --git a/drivers/net/ethernet/packetengines/yellowfin.c b/drivers/net/ethernet/packetengines/yellowfin.c index 03650022d444..640ac01689fb 100644 --- a/drivers/net/ethernet/packetengines/yellowfin.c +++ b/drivers/net/ethernet/packetengines/yellowfin.c @@ -1340,9 +1340,9 @@ static void yellowfin_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo { struct yellowfin_private *np = netdev_priv(dev); - strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); - strlcpy(info->version, DRV_VERSION, sizeof(info->version)); - strlcpy(info->bus_info, pci_name(np->pci_dev), sizeof(info->bus_info)); + strscpy(info->driver, DRV_NAME, sizeof(info->driver)); + strscpy(info->version, DRV_VERSION, sizeof(info->version)); + strscpy(info->bus_info, pci_name(np->pci_dev), sizeof(info->bus_info)); } static const struct ethtool_ops ethtool_ops = { diff --git a/drivers/net/ethernet/pasemi/pasemi_mac.c b/drivers/net/ethernet/pasemi/pasemi_mac.c index f0ace3a0e85c..aaab590ef548 100644 --- a/drivers/net/ethernet/pasemi/pasemi_mac.c +++ b/drivers/net/ethernet/pasemi/pasemi_mac.c @@ -1697,7 +1697,7 @@ pasemi_mac_probe(struct pci_dev *pdev, const struct pci_device_id *ent) mac->pdev = pdev; mac->netdev = dev; - netif_napi_add(dev, &mac->napi, pasemi_mac_poll, 64); + netif_napi_add(dev, &mac->napi, pasemi_mac_poll); dev->features = NETIF_F_IP_CSUM | NETIF_F_LLTX | NETIF_F_SG | NETIF_F_HIGHDMA | NETIF_F_GSO; diff --git a/drivers/net/ethernet/pensando/ionic/ionic_bus_pci.c b/drivers/net/ethernet/pensando/ionic/ionic_bus_pci.c index 0a7a757494bc..ce436e97324a 100644 --- a/drivers/net/ethernet/pensando/ionic/ionic_bus_pci.c +++ b/drivers/net/ethernet/pensando/ionic/ionic_bus_pci.c @@ -320,16 +320,16 @@ static int ionic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) dev_err(dev, "Cannot enable existing VFs: %d\n", err); } - err = ionic_lif_register(ionic->lif); + err = ionic_devlink_register(ionic); if (err) { - dev_err(dev, "Cannot register LIF: %d, aborting\n", err); + dev_err(dev, "Cannot register devlink: %d\n", err); goto err_out_deinit_lifs; } - err = ionic_devlink_register(ionic); + err = ionic_lif_register(ionic->lif); if (err) { - dev_err(dev, "Cannot register devlink: %d\n", err); - goto err_out_deregister_lifs; + dev_err(dev, "Cannot register LIF: %d, aborting\n", err); + goto err_out_deregister_devlink; } mod_timer(&ionic->watchdog_timer, @@ -337,8 +337,8 @@ static int ionic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) return 0; -err_out_deregister_lifs: - ionic_lif_unregister(ionic->lif); +err_out_deregister_devlink: + ionic_devlink_unregister(ionic); err_out_deinit_lifs: ionic_vf_dealloc(ionic); ionic_lif_deinit(ionic->lif); @@ -380,8 +380,8 @@ static void ionic_remove(struct pci_dev *pdev) del_timer_sync(&ionic->watchdog_timer); if (ionic->lif) { - ionic_devlink_unregister(ionic); ionic_lif_unregister(ionic->lif); + ionic_devlink_unregister(ionic); ionic_lif_deinit(ionic->lif); ionic_lif_free(ionic->lif); ionic->lif = NULL; diff --git a/drivers/net/ethernet/pensando/ionic/ionic_lif.c b/drivers/net/ethernet/pensando/ionic/ionic_lif.c index 0be79c516781..5d58fd99be3c 100644 --- a/drivers/net/ethernet/pensando/ionic/ionic_lif.c +++ b/drivers/net/ethernet/pensando/ionic/ionic_lif.c @@ -774,8 +774,7 @@ static int ionic_lif_txq_init(struct ionic_lif *lif, struct ionic_qcq *qcq) dev_dbg(dev, "txq->hw_index %d\n", q->hw_index); if (test_bit(IONIC_LIF_F_SPLIT_INTR, lif->state)) - netif_napi_add(lif->netdev, &qcq->napi, ionic_tx_napi, - NAPI_POLL_WEIGHT); + netif_napi_add(lif->netdev, &qcq->napi, ionic_tx_napi); qcq->flags |= IONIC_QCQ_F_INITED; @@ -830,11 +829,9 @@ static int ionic_lif_rxq_init(struct ionic_lif *lif, struct ionic_qcq *qcq) dev_dbg(dev, "rxq->hw_index %d\n", q->hw_index); if (test_bit(IONIC_LIF_F_SPLIT_INTR, lif->state)) - netif_napi_add(lif->netdev, &qcq->napi, ionic_rx_napi, - NAPI_POLL_WEIGHT); + netif_napi_add(lif->netdev, &qcq->napi, ionic_rx_napi); else - netif_napi_add(lif->netdev, &qcq->napi, ionic_txrx_napi, - NAPI_POLL_WEIGHT); + netif_napi_add(lif->netdev, &qcq->napi, ionic_txrx_napi); qcq->flags |= IONIC_QCQ_F_INITED; @@ -3165,8 +3162,7 @@ static int ionic_lif_adminq_init(struct ionic_lif *lif) dev_dbg(dev, "adminq->hw_type %d\n", q->hw_type); dev_dbg(dev, "adminq->hw_index %d\n", q->hw_index); - netif_napi_add(lif->netdev, &qcq->napi, ionic_adminq_napi, - NAPI_POLL_WEIGHT); + netif_napi_add(lif->netdev, &qcq->napi, ionic_adminq_napi); napi_enable(&qcq->napi); diff --git a/drivers/net/ethernet/qlogic/netxen/netxen_nic_ethtool.c b/drivers/net/ethernet/qlogic/netxen/netxen_nic_ethtool.c index 3c4a84ea6321..8c4cb910e09b 100644 --- a/drivers/net/ethernet/qlogic/netxen/netxen_nic_ethtool.c +++ b/drivers/net/ethernet/qlogic/netxen/netxen_nic_ethtool.c @@ -65,9 +65,9 @@ netxen_nic_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *drvinfo) u32 fw_minor = 0; u32 fw_build = 0; - strlcpy(drvinfo->driver, netxen_nic_driver_name, + strscpy(drvinfo->driver, netxen_nic_driver_name, sizeof(drvinfo->driver)); - strlcpy(drvinfo->version, NETXEN_NIC_LINUX_VERSIONID, + strscpy(drvinfo->version, NETXEN_NIC_LINUX_VERSIONID, sizeof(drvinfo->version)); fw_major = NXRD32(adapter, NETXEN_FW_VERSION_MAJOR); fw_minor = NXRD32(adapter, NETXEN_FW_VERSION_MINOR); @@ -75,7 +75,7 @@ netxen_nic_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *drvinfo) snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version), "%d.%d.%d", fw_major, fw_minor, fw_build); - strlcpy(drvinfo->bus_info, pci_name(adapter->pdev), + strscpy(drvinfo->bus_info, pci_name(adapter->pdev), sizeof(drvinfo->bus_info)); } diff --git a/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c b/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c index 4e6f00af17d9..de8d54b23f73 100644 --- a/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c +++ b/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c @@ -173,8 +173,7 @@ netxen_napi_add(struct netxen_adapter *adapter, struct net_device *netdev) for (ring = 0; ring < adapter->max_sds_rings; ring++) { sds_ring = &recv_ctx->sds_rings[ring]; - netif_napi_add(netdev, &sds_ring->napi, - netxen_nic_poll, NAPI_POLL_WEIGHT); + netif_napi_add(netdev, &sds_ring->napi, netxen_nic_poll); } return 0; diff --git a/drivers/net/ethernet/qlogic/qed/qed_int.c b/drivers/net/ethernet/qlogic/qed/qed_int.c index d701ecd3ba00..2661c483c67e 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_int.c +++ b/drivers/net/ethernet/qlogic/qed/qed_int.c @@ -1119,7 +1119,7 @@ static int qed_int_deassertion(struct qed_hwfn *p_hwfn, snprintf(bit_name, 30, p_aeu->bit_name, num); else - strlcpy(bit_name, + strscpy(bit_name, p_aeu->bit_name, 30); /* We now need to pass bitmask in its diff --git a/drivers/net/ethernet/qlogic/qede/qede_ethtool.c b/drivers/net/ethernet/qlogic/qede/qede_ethtool.c index 97a7ab0826ed..8034d812d5a0 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_ethtool.c +++ b/drivers/net/ethernet/qlogic/qede/qede_ethtool.c @@ -624,7 +624,7 @@ static void qede_get_drvinfo(struct net_device *ndev, struct qede_dev *edev = netdev_priv(ndev); char mbi[ETHTOOL_FWVERS_LEN]; - strlcpy(info->driver, "qede", sizeof(info->driver)); + strscpy(info->driver, "qede", sizeof(info->driver)); snprintf(storm, ETHTOOL_FWVERS_LEN, "%d.%d.%d.%d", edev->dev_info.common.fw_major, @@ -661,7 +661,7 @@ static void qede_get_drvinfo(struct net_device *ndev, "mfw %s", mfw); } - strlcpy(info->bus_info, pci_name(edev->pdev), sizeof(info->bus_info)); + strscpy(info->bus_info, pci_name(edev->pdev), sizeof(info->bus_info)); } static void qede_get_wol(struct net_device *ndev, struct ethtool_wolinfo *wol) diff --git a/drivers/net/ethernet/qlogic/qede/qede_main.c b/drivers/net/ethernet/qlogic/qede/qede_main.c index f56b679adb4b..953f304b8588 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_main.c +++ b/drivers/net/ethernet/qlogic/qede/qede_main.c @@ -1214,7 +1214,7 @@ static int __qede_probe(struct pci_dev *pdev, u32 dp_module, u8 dp_level, /* Start the Slowpath-process */ memset(&sp_params, 0, sizeof(sp_params)); sp_params.int_mode = QED_INT_MODE_MSIX; - strlcpy(sp_params.name, "qede LAN", QED_DRV_VER_STR_SIZE); + strscpy(sp_params.name, "qede LAN", QED_DRV_VER_STR_SIZE); rc = qed_ops->common->slowpath_start(cdev, &sp_params); if (rc) { pr_notice("Cannot start slowpath\n"); @@ -1904,8 +1904,7 @@ static void qede_napi_add_enable(struct qede_dev *edev) /* Add NAPI objects */ for_each_queue(i) { - netif_napi_add(edev->ndev, &edev->fp_array[i].napi, - qede_poll, NAPI_POLL_WEIGHT); + netif_napi_add(edev->ndev, &edev->fp_array[i].napi, qede_poll); napi_enable(&edev->fp_array[i].napi); } } diff --git a/drivers/net/ethernet/qlogic/qla3xxx.c b/drivers/net/ethernet/qlogic/qla3xxx.c index 06f4d9a9e938..76072f8c3d2f 100644 --- a/drivers/net/ethernet/qlogic/qla3xxx.c +++ b/drivers/net/ethernet/qlogic/qla3xxx.c @@ -1736,10 +1736,10 @@ static void ql_get_drvinfo(struct net_device *ndev, struct ethtool_drvinfo *drvinfo) { struct ql3_adapter *qdev = netdev_priv(ndev); - strlcpy(drvinfo->driver, ql3xxx_driver_name, sizeof(drvinfo->driver)); - strlcpy(drvinfo->version, ql3xxx_driver_version, + strscpy(drvinfo->driver, ql3xxx_driver_name, sizeof(drvinfo->driver)); + strscpy(drvinfo->version, ql3xxx_driver_version, sizeof(drvinfo->version)); - strlcpy(drvinfo->bus_info, pci_name(qdev->pdev), + strscpy(drvinfo->bus_info, pci_name(qdev->pdev), sizeof(drvinfo->bus_info)); } @@ -3813,7 +3813,7 @@ static int ql3xxx_probe(struct pci_dev *pdev, ndev->ethtool_ops = &ql3xxx_ethtool_ops; ndev->watchdog_timeo = 5 * HZ; - netif_napi_add(ndev, &qdev->napi, ql_poll, 64); + netif_napi_add(ndev, &qdev->napi, ql_poll); ndev->irq = pdev->irq; diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c index 54a2d653be63..1ee491f78c6b 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c @@ -277,10 +277,10 @@ qlcnic_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *drvinfo) snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version), "%d.%d.%d", fw_major, fw_minor, fw_build); - strlcpy(drvinfo->bus_info, pci_name(adapter->pdev), + strscpy(drvinfo->bus_info, pci_name(adapter->pdev), sizeof(drvinfo->bus_info)); - strlcpy(drvinfo->driver, qlcnic_driver_name, sizeof(drvinfo->driver)); - strlcpy(drvinfo->version, QLCNIC_LINUX_VERSIONID, + strscpy(drvinfo->driver, qlcnic_driver_name, sizeof(drvinfo->driver)); + strscpy(drvinfo->version, QLCNIC_LINUX_VERSIONID, sizeof(drvinfo->version)); } diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c index 9da5e97f8a0a..92930a055cbc 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c @@ -1586,17 +1586,15 @@ int qlcnic_82xx_napi_add(struct qlcnic_adapter *adapter, sds_ring = &recv_ctx->sds_rings[ring]; if (qlcnic_check_multi_tx(adapter) && !adapter->ahw->diag_test) { - netif_napi_add(netdev, &sds_ring->napi, qlcnic_rx_poll, - NAPI_POLL_WEIGHT); + netif_napi_add(netdev, &sds_ring->napi, + qlcnic_rx_poll); } else { if (ring == (adapter->drv_sds_rings - 1)) netif_napi_add(netdev, &sds_ring->napi, - qlcnic_poll, - NAPI_POLL_WEIGHT); + qlcnic_poll); else netif_napi_add(netdev, &sds_ring->napi, - qlcnic_rx_poll, - NAPI_POLL_WEIGHT); + qlcnic_rx_poll); } } @@ -2115,17 +2113,14 @@ int qlcnic_83xx_napi_add(struct qlcnic_adapter *adapter, if (adapter->flags & QLCNIC_MSIX_ENABLED) { if (!(adapter->flags & QLCNIC_TX_INTR_SHARED)) netif_napi_add(netdev, &sds_ring->napi, - qlcnic_83xx_rx_poll, - NAPI_POLL_WEIGHT); + qlcnic_83xx_rx_poll); else netif_napi_add(netdev, &sds_ring->napi, - qlcnic_83xx_msix_sriov_vf_poll, - NAPI_POLL_WEIGHT); + qlcnic_83xx_msix_sriov_vf_poll); } else { netif_napi_add(netdev, &sds_ring->napi, - qlcnic_83xx_poll, - NAPI_POLL_WEIGHT); + qlcnic_83xx_poll); } } diff --git a/drivers/net/ethernet/qualcomm/emac/emac.c b/drivers/net/ethernet/qualcomm/emac/emac.c index a55c52696d49..3115b2c12898 100644 --- a/drivers/net/ethernet/qualcomm/emac/emac.c +++ b/drivers/net/ethernet/qualcomm/emac/emac.c @@ -684,8 +684,7 @@ static int emac_probe(struct platform_device *pdev) /* Initialize queues */ emac_mac_rx_tx_ring_init_all(pdev, adpt); - netif_napi_add(netdev, &adpt->rx_q.napi, emac_napi_rtx, - NAPI_POLL_WEIGHT); + netif_napi_add(netdev, &adpt->rx_q.napi, emac_napi_rtx); ret = register_netdev(netdev); if (ret) { diff --git a/drivers/net/ethernet/qualcomm/qca_debug.c b/drivers/net/ethernet/qualcomm/qca_debug.c index 792ce9a323cd..f62c39544e08 100644 --- a/drivers/net/ethernet/qualcomm/qca_debug.c +++ b/drivers/net/ethernet/qualcomm/qca_debug.c @@ -164,10 +164,10 @@ qcaspi_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *p) { struct qcaspi *qca = netdev_priv(dev); - strlcpy(p->driver, QCASPI_DRV_NAME, sizeof(p->driver)); - strlcpy(p->version, QCASPI_DRV_VERSION, sizeof(p->version)); - strlcpy(p->fw_version, "QCA7000", sizeof(p->fw_version)); - strlcpy(p->bus_info, dev_name(&qca->spi_dev->dev), + strscpy(p->driver, QCASPI_DRV_NAME, sizeof(p->driver)); + strscpy(p->version, QCASPI_DRV_VERSION, sizeof(p->version)); + strscpy(p->fw_version, "QCA7000", sizeof(p->fw_version)); + strscpy(p->bus_info, dev_name(&qca->spi_dev->dev), sizeof(p->bus_info)); } diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_map.h b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map.h index e5a0b38f7dbe..2b033060fc20 100644 --- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_map.h +++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map.h @@ -19,7 +19,7 @@ struct rmnet_map_control_command { __be16 flow_control_seq_num; __be32 qos_id; } flow_control; - u8 data[0]; + DECLARE_FLEX_ARRAY(u8, data); }; } __aligned(1); diff --git a/drivers/net/ethernet/rdc/r6040.c b/drivers/net/ethernet/rdc/r6040.c index a6bf7d505178..eecd52ed1ed2 100644 --- a/drivers/net/ethernet/rdc/r6040.c +++ b/drivers/net/ethernet/rdc/r6040.c @@ -939,9 +939,9 @@ static void netdev_get_drvinfo(struct net_device *dev, { struct r6040_private *rp = netdev_priv(dev); - strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); - strlcpy(info->version, DRV_VERSION, sizeof(info->version)); - strlcpy(info->bus_info, pci_name(rp->pdev), sizeof(info->bus_info)); + strscpy(info->driver, DRV_NAME, sizeof(info->driver)); + strscpy(info->version, DRV_VERSION, sizeof(info->version)); + strscpy(info->bus_info, pci_name(rp->pdev), sizeof(info->bus_info)); } static const struct ethtool_ops netdev_ethtool_ops = { @@ -1127,7 +1127,7 @@ static int r6040_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) dev->ethtool_ops = &netdev_ethtool_ops; dev->watchdog_timeo = TX_TIMEOUT; - netif_napi_add(dev, &lp->napi, r6040_poll, 64); + netif_napi_add(dev, &lp->napi, r6040_poll); lp->mii_bus = mdiobus_alloc(); if (!lp->mii_bus) { diff --git a/drivers/net/ethernet/realtek/8139cp.c b/drivers/net/ethernet/realtek/8139cp.c index e0feeec13da6..f5786d78ed23 100644 --- a/drivers/net/ethernet/realtek/8139cp.c +++ b/drivers/net/ethernet/realtek/8139cp.c @@ -1382,9 +1382,9 @@ static void cp_get_drvinfo (struct net_device *dev, struct ethtool_drvinfo *info { struct cp_private *cp = netdev_priv(dev); - strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); - strlcpy(info->version, DRV_VERSION, sizeof(info->version)); - strlcpy(info->bus_info, pci_name(cp->pdev), sizeof(info->bus_info)); + strscpy(info->driver, DRV_NAME, sizeof(info->driver)); + strscpy(info->version, DRV_VERSION, sizeof(info->version)); + strscpy(info->bus_info, pci_name(cp->pdev), sizeof(info->bus_info)); } static void cp_get_ringparam(struct net_device *dev, diff --git a/drivers/net/ethernet/realtek/8139too.c b/drivers/net/ethernet/realtek/8139too.c index 15b40fd93cd2..469e2e229c6e 100644 --- a/drivers/net/ethernet/realtek/8139too.c +++ b/drivers/net/ethernet/realtek/8139too.c @@ -1002,7 +1002,7 @@ static int rtl8139_init_one(struct pci_dev *pdev, dev->netdev_ops = &rtl8139_netdev_ops; dev->ethtool_ops = &rtl8139_ethtool_ops; dev->watchdog_timeo = TX_TIMEOUT; - netif_napi_add(dev, &tp->napi, rtl8139_poll, 64); + netif_napi_add(dev, &tp->napi, rtl8139_poll); /* note: the hardware is not capable of sg/csum/highdma, however * through the use of skb_copy_and_csum_dev we enable these @@ -2380,9 +2380,9 @@ static int rtl8139_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol) static void rtl8139_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { struct rtl8139_private *tp = netdev_priv(dev); - strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); - strlcpy(info->version, DRV_VERSION, sizeof(info->version)); - strlcpy(info->bus_info, pci_name(tp->pci_dev), sizeof(info->bus_info)); + strscpy(info->driver, DRV_NAME, sizeof(info->driver)); + strscpy(info->version, DRV_VERSION, sizeof(info->version)); + strscpy(info->bus_info, pci_name(tp->pci_dev), sizeof(info->bus_info)); } static int rtl8139_get_link_ksettings(struct net_device *dev, diff --git a/drivers/net/ethernet/realtek/r8169.h b/drivers/net/ethernet/realtek/r8169.h index 8da4b66b71b5..55ef8251feb5 100644 --- a/drivers/net/ethernet/realtek/r8169.h +++ b/drivers/net/ethernet/realtek/r8169.h @@ -23,10 +23,10 @@ enum mac_version { RTL_GIGA_MAC_VER_09, RTL_GIGA_MAC_VER_10, RTL_GIGA_MAC_VER_11, - RTL_GIGA_MAC_VER_12, - RTL_GIGA_MAC_VER_13, + /* RTL_GIGA_MAC_VER_12 was handled the same as VER_17 */ + /* RTL_GIGA_MAC_VER_13 was merged with VER_10 */ RTL_GIGA_MAC_VER_14, - RTL_GIGA_MAC_VER_16, + /* RTL_GIGA_MAC_VER_16 was merged with VER_10 */ RTL_GIGA_MAC_VER_17, RTL_GIGA_MAC_VER_18, RTL_GIGA_MAC_VER_19, @@ -51,20 +51,20 @@ enum mac_version { RTL_GIGA_MAC_VER_38, RTL_GIGA_MAC_VER_39, RTL_GIGA_MAC_VER_40, - RTL_GIGA_MAC_VER_41, + /* support for RTL_GIGA_MAC_VER_41 has been removed */ RTL_GIGA_MAC_VER_42, RTL_GIGA_MAC_VER_43, RTL_GIGA_MAC_VER_44, - RTL_GIGA_MAC_VER_45, + /* support for RTL_GIGA_MAC_VER_45 has been removed */ RTL_GIGA_MAC_VER_46, - RTL_GIGA_MAC_VER_47, + /* support for RTL_GIGA_MAC_VER_47 has been removed */ RTL_GIGA_MAC_VER_48, - RTL_GIGA_MAC_VER_49, - RTL_GIGA_MAC_VER_50, + /* support for RTL_GIGA_MAC_VER_49 has been removed */ + /* support for RTL_GIGA_MAC_VER_50 has been removed */ RTL_GIGA_MAC_VER_51, RTL_GIGA_MAC_VER_52, RTL_GIGA_MAC_VER_53, - RTL_GIGA_MAC_VER_60, + /* support for RTL_GIGA_MAC_VER_60 has been removed */ RTL_GIGA_MAC_VER_61, RTL_GIGA_MAC_VER_63, RTL_GIGA_MAC_NONE diff --git a/drivers/net/ethernet/realtek/r8169_main.c b/drivers/net/ethernet/realtek/r8169_main.c index 1b7fdb4f056b..a73d061d9fcb 100644 --- a/drivers/net/ethernet/realtek/r8169_main.c +++ b/drivers/net/ethernet/realtek/r8169_main.c @@ -49,10 +49,8 @@ #define FIRMWARE_8106E_2 "rtl_nic/rtl8106e-2.fw" #define FIRMWARE_8168G_2 "rtl_nic/rtl8168g-2.fw" #define FIRMWARE_8168G_3 "rtl_nic/rtl8168g-3.fw" -#define FIRMWARE_8168H_1 "rtl_nic/rtl8168h-1.fw" #define FIRMWARE_8168H_2 "rtl_nic/rtl8168h-2.fw" #define FIRMWARE_8168FP_3 "rtl_nic/rtl8168fp-3.fw" -#define FIRMWARE_8107E_1 "rtl_nic/rtl8107e-1.fw" #define FIRMWARE_8107E_2 "rtl_nic/rtl8107e-2.fw" #define FIRMWARE_8125A_3 "rtl_nic/rtl8125a-3.fw" #define FIRMWARE_8125B_2 "rtl_nic/rtl8125b-2.fw" @@ -102,12 +100,9 @@ static const struct { [RTL_GIGA_MAC_VER_07] = {"RTL8102e" }, [RTL_GIGA_MAC_VER_08] = {"RTL8102e" }, [RTL_GIGA_MAC_VER_09] = {"RTL8102e/RTL8103e" }, - [RTL_GIGA_MAC_VER_10] = {"RTL8101e" }, + [RTL_GIGA_MAC_VER_10] = {"RTL8101e/RTL8100e" }, [RTL_GIGA_MAC_VER_11] = {"RTL8168b/8111b" }, - [RTL_GIGA_MAC_VER_12] = {"RTL8168b/8111b" }, - [RTL_GIGA_MAC_VER_13] = {"RTL8101e/RTL8100e" }, [RTL_GIGA_MAC_VER_14] = {"RTL8401" }, - [RTL_GIGA_MAC_VER_16] = {"RTL8101e" }, [RTL_GIGA_MAC_VER_17] = {"RTL8168b/8111b" }, [RTL_GIGA_MAC_VER_18] = {"RTL8168cp/8111cp" }, [RTL_GIGA_MAC_VER_19] = {"RTL8168c/8111c" }, @@ -131,20 +126,14 @@ static const struct { [RTL_GIGA_MAC_VER_38] = {"RTL8411", FIRMWARE_8411_1 }, [RTL_GIGA_MAC_VER_39] = {"RTL8106e", FIRMWARE_8106E_1}, [RTL_GIGA_MAC_VER_40] = {"RTL8168g/8111g", FIRMWARE_8168G_2}, - [RTL_GIGA_MAC_VER_41] = {"RTL8168g/8111g" }, [RTL_GIGA_MAC_VER_42] = {"RTL8168gu/8111gu", FIRMWARE_8168G_3}, [RTL_GIGA_MAC_VER_43] = {"RTL8106eus", FIRMWARE_8106E_2}, [RTL_GIGA_MAC_VER_44] = {"RTL8411b", FIRMWARE_8411_2 }, - [RTL_GIGA_MAC_VER_45] = {"RTL8168h/8111h", FIRMWARE_8168H_1}, [RTL_GIGA_MAC_VER_46] = {"RTL8168h/8111h", FIRMWARE_8168H_2}, - [RTL_GIGA_MAC_VER_47] = {"RTL8107e", FIRMWARE_8107E_1}, [RTL_GIGA_MAC_VER_48] = {"RTL8107e", FIRMWARE_8107E_2}, - [RTL_GIGA_MAC_VER_49] = {"RTL8168ep/8111ep" }, - [RTL_GIGA_MAC_VER_50] = {"RTL8168ep/8111ep" }, [RTL_GIGA_MAC_VER_51] = {"RTL8168ep/8111ep" }, [RTL_GIGA_MAC_VER_52] = {"RTL8168fp/RTL8117", FIRMWARE_8168FP_3}, [RTL_GIGA_MAC_VER_53] = {"RTL8168fp/RTL8117", }, - [RTL_GIGA_MAC_VER_60] = {"RTL8125A" }, [RTL_GIGA_MAC_VER_61] = {"RTL8125A", FIRMWARE_8125A_3}, /* reserve 62 for CFG_METHOD_4 in the vendor driver */ [RTL_GIGA_MAC_VER_63] = {"RTL8125B", FIRMWARE_8125B_2}, @@ -658,10 +647,8 @@ MODULE_FIRMWARE(FIRMWARE_8106E_1); MODULE_FIRMWARE(FIRMWARE_8106E_2); MODULE_FIRMWARE(FIRMWARE_8168G_2); MODULE_FIRMWARE(FIRMWARE_8168G_3); -MODULE_FIRMWARE(FIRMWARE_8168H_1); MODULE_FIRMWARE(FIRMWARE_8168H_2); MODULE_FIRMWARE(FIRMWARE_8168FP_3); -MODULE_FIRMWARE(FIRMWARE_8107E_1); MODULE_FIRMWARE(FIRMWARE_8107E_2); MODULE_FIRMWARE(FIRMWARE_8125A_3); MODULE_FIRMWARE(FIRMWARE_8125B_2); @@ -689,7 +676,7 @@ static void rtl_pci_commit(struct rtl8169_private *tp) static bool rtl_is_8125(struct rtl8169_private *tp) { - return tp->mac_version >= RTL_GIGA_MAC_VER_60; + return tp->mac_version >= RTL_GIGA_MAC_VER_61; } static bool rtl_is_8168evl_up(struct rtl8169_private *tp) @@ -892,8 +879,6 @@ static void rtl8168g_phy_suspend_quirk(struct rtl8169_private *tp, int value) { switch (tp->mac_version) { case RTL_GIGA_MAC_VER_40: - case RTL_GIGA_MAC_VER_41: - case RTL_GIGA_MAC_VER_49: if (value & BMCR_RESET || !(value & BMCR_PDOWN)) rtl_eri_set_bits(tp, 0x1a8, 0xfc000000); else @@ -1207,7 +1192,7 @@ static enum rtl_dash_type rtl_check_dash(struct rtl8169_private *tp) case RTL_GIGA_MAC_VER_28: case RTL_GIGA_MAC_VER_31: return r8168dp_check_dash(tp) ? RTL_DASH_DP : RTL_DASH_NONE; - case RTL_GIGA_MAC_VER_49 ... RTL_GIGA_MAC_VER_53: + case RTL_GIGA_MAC_VER_51 ... RTL_GIGA_MAC_VER_53: return r8168ep_check_dash(tp) ? RTL_DASH_EP : RTL_DASH_NONE; default: return RTL_DASH_NONE; @@ -1423,11 +1408,11 @@ static void rtl8169_get_drvinfo(struct net_device *dev, struct rtl8169_private *tp = netdev_priv(dev); struct rtl_fw *rtl_fw = tp->rtl_fw; - strlcpy(info->driver, KBUILD_MODNAME, sizeof(info->driver)); - strlcpy(info->bus_info, pci_name(tp->pci_dev), sizeof(info->bus_info)); + strscpy(info->driver, KBUILD_MODNAME, sizeof(info->driver)); + strscpy(info->bus_info, pci_name(tp->pci_dev), sizeof(info->bus_info)); BUILD_BUG_ON(sizeof(info->fw_version) < sizeof(rtl_fw->version)); if (rtl_fw) - strlcpy(info->fw_version, rtl_fw->version, + strscpy(info->fw_version, rtl_fw->version, sizeof(info->fw_version)); } @@ -2011,7 +1996,10 @@ static enum mac_version rtl8169_get_mac_version(u16 xid, bool gmii) /* 8168F family. */ { 0x7c8, 0x488, RTL_GIGA_MAC_VER_38 }, - { 0x7cf, 0x481, RTL_GIGA_MAC_VER_36 }, + /* It seems this chip version never made it to + * the wild. Let's disable detection. + * { 0x7cf, 0x481, RTL_GIGA_MAC_VER_36 }, + */ { 0x7cf, 0x480, RTL_GIGA_MAC_VER_35 }, /* 8168E family. */ @@ -2041,7 +2029,6 @@ static enum mac_version rtl8169_get_mac_version(u16 xid, bool gmii) { 0x7c8, 0x3c0, RTL_GIGA_MAC_VER_22 }, /* 8168B family. */ - { 0x7cf, 0x380, RTL_GIGA_MAC_VER_12 }, { 0x7c8, 0x380, RTL_GIGA_MAC_VER_17 }, { 0x7c8, 0x300, RTL_GIGA_MAC_VER_11 }, @@ -2054,19 +2041,10 @@ static enum mac_version rtl8169_get_mac_version(u16 xid, bool gmii) { 0x7cf, 0x249, RTL_GIGA_MAC_VER_08 }, { 0x7cf, 0x348, RTL_GIGA_MAC_VER_07 }, { 0x7cf, 0x248, RTL_GIGA_MAC_VER_07 }, - { 0x7cf, 0x340, RTL_GIGA_MAC_VER_13 }, { 0x7cf, 0x240, RTL_GIGA_MAC_VER_14 }, - { 0x7cf, 0x343, RTL_GIGA_MAC_VER_10 }, - { 0x7cf, 0x342, RTL_GIGA_MAC_VER_16 }, { 0x7c8, 0x348, RTL_GIGA_MAC_VER_09 }, { 0x7c8, 0x248, RTL_GIGA_MAC_VER_09 }, - { 0x7c8, 0x340, RTL_GIGA_MAC_VER_16 }, - /* FIXME: where did these entries come from ? -- FR - * Not even r8101 vendor driver knows these id's, - * so let's disable detection for now. -- HK - * { 0xfc8, 0x388, RTL_GIGA_MAC_VER_13 }, - * { 0xfc8, 0x308, RTL_GIGA_MAC_VER_13 }, - */ + { 0x7c8, 0x340, RTL_GIGA_MAC_VER_10 }, /* 8110 family. */ { 0xfc8, 0x980, RTL_GIGA_MAC_VER_06 }, @@ -2088,8 +2066,6 @@ static enum mac_version rtl8169_get_mac_version(u16 xid, bool gmii) if (ver != RTL_GIGA_MAC_NONE && !gmii) { if (ver == RTL_GIGA_MAC_VER_42) ver = RTL_GIGA_MAC_VER_43; - else if (ver == RTL_GIGA_MAC_VER_45) - ver = RTL_GIGA_MAC_VER_47; else if (ver == RTL_GIGA_MAC_VER_46) ver = RTL_GIGA_MAC_VER_48; } @@ -2271,7 +2247,7 @@ static void rtl_init_rxcfg(struct rtl8169_private *tp) case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_53: RTL_W32(tp, RxConfig, RX128_INT_EN | RX_MULTI_EN | RX_DMA_BURST | RX_EARLY_OFF); break; - case RTL_GIGA_MAC_VER_60 ... RTL_GIGA_MAC_VER_63: + case RTL_GIGA_MAC_VER_61 ... RTL_GIGA_MAC_VER_63: RTL_W32(tp, RxConfig, RX_FETCH_DFLT_8125 | RX_DMA_BURST); break; default: @@ -2338,7 +2314,6 @@ static void rtl_jumbo_config(struct rtl8169_private *tp) rtl_unlock_config_regs(tp); switch (tp->mac_version) { - case RTL_GIGA_MAC_VER_12: case RTL_GIGA_MAC_VER_17: if (jumbo) { readrq = 512; @@ -2455,7 +2430,7 @@ static void rtl_wait_txrx_fifo_empty(struct rtl8169_private *tp) rtl_loop_wait_high(tp, &rtl_txcfg_empty_cond, 100, 42); rtl_loop_wait_high(tp, &rtl_rxtx_empty_cond, 100, 42); break; - case RTL_GIGA_MAC_VER_60 ... RTL_GIGA_MAC_VER_61: + case RTL_GIGA_MAC_VER_61 ... RTL_GIGA_MAC_VER_61: rtl_loop_wait_high(tp, &rtl_rxtx_empty_cond, 100, 42); break; case RTL_GIGA_MAC_VER_63: @@ -2468,6 +2443,11 @@ static void rtl_wait_txrx_fifo_empty(struct rtl8169_private *tp) } } +static void rtl_disable_rxdvgate(struct rtl8169_private *tp) +{ + RTL_W32(tp, MISC, RTL_R32(tp, MISC) & ~RXDV_GATED_EN); +} + static void rtl_enable_rxdvgate(struct rtl8169_private *tp) { RTL_W32(tp, MISC, RTL_R32(tp, MISC) | RXDV_GATED_EN); @@ -2700,8 +2680,8 @@ static void rtl_hw_aspm_clkreq_enable(struct rtl8169_private *tp, bool enable) RTL_W8(tp, Config2, RTL_R8(tp, Config2) | ClkReqEn); switch (tp->mac_version) { - case RTL_GIGA_MAC_VER_45 ... RTL_GIGA_MAC_VER_48: - case RTL_GIGA_MAC_VER_60 ... RTL_GIGA_MAC_VER_63: + case RTL_GIGA_MAC_VER_46 ... RTL_GIGA_MAC_VER_48: + case RTL_GIGA_MAC_VER_61 ... RTL_GIGA_MAC_VER_63: /* reset ephy tx/rx disable timer */ r8168_mac_ocp_modify(tp, 0xe094, 0xff00, 0); /* chip can trigger L1.2 */ @@ -2712,8 +2692,8 @@ static void rtl_hw_aspm_clkreq_enable(struct rtl8169_private *tp, bool enable) } } else { switch (tp->mac_version) { - case RTL_GIGA_MAC_VER_45 ... RTL_GIGA_MAC_VER_48: - case RTL_GIGA_MAC_VER_60 ... RTL_GIGA_MAC_VER_63: + case RTL_GIGA_MAC_VER_46 ... RTL_GIGA_MAC_VER_48: + case RTL_GIGA_MAC_VER_61 ... RTL_GIGA_MAC_VER_63: r8168_mac_ocp_modify(tp, 0xe092, 0x00ff, 0); break; default: @@ -2985,7 +2965,7 @@ static void rtl_hw_start_8168g(struct rtl8169_private *tp) rtl_reset_packet_filter(tp); rtl_eri_write(tp, 0x2f8, ERIAR_MASK_0011, 0x1d8f); - RTL_W32(tp, MISC, RTL_R32(tp, MISC) & ~RXDV_GATED_EN); + rtl_disable_rxdvgate(tp); rtl_eri_write(tp, 0xc0, ERIAR_MASK_0011, 0x0000); rtl_eri_write(tp, 0xb8, ERIAR_MASK_0011, 0x0000); @@ -3223,7 +3203,7 @@ static void rtl_hw_start_8168h_1(struct rtl8169_private *tp) rtl_eri_write(tp, 0x5f0, ERIAR_MASK_0011, 0x4f87); - RTL_W32(tp, MISC, RTL_R32(tp, MISC) & ~RXDV_GATED_EN); + rtl_disable_rxdvgate(tp); rtl_eri_write(tp, 0xc0, ERIAR_MASK_0011, 0x0000); rtl_eri_write(tp, 0xb8, ERIAR_MASK_0011, 0x0000); @@ -3274,7 +3254,7 @@ static void rtl_hw_start_8168ep(struct rtl8169_private *tp) rtl_eri_write(tp, 0x5f0, ERIAR_MASK_0011, 0x4f87); - RTL_W32(tp, MISC, RTL_R32(tp, MISC) & ~RXDV_GATED_EN); + rtl_disable_rxdvgate(tp); rtl_eri_write(tp, 0xc0, ERIAR_MASK_0011, 0x0000); rtl_eri_write(tp, 0xb8, ERIAR_MASK_0011, 0x0000); @@ -3288,45 +3268,6 @@ static void rtl_hw_start_8168ep(struct rtl8169_private *tp) rtl_pcie_state_l2l3_disable(tp); } -static void rtl_hw_start_8168ep_1(struct rtl8169_private *tp) -{ - static const struct ephy_info e_info_8168ep_1[] = { - { 0x00, 0xffff, 0x10ab }, - { 0x06, 0xffff, 0xf030 }, - { 0x08, 0xffff, 0x2006 }, - { 0x0d, 0xffff, 0x1666 }, - { 0x0c, 0x3ff0, 0x0000 } - }; - - /* disable aspm and clock request before access ephy */ - rtl_hw_aspm_clkreq_enable(tp, false); - rtl_ephy_init(tp, e_info_8168ep_1); - - rtl_hw_start_8168ep(tp); - - rtl_hw_aspm_clkreq_enable(tp, true); -} - -static void rtl_hw_start_8168ep_2(struct rtl8169_private *tp) -{ - static const struct ephy_info e_info_8168ep_2[] = { - { 0x00, 0xffff, 0x10a3 }, - { 0x19, 0xffff, 0xfc00 }, - { 0x1e, 0xffff, 0x20ea } - }; - - /* disable aspm and clock request before access ephy */ - rtl_hw_aspm_clkreq_enable(tp, false); - rtl_ephy_init(tp, e_info_8168ep_2); - - rtl_hw_start_8168ep(tp); - - RTL_W8(tp, DLLPR, RTL_R8(tp, DLLPR) & ~PFM_EN); - RTL_W8(tp, MISC_1, RTL_R8(tp, MISC_1) & ~PFM_D3COLD_EN); - - rtl_hw_aspm_clkreq_enable(tp, true); -} - static void rtl_hw_start_8168ep_3(struct rtl8169_private *tp) { static const struct ephy_info e_info_8168ep_3[] = { @@ -3377,7 +3318,7 @@ static void rtl_hw_start_8117(struct rtl8169_private *tp) rtl_eri_write(tp, 0x5f0, ERIAR_MASK_0011, 0x4f87); - RTL_W32(tp, MISC, RTL_R32(tp, MISC) & ~RXDV_GATED_EN); + rtl_disable_rxdvgate(tp); rtl_eri_write(tp, 0xc0, ERIAR_MASK_0011, 0x0000); rtl_eri_write(tp, 0xb8, ERIAR_MASK_0011, 0x0000); @@ -3621,48 +3562,7 @@ static void rtl_hw_start_8125_common(struct rtl8169_private *tp) else rtl8125a_config_eee_mac(tp); - RTL_W32(tp, MISC, RTL_R32(tp, MISC) & ~RXDV_GATED_EN); - udelay(10); -} - -static void rtl_hw_start_8125a_1(struct rtl8169_private *tp) -{ - static const struct ephy_info e_info_8125a_1[] = { - { 0x01, 0xffff, 0xa812 }, - { 0x09, 0xffff, 0x520c }, - { 0x04, 0xffff, 0xd000 }, - { 0x0d, 0xffff, 0xf702 }, - { 0x0a, 0xffff, 0x8653 }, - { 0x06, 0xffff, 0x001e }, - { 0x08, 0xffff, 0x3595 }, - { 0x20, 0xffff, 0x9455 }, - { 0x21, 0xffff, 0x99ff }, - { 0x02, 0xffff, 0x6046 }, - { 0x29, 0xffff, 0xfe00 }, - { 0x23, 0xffff, 0xab62 }, - - { 0x41, 0xffff, 0xa80c }, - { 0x49, 0xffff, 0x520c }, - { 0x44, 0xffff, 0xd000 }, - { 0x4d, 0xffff, 0xf702 }, - { 0x4a, 0xffff, 0x8653 }, - { 0x46, 0xffff, 0x001e }, - { 0x48, 0xffff, 0x3595 }, - { 0x60, 0xffff, 0x9455 }, - { 0x61, 0xffff, 0x99ff }, - { 0x42, 0xffff, 0x6046 }, - { 0x69, 0xffff, 0xfe00 }, - { 0x63, 0xffff, 0xab62 }, - }; - - rtl_set_def_aspm_entry_latency(tp); - - /* disable aspm and clock request before access ephy */ - rtl_hw_aspm_clkreq_enable(tp, false); - rtl_ephy_init(tp, e_info_8125a_1); - - rtl_hw_start_8125_common(tp); - rtl_hw_aspm_clkreq_enable(tp, true); + rtl_disable_rxdvgate(tp); } static void rtl_hw_start_8125a_2(struct rtl8169_private *tp) @@ -3721,10 +3621,7 @@ static void rtl_hw_config(struct rtl8169_private *tp) [RTL_GIGA_MAC_VER_09] = rtl_hw_start_8102e_2, [RTL_GIGA_MAC_VER_10] = NULL, [RTL_GIGA_MAC_VER_11] = rtl_hw_start_8168b, - [RTL_GIGA_MAC_VER_12] = rtl_hw_start_8168b, - [RTL_GIGA_MAC_VER_13] = NULL, [RTL_GIGA_MAC_VER_14] = rtl_hw_start_8401, - [RTL_GIGA_MAC_VER_16] = NULL, [RTL_GIGA_MAC_VER_17] = rtl_hw_start_8168b, [RTL_GIGA_MAC_VER_18] = rtl_hw_start_8168cp_1, [RTL_GIGA_MAC_VER_19] = rtl_hw_start_8168c_1, @@ -3748,20 +3645,14 @@ static void rtl_hw_config(struct rtl8169_private *tp) [RTL_GIGA_MAC_VER_38] = rtl_hw_start_8411, [RTL_GIGA_MAC_VER_39] = rtl_hw_start_8106, [RTL_GIGA_MAC_VER_40] = rtl_hw_start_8168g_1, - [RTL_GIGA_MAC_VER_41] = rtl_hw_start_8168g_1, [RTL_GIGA_MAC_VER_42] = rtl_hw_start_8168g_2, [RTL_GIGA_MAC_VER_43] = rtl_hw_start_8168g_2, [RTL_GIGA_MAC_VER_44] = rtl_hw_start_8411_2, - [RTL_GIGA_MAC_VER_45] = rtl_hw_start_8168h_1, [RTL_GIGA_MAC_VER_46] = rtl_hw_start_8168h_1, - [RTL_GIGA_MAC_VER_47] = rtl_hw_start_8168h_1, [RTL_GIGA_MAC_VER_48] = rtl_hw_start_8168h_1, - [RTL_GIGA_MAC_VER_49] = rtl_hw_start_8168ep_1, - [RTL_GIGA_MAC_VER_50] = rtl_hw_start_8168ep_2, [RTL_GIGA_MAC_VER_51] = rtl_hw_start_8168ep_3, [RTL_GIGA_MAC_VER_52] = rtl_hw_start_8117, [RTL_GIGA_MAC_VER_53] = rtl_hw_start_8117, - [RTL_GIGA_MAC_VER_60] = rtl_hw_start_8125a_1, [RTL_GIGA_MAC_VER_61] = rtl_hw_start_8125a_2, [RTL_GIGA_MAC_VER_63] = rtl_hw_start_8125b, }; @@ -4156,7 +4047,6 @@ static unsigned int rtl_quirk_packet_padto(struct rtl8169_private *tp, switch (tp->mac_version) { case RTL_GIGA_MAC_VER_34: - case RTL_GIGA_MAC_VER_60: case RTL_GIGA_MAC_VER_61: case RTL_GIGA_MAC_VER_63: padto = max_t(unsigned int, padto, ETH_ZLEN); @@ -4677,8 +4567,7 @@ static void r8169_phylink_handler(struct net_device *ndev) pm_runtime_idle(&tp->pci_dev->dev); } - if (net_ratelimit()) - phy_print_status(tp->phydev); + phy_print_status(tp->phydev); } static int r8169_phy_connect(struct rtl8169_private *tp) @@ -4954,23 +4843,6 @@ static const struct dev_pm_ops rtl8169_pm_ops = { rtl8169_runtime_idle) }; -static void rtl_wol_shutdown_quirk(struct rtl8169_private *tp) -{ - /* WoL fails with 8168b when the receiver is disabled. */ - switch (tp->mac_version) { - case RTL_GIGA_MAC_VER_11: - case RTL_GIGA_MAC_VER_12: - case RTL_GIGA_MAC_VER_17: - pci_clear_master(tp->pci_dev); - - RTL_W8(tp, ChipCmd, CmdRxEnb); - rtl_pci_commit(tp); - break; - default: - break; - } -} - static void rtl_shutdown(struct pci_dev *pdev) { struct rtl8169_private *tp = pci_get_drvdata(pdev); @@ -4984,9 +4856,6 @@ static void rtl_shutdown(struct pci_dev *pdev) if (system_state == SYSTEM_POWER_OFF && tp->dash_type == RTL_DASH_NONE) { - if (tp->saved_wolopts) - rtl_wol_shutdown_quirk(tp); - pci_wake_from_d3(pdev, tp->saved_wolopts); pci_set_power_state(pdev, PCI_D3hot); } @@ -5194,13 +5063,13 @@ static void rtl_hw_init_8125(struct rtl8169_private *tp) static void rtl_hw_initialize(struct rtl8169_private *tp) { switch (tp->mac_version) { - case RTL_GIGA_MAC_VER_49 ... RTL_GIGA_MAC_VER_53: + case RTL_GIGA_MAC_VER_51 ... RTL_GIGA_MAC_VER_53: rtl8168ep_stop_cmac(tp); fallthrough; case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_48: rtl_hw_init_8168g(tp); break; - case RTL_GIGA_MAC_VER_60 ... RTL_GIGA_MAC_VER_63: + case RTL_GIGA_MAC_VER_61 ... RTL_GIGA_MAC_VER_63: rtl_hw_init_8125(tp); break; default: @@ -5220,7 +5089,6 @@ static int rtl_jumbo_max(struct rtl8169_private *tp) return JUMBO_7K; /* RTL8168b */ case RTL_GIGA_MAC_VER_11: - case RTL_GIGA_MAC_VER_12: case RTL_GIGA_MAC_VER_17: return JUMBO_4K; /* RTL8168c */ @@ -5231,37 +5099,6 @@ static int rtl_jumbo_max(struct rtl8169_private *tp) } } -static void rtl_disable_clk(void *data) -{ - clk_disable_unprepare(data); -} - -static int rtl_get_ether_clk(struct rtl8169_private *tp) -{ - struct device *d = tp_to_dev(tp); - struct clk *clk; - int rc; - - clk = devm_clk_get(d, "ether_clk"); - if (IS_ERR(clk)) { - rc = PTR_ERR(clk); - if (rc == -ENOENT) - /* clk-core allows NULL (for suspend / resume) */ - rc = 0; - else - dev_err_probe(d, rc, "failed to get clk\n"); - } else { - tp->clk = clk; - rc = clk_prepare_enable(clk); - if (rc) - dev_err(d, "failed to enable clk: %d\n", rc); - else - rc = devm_add_action_or_reset(d, rtl_disable_clk, clk); - } - - return rc; -} - static void rtl_init_mac_address(struct rtl8169_private *tp) { u8 mac_addr[ETH_ALEN] __aligned(2) = {}; @@ -5291,7 +5128,7 @@ done: /* register is set if system vendor successfully tested ASPM 1.2 */ static bool rtl_aspm_is_safe(struct rtl8169_private *tp) { - if (tp->mac_version >= RTL_GIGA_MAC_VER_60 && + if (tp->mac_version >= RTL_GIGA_MAC_VER_61 && r8168_mac_ocp_read(tp, 0xc0b2) & 0xf) return true; @@ -5325,9 +5162,9 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) return -ENOMEM; /* Get the *optional* external "ether_clk" used on some boards */ - rc = rtl_get_ether_clk(tp); - if (rc) - return rc; + tp->clk = devm_clk_get_optional_enabled(&pdev->dev, "ether_clk"); + if (IS_ERR(tp->clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(tp->clk), "failed to get ether_clk\n"); /* enable device (incl. PCI PM wakeup and hotplug setup) */ rc = pcim_enable_device(pdev); @@ -5346,12 +5183,6 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) return -ENODEV; } - /* check for weird/broken PCI region reporting */ - if (pci_resource_len(pdev, region) < R8169_REGS_SIZE) { - dev_err(&pdev->dev, "Invalid PCI region size(s), aborting\n"); - return -ENODEV; - } - rc = pcim_iomap_regions(pdev, BIT(region), KBUILD_MODNAME); if (rc < 0) { dev_err(&pdev->dev, "cannot remap MMIO, aborting\n"); @@ -5378,7 +5209,7 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) */ if (rtl_aspm_is_safe(tp)) rc = 0; - else if (tp->mac_version >= RTL_GIGA_MAC_VER_45) + else if (tp->mac_version >= RTL_GIGA_MAC_VER_46) rc = pci_disable_link_state(pdev, PCIE_LINK_STATE_L1_2); else rc = pci_disable_link_state(pdev, PCIE_LINK_STATE_L1); @@ -5413,7 +5244,7 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) dev->ethtool_ops = &rtl8169_ethtool_ops; - netif_napi_add(dev, &tp->napi, rtl8169_poll, NAPI_POLL_WEIGHT); + netif_napi_add(dev, &tp->napi, rtl8169_poll); dev->hw_features = NETIF_F_IP_CSUM | NETIF_F_RXCSUM | NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX; diff --git a/drivers/net/ethernet/realtek/r8169_phy_config.c b/drivers/net/ethernet/realtek/r8169_phy_config.c index 15c295f90196..930496cd34ed 100644 --- a/drivers/net/ethernet/realtek/r8169_phy_config.c +++ b/drivers/net/ethernet/realtek/r8169_phy_config.c @@ -793,71 +793,6 @@ static void rtl8168g_2_hw_phy_config(struct rtl8169_private *tp, rtl8168g_config_eee_phy(phydev); } -static void rtl8168h_1_hw_phy_config(struct rtl8169_private *tp, - struct phy_device *phydev) -{ - u16 dout_tapbin; - u32 data; - - r8169_apply_firmware(tp); - - /* CHN EST parameters adjust - giga master */ - r8168g_phy_param(phydev, 0x809b, 0xf800, 0x8000); - r8168g_phy_param(phydev, 0x80a2, 0xff00, 0x8000); - r8168g_phy_param(phydev, 0x80a4, 0xff00, 0x8500); - r8168g_phy_param(phydev, 0x809c, 0xff00, 0xbd00); - - /* CHN EST parameters adjust - giga slave */ - r8168g_phy_param(phydev, 0x80ad, 0xf800, 0x7000); - r8168g_phy_param(phydev, 0x80b4, 0xff00, 0x5000); - r8168g_phy_param(phydev, 0x80ac, 0xff00, 0x4000); - - /* CHN EST parameters adjust - fnet */ - r8168g_phy_param(phydev, 0x808e, 0xff00, 0x1200); - r8168g_phy_param(phydev, 0x8090, 0xff00, 0xe500); - r8168g_phy_param(phydev, 0x8092, 0xff00, 0x9f00); - - /* enable R-tune & PGA-retune function */ - dout_tapbin = 0; - data = phy_read_paged(phydev, 0x0a46, 0x13); - data &= 3; - data <<= 2; - dout_tapbin |= data; - data = phy_read_paged(phydev, 0x0a46, 0x12); - data &= 0xc000; - data >>= 14; - dout_tapbin |= data; - dout_tapbin = ~(dout_tapbin ^ 0x08); - dout_tapbin <<= 12; - dout_tapbin &= 0xf000; - - r8168g_phy_param(phydev, 0x827a, 0xf000, dout_tapbin); - r8168g_phy_param(phydev, 0x827b, 0xf000, dout_tapbin); - r8168g_phy_param(phydev, 0x827c, 0xf000, dout_tapbin); - r8168g_phy_param(phydev, 0x827d, 0xf000, dout_tapbin); - r8168g_phy_param(phydev, 0x0811, 0x0000, 0x0800); - phy_modify_paged(phydev, 0x0a42, 0x16, 0x0000, 0x0002); - - rtl8168g_enable_gphy_10m(phydev); - - /* SAR ADC performance */ - phy_modify_paged(phydev, 0x0bca, 0x17, BIT(12) | BIT(13), BIT(14)); - - r8168g_phy_param(phydev, 0x803f, 0x3000, 0x0000); - r8168g_phy_param(phydev, 0x8047, 0x3000, 0x0000); - r8168g_phy_param(phydev, 0x804f, 0x3000, 0x0000); - r8168g_phy_param(phydev, 0x8057, 0x3000, 0x0000); - r8168g_phy_param(phydev, 0x805f, 0x3000, 0x0000); - r8168g_phy_param(phydev, 0x8067, 0x3000, 0x0000); - r8168g_phy_param(phydev, 0x806f, 0x3000, 0x0000); - - /* disable phy pfm mode */ - phy_modify_paged(phydev, 0x0a44, 0x11, BIT(7), 0); - - rtl8168g_disable_aldps(phydev); - rtl8168h_config_eee_phy(phydev); -} - static void rtl8168h_2_hw_phy_config(struct rtl8169_private *tp, struct phy_device *phydev) { @@ -895,27 +830,6 @@ static void rtl8168h_2_hw_phy_config(struct rtl8169_private *tp, rtl8168g_config_eee_phy(phydev); } -static void rtl8168ep_1_hw_phy_config(struct rtl8169_private *tp, - struct phy_device *phydev) -{ - /* Enable PHY auto speed down */ - phy_modify_paged(phydev, 0x0a44, 0x11, 0, BIT(3) | BIT(2)); - - rtl8168g_phy_adjust_10m_aldps(phydev); - - /* Enable EEE auto-fallback function */ - phy_modify_paged(phydev, 0x0a4b, 0x11, 0, BIT(2)); - - /* Enable UC LPF tune function */ - r8168g_phy_param(phydev, 0x8012, 0x0000, 0x8000); - - /* set rg_sel_sdm_rate */ - phy_modify_paged(phydev, 0x0c42, 0x11, BIT(13), BIT(14)); - - rtl8168g_disable_aldps(phydev); - rtl8168g_config_eee_phy(phydev); -} - static void rtl8168ep_2_hw_phy_config(struct rtl8169_private *tp, struct phy_device *phydev) { @@ -1081,44 +995,6 @@ static void rtl8125_legacy_force_mode(struct phy_device *phydev) phy_modify_paged(phydev, 0xa5b, 0x12, BIT(15), 0); } -static void rtl8125a_1_hw_phy_config(struct rtl8169_private *tp, - struct phy_device *phydev) -{ - phy_modify_paged(phydev, 0xad4, 0x10, 0x03ff, 0x0084); - phy_modify_paged(phydev, 0xad4, 0x17, 0x0000, 0x0010); - phy_modify_paged(phydev, 0xad1, 0x13, 0x03ff, 0x0006); - phy_modify_paged(phydev, 0xad3, 0x11, 0x003f, 0x0006); - phy_modify_paged(phydev, 0xac0, 0x14, 0x0000, 0x1100); - phy_modify_paged(phydev, 0xac8, 0x15, 0xf000, 0x7000); - phy_modify_paged(phydev, 0xad1, 0x14, 0x0000, 0x0400); - phy_modify_paged(phydev, 0xad1, 0x15, 0x0000, 0x03ff); - phy_modify_paged(phydev, 0xad1, 0x16, 0x0000, 0x03ff); - - r8168g_phy_param(phydev, 0x80ea, 0xff00, 0xc400); - r8168g_phy_param(phydev, 0x80eb, 0x0700, 0x0300); - r8168g_phy_param(phydev, 0x80f8, 0xff00, 0x1c00); - r8168g_phy_param(phydev, 0x80f1, 0xff00, 0x3000); - r8168g_phy_param(phydev, 0x80fe, 0xff00, 0xa500); - r8168g_phy_param(phydev, 0x8102, 0xff00, 0x5000); - r8168g_phy_param(phydev, 0x8105, 0xff00, 0x3300); - r8168g_phy_param(phydev, 0x8100, 0xff00, 0x7000); - r8168g_phy_param(phydev, 0x8104, 0xff00, 0xf000); - r8168g_phy_param(phydev, 0x8106, 0xff00, 0x6500); - r8168g_phy_param(phydev, 0x80dc, 0xff00, 0xed00); - r8168g_phy_param(phydev, 0x80df, 0x0000, 0x0100); - r8168g_phy_param(phydev, 0x80e1, 0x0100, 0x0000); - - phy_modify_paged(phydev, 0xbf0, 0x13, 0x003f, 0x0038); - r8168g_phy_param(phydev, 0x819f, 0xffff, 0xd0b6); - - phy_write_paged(phydev, 0xbc3, 0x12, 0x5555); - phy_modify_paged(phydev, 0xbf0, 0x15, 0x0e00, 0x0a00); - phy_modify_paged(phydev, 0xa5c, 0x10, 0x0400, 0x0000); - rtl8168g_enable_gphy_10m(phydev); - - rtl8125a_config_eee_phy(phydev); -} - static void rtl8125a_2_hw_phy_config(struct rtl8169_private *tp, struct phy_device *phydev) { @@ -1239,10 +1115,7 @@ void r8169_hw_phy_config(struct rtl8169_private *tp, struct phy_device *phydev, [RTL_GIGA_MAC_VER_09] = rtl8102e_hw_phy_config, [RTL_GIGA_MAC_VER_10] = NULL, [RTL_GIGA_MAC_VER_11] = rtl8168bb_hw_phy_config, - [RTL_GIGA_MAC_VER_12] = rtl8168bef_hw_phy_config, - [RTL_GIGA_MAC_VER_13] = NULL, [RTL_GIGA_MAC_VER_14] = rtl8401_hw_phy_config, - [RTL_GIGA_MAC_VER_16] = NULL, [RTL_GIGA_MAC_VER_17] = rtl8168bef_hw_phy_config, [RTL_GIGA_MAC_VER_18] = rtl8168cp_1_hw_phy_config, [RTL_GIGA_MAC_VER_19] = rtl8168c_1_hw_phy_config, @@ -1266,20 +1139,14 @@ void r8169_hw_phy_config(struct rtl8169_private *tp, struct phy_device *phydev, [RTL_GIGA_MAC_VER_38] = rtl8411_hw_phy_config, [RTL_GIGA_MAC_VER_39] = rtl8106e_hw_phy_config, [RTL_GIGA_MAC_VER_40] = rtl8168g_1_hw_phy_config, - [RTL_GIGA_MAC_VER_41] = NULL, [RTL_GIGA_MAC_VER_42] = rtl8168g_2_hw_phy_config, [RTL_GIGA_MAC_VER_43] = rtl8168g_2_hw_phy_config, [RTL_GIGA_MAC_VER_44] = rtl8168g_2_hw_phy_config, - [RTL_GIGA_MAC_VER_45] = rtl8168h_1_hw_phy_config, [RTL_GIGA_MAC_VER_46] = rtl8168h_2_hw_phy_config, - [RTL_GIGA_MAC_VER_47] = rtl8168h_1_hw_phy_config, [RTL_GIGA_MAC_VER_48] = rtl8168h_2_hw_phy_config, - [RTL_GIGA_MAC_VER_49] = rtl8168ep_1_hw_phy_config, - [RTL_GIGA_MAC_VER_50] = rtl8168ep_2_hw_phy_config, [RTL_GIGA_MAC_VER_51] = rtl8168ep_2_hw_phy_config, [RTL_GIGA_MAC_VER_52] = rtl8117_hw_phy_config, [RTL_GIGA_MAC_VER_53] = rtl8117_hw_phy_config, - [RTL_GIGA_MAC_VER_60] = rtl8125a_1_hw_phy_config, [RTL_GIGA_MAC_VER_61] = rtl8125a_2_hw_phy_config, [RTL_GIGA_MAC_VER_63] = rtl8125b_hw_phy_config, }; diff --git a/drivers/net/ethernet/renesas/ravb.h b/drivers/net/ethernet/renesas/ravb.h index b980bce763d3..e0f8276cffed 100644 --- a/drivers/net/ethernet/renesas/ravb.h +++ b/drivers/net/ethernet/renesas/ravb.h @@ -189,6 +189,7 @@ enum ravb_reg { PSR = 0x0528, PIPR = 0x052c, CXR31 = 0x0530, /* RZ/G2L only */ + CXR35 = 0x0540, /* RZ/G2L only */ MPR = 0x0558, PFTCR = 0x055c, PFRCR = 0x0560, @@ -965,6 +966,13 @@ enum CXR31_BIT { CXR31_SEL_LINK1 = 0x00000008, }; +enum CXR35_BIT { + CXR35_SEL_XMII = 0x00000003, + CXR35_SEL_XMII_RGMII = 0x00000000, + CXR35_SEL_XMII_MII = 0x00000002, + CXR35_HALFCYC_CLKSW = 0xffff0000, +}; + enum CSR0_BIT { CSR0_TPE = 0x00000010, CSR0_RPE = 0x00000020, diff --git a/drivers/net/ethernet/renesas/ravb_main.c b/drivers/net/ethernet/renesas/ravb_main.c index 7e32b04eb0c7..36324126db6d 100644 --- a/drivers/net/ethernet/renesas/ravb_main.c +++ b/drivers/net/ethernet/renesas/ravb_main.c @@ -540,7 +540,13 @@ static void ravb_emac_init_gbeth(struct net_device *ndev) /* E-MAC interrupt enable register */ ravb_write(ndev, ECSIPR_ICDIP, ECSIPR); - ravb_modify(ndev, CXR31, CXR31_SEL_LINK0 | CXR31_SEL_LINK1, CXR31_SEL_LINK0); + if (priv->phy_interface == PHY_INTERFACE_MODE_MII) { + ravb_modify(ndev, CXR31, CXR31_SEL_LINK0 | CXR31_SEL_LINK1, 0); + ravb_write(ndev, (1000 << 16) | CXR35_SEL_XMII_MII, CXR35); + } else { + ravb_modify(ndev, CXR31, CXR31_SEL_LINK0 | CXR31_SEL_LINK1, + CXR31_SEL_LINK0); + } } static void ravb_emac_init_rcar(struct net_device *ndev) @@ -2514,6 +2520,7 @@ static const struct of_device_id ravb_match_table[] = { { .compatible = "renesas,etheravb-rcar-gen2", .data = &ravb_gen2_hw_info }, { .compatible = "renesas,etheravb-r8a7795", .data = &ravb_gen3_hw_info }, { .compatible = "renesas,etheravb-rcar-gen3", .data = &ravb_gen3_hw_info }, + { .compatible = "renesas,etheravb-rcar-gen4", .data = &ravb_gen3_hw_info }, { .compatible = "renesas,etheravb-rzv2m", .data = &ravb_rzv2m_hw_info }, { .compatible = "renesas,rzg2l-gbeth", .data = &gbeth_hw_info }, { } @@ -2834,9 +2841,9 @@ static int ravb_probe(struct platform_device *pdev) goto out_dma_free; } - netif_napi_add(ndev, &priv->napi[RAVB_BE], ravb_poll, 64); + netif_napi_add(ndev, &priv->napi[RAVB_BE], ravb_poll); if (info->nc_queues) - netif_napi_add(ndev, &priv->napi[RAVB_NC], ravb_poll, 64); + netif_napi_add(ndev, &priv->napi[RAVB_NC], ravb_poll); /* Network device register */ error = register_netdev(ndev); diff --git a/drivers/net/ethernet/renesas/sh_eth.c b/drivers/net/ethernet/renesas/sh_eth.c index 7fd8828d3a84..71a499113308 100644 --- a/drivers/net/ethernet/renesas/sh_eth.c +++ b/drivers/net/ethernet/renesas/sh_eth.c @@ -3368,7 +3368,7 @@ static int sh_eth_drv_probe(struct platform_device *pdev) goto out_release; } - netif_napi_add(ndev, &mdp->napi, sh_eth_poll, 64); + netif_napi_add(ndev, &mdp->napi, sh_eth_poll); /* network device register */ ret = register_netdev(ndev); diff --git a/drivers/net/ethernet/rocker/rocker_main.c b/drivers/net/ethernet/rocker/rocker_main.c index fc83ec23bd1d..023682cd2768 100644 --- a/drivers/net/ethernet/rocker/rocker_main.c +++ b/drivers/net/ethernet/rocker/rocker_main.c @@ -2226,8 +2226,8 @@ rocker_port_set_link_ksettings(struct net_device *dev, static void rocker_port_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *drvinfo) { - strlcpy(drvinfo->driver, rocker_driver_name, sizeof(drvinfo->driver)); - strlcpy(drvinfo->version, UTS_RELEASE, sizeof(drvinfo->version)); + strscpy(drvinfo->driver, rocker_driver_name, sizeof(drvinfo->driver)); + strscpy(drvinfo->version, UTS_RELEASE, sizeof(drvinfo->version)); } static struct rocker_port_stats { @@ -2574,8 +2574,7 @@ static int rocker_probe_port(struct rocker *rocker, unsigned int port_number) dev->netdev_ops = &rocker_port_netdev_ops; dev->ethtool_ops = &rocker_port_ethtool_ops; netif_napi_add_tx(dev, &rocker_port->napi_tx, rocker_port_poll_tx); - netif_napi_add(dev, &rocker_port->napi_rx, rocker_port_poll_rx, - NAPI_POLL_WEIGHT); + netif_napi_add(dev, &rocker_port->napi_rx, rocker_port_poll_rx); rocker_carrier_init(rocker_port); dev->features |= NETIF_F_NETNS_LOCAL | NETIF_F_SG; diff --git a/drivers/net/ethernet/samsung/sxgbe/sxgbe_ethtool.c b/drivers/net/ethernet/samsung/sxgbe/sxgbe_ethtool.c index 98edb01024f0..8ba017ec9849 100644 --- a/drivers/net/ethernet/samsung/sxgbe/sxgbe_ethtool.c +++ b/drivers/net/ethernet/samsung/sxgbe/sxgbe_ethtool.c @@ -175,8 +175,8 @@ static int sxgbe_set_eee(struct net_device *dev, static void sxgbe_getdrvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { - strlcpy(info->driver, KBUILD_MODNAME, sizeof(info->driver)); - strlcpy(info->version, DRV_VERSION, sizeof(info->version)); + strscpy(info->driver, KBUILD_MODNAME, sizeof(info->driver)); + strscpy(info->version, DRV_VERSION, sizeof(info->version)); } static u32 sxgbe_getmsglevel(struct net_device *dev) diff --git a/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c b/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c index a1c10b61269b..9664f029fa16 100644 --- a/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c +++ b/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c @@ -2143,7 +2143,7 @@ struct sxgbe_priv_data *sxgbe_drv_probe(struct device *device, pr_info("Enable RX Mitigation via HW Watchdog Timer\n"); } - netif_napi_add(ndev, &priv->napi, sxgbe_poll, 64); + netif_napi_add(ndev, &priv->napi, sxgbe_poll); spin_lock_init(&priv->stats_lock); diff --git a/drivers/net/ethernet/sfc/Makefile b/drivers/net/ethernet/sfc/Makefile index bb06fa228367..b5e45fc6337e 100644 --- a/drivers/net/ethernet/sfc/Makefile +++ b/drivers/net/ethernet/sfc/Makefile @@ -9,7 +9,7 @@ sfc-y += efx.o efx_common.o efx_channels.o nic.o \ ef100_ethtool.o ef100_rx.o ef100_tx.o sfc-$(CONFIG_SFC_MTD) += mtd.o sfc-$(CONFIG_SFC_SRIOV) += sriov.o ef10_sriov.o ef100_sriov.o ef100_rep.o \ - mae.o tc.o + mae.o tc.o tc_bindings.o obj-$(CONFIG_SFC) += sfc.o diff --git a/drivers/net/ethernet/sfc/ef100_ethtool.c b/drivers/net/ethernet/sfc/ef100_ethtool.c index 702abbe59b76..135ece2f1375 100644 --- a/drivers/net/ethernet/sfc/ef100_ethtool.c +++ b/drivers/net/ethernet/sfc/ef100_ethtool.c @@ -43,6 +43,8 @@ const struct ethtool_ops ef100_ethtool_ops = { .get_pauseparam = efx_ethtool_get_pauseparam, .set_pauseparam = efx_ethtool_set_pauseparam, .get_sset_count = efx_ethtool_get_sset_count, + .get_priv_flags = efx_ethtool_get_priv_flags, + .set_priv_flags = efx_ethtool_set_priv_flags, .self_test = efx_ethtool_self_test, .get_strings = efx_ethtool_get_strings, .get_link_ksettings = efx_ethtool_get_link_ksettings, diff --git a/drivers/net/ethernet/sfc/ef100_netdev.c b/drivers/net/ethernet/sfc/ef100_netdev.c index 17b9d37218cb..88fa29572e23 100644 --- a/drivers/net/ethernet/sfc/ef100_netdev.c +++ b/drivers/net/ethernet/sfc/ef100_netdev.c @@ -23,6 +23,7 @@ #include "mcdi_filters.h" #include "rx_common.h" #include "ef100_sriov.h" +#include "tc_bindings.h" static void ef100_update_name(struct efx_nic *efx) { @@ -246,6 +247,9 @@ static const struct net_device_ops ef100_netdev_ops = { #ifdef CONFIG_RFS_ACCEL .ndo_rx_flow_steer = efx_filter_rfs, #endif +#ifdef CONFIG_SFC_SRIOV + .ndo_setup_tc = efx_tc_setup, +#endif }; /* Netdev registration diff --git a/drivers/net/ethernet/sfc/ef100_nic.c b/drivers/net/ethernet/sfc/ef100_nic.c index 8061efdaf82c..ad686c671ab8 100644 --- a/drivers/net/ethernet/sfc/ef100_nic.c +++ b/drivers/net/ethernet/sfc/ef100_nic.c @@ -1137,6 +1137,9 @@ int ef100_probe_netdev_pf(struct efx_nic *efx) */ netif_warn(efx, probe, net_dev, "Failed to probe MAE rc %d\n", rc); + } else { + net_dev->features |= NETIF_F_HW_TC; + efx->fixed_features |= NETIF_F_HW_TC; } #endif return 0; diff --git a/drivers/net/ethernet/sfc/ef100_rep.c b/drivers/net/ethernet/sfc/ef100_rep.c index 73ae4656a6e7..81ab22c74635 100644 --- a/drivers/net/ethernet/sfc/ef100_rep.c +++ b/drivers/net/ethernet/sfc/ef100_rep.c @@ -14,6 +14,7 @@ #include "ef100_nic.h" #include "mae.h" #include "rx_common.h" +#include "tc_bindings.h" #define EFX_EF100_REP_DRIVER "efx_ef100_rep" @@ -42,8 +43,7 @@ static int efx_ef100_rep_open(struct net_device *net_dev) { struct efx_rep *efv = netdev_priv(net_dev); - netif_napi_add(net_dev, &efv->napi, efx_ef100_rep_poll, - NAPI_POLL_WEIGHT); + netif_napi_add(net_dev, &efv->napi, efx_ef100_rep_poll); napi_enable(&efv->napi); return 0; } @@ -107,6 +107,20 @@ static int efx_ef100_rep_get_phys_port_name(struct net_device *dev, return 0; } +static int efx_ef100_rep_setup_tc(struct net_device *net_dev, + enum tc_setup_type type, void *type_data) +{ + struct efx_rep *efv = netdev_priv(net_dev); + struct efx_nic *efx = efv->parent; + + if (type == TC_SETUP_CLSFLOWER) + return efx_tc_flower(efx, net_dev, type_data, efv); + if (type == TC_SETUP_BLOCK) + return efx_tc_setup_block(net_dev, efx, type_data, efv); + + return -EOPNOTSUPP; +} + static void efx_ef100_rep_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats) { @@ -120,13 +134,14 @@ static void efx_ef100_rep_get_stats64(struct net_device *dev, stats->tx_errors = atomic64_read(&efv->stats.tx_errors); } -static const struct net_device_ops efx_ef100_rep_netdev_ops = { +const struct net_device_ops efx_ef100_rep_netdev_ops = { .ndo_open = efx_ef100_rep_open, .ndo_stop = efx_ef100_rep_close, .ndo_start_xmit = efx_ef100_rep_xmit, .ndo_get_port_parent_id = efx_ef100_rep_get_port_parent_id, .ndo_get_phys_port_name = efx_ef100_rep_get_phys_port_name, .ndo_get_stats64 = efx_ef100_rep_get_stats64, + .ndo_setup_tc = efx_ef100_rep_setup_tc, }; static void efx_ef100_rep_get_drvinfo(struct net_device *dev, diff --git a/drivers/net/ethernet/sfc/ef100_rep.h b/drivers/net/ethernet/sfc/ef100_rep.h index 070f700893c1..c21bc716f847 100644 --- a/drivers/net/ethernet/sfc/ef100_rep.h +++ b/drivers/net/ethernet/sfc/ef100_rep.h @@ -66,4 +66,5 @@ void efx_ef100_rep_rx_packet(struct efx_rep *efv, struct efx_rx_buffer *rx_buf); * Caller must hold rcu_read_lock(). */ struct efx_rep *efx_ef100_find_rep_by_mport(struct efx_nic *efx, u16 mport); +extern const struct net_device_ops efx_ef100_rep_netdev_ops; #endif /* EF100_REP_H */ diff --git a/drivers/net/ethernet/sfc/efx.c b/drivers/net/ethernet/sfc/efx.c index 153d68e29b8b..054d5ce6029e 100644 --- a/drivers/net/ethernet/sfc/efx.c +++ b/drivers/net/ethernet/sfc/efx.c @@ -778,7 +778,7 @@ static void efx_unregister_netdev(struct efx_nic *efx) return; if (efx_dev_registered(efx)) { - strlcpy(efx->name, pci_name(efx->pci_dev), sizeof(efx->name)); + strscpy(efx->name, pci_name(efx->pci_dev), sizeof(efx->name)); efx_fini_mcdi_logging(efx); device_remove_file(&efx->pci_dev->dev, &dev_attr_phy_type); unregister_netdev(efx->net_dev); @@ -1175,6 +1175,17 @@ static int efx_pm_freeze(struct device *dev) return 0; } +static void efx_pci_shutdown(struct pci_dev *pci_dev) +{ + struct efx_nic *efx = pci_get_drvdata(pci_dev); + + if (!efx) + return; + + efx_pm_freeze(&pci_dev->dev); + pci_disable_device(pci_dev); +} + static int efx_pm_thaw(struct device *dev) { int rc; @@ -1279,6 +1290,7 @@ static struct pci_driver efx_pci_driver = { .probe = efx_pci_probe, .remove = efx_pci_remove, .driver.pm = &efx_pm_ops, + .shutdown = efx_pci_shutdown, .err_handler = &efx_err_handlers, #ifdef CONFIG_SFC_SRIOV .sriov_configure = efx_pci_sriov_configure, diff --git a/drivers/net/ethernet/sfc/efx_channels.c b/drivers/net/ethernet/sfc/efx_channels.c index 5b4d661ab986..aaa381743bca 100644 --- a/drivers/net/ethernet/sfc/efx_channels.c +++ b/drivers/net/ethernet/sfc/efx_channels.c @@ -1313,7 +1313,7 @@ void efx_init_napi_channel(struct efx_channel *channel) struct efx_nic *efx = channel->efx; channel->napi_dev = efx->net_dev; - netif_napi_add(channel->napi_dev, &channel->napi_str, efx_poll, 64); + netif_napi_add(channel->napi_dev, &channel->napi_str, efx_poll); } void efx_init_napi(struct efx_nic *efx) diff --git a/drivers/net/ethernet/sfc/efx_common.c b/drivers/net/ethernet/sfc/efx_common.c index a929a1aaba92..c2224e41a694 100644 --- a/drivers/net/ethernet/sfc/efx_common.c +++ b/drivers/net/ethernet/sfc/efx_common.c @@ -996,7 +996,7 @@ int efx_init_struct(struct efx_nic *efx, struct pci_dev *pci_dev) efx->pci_dev = pci_dev; efx->msg_enable = debug; efx->state = STATE_UNINIT; - strlcpy(efx->name, pci_name(pci_dev), sizeof(efx->name)); + strscpy(efx->name, pci_name(pci_dev), sizeof(efx->name)); efx->rx_prefix_size = efx->type->rx_prefix_size; efx->rx_ip_align = diff --git a/drivers/net/ethernet/sfc/ethtool_common.c b/drivers/net/ethernet/sfc/ethtool_common.c index bc840ede3053..6649a2327d03 100644 --- a/drivers/net/ethernet/sfc/ethtool_common.c +++ b/drivers/net/ethernet/sfc/ethtool_common.c @@ -101,15 +101,23 @@ static const struct efx_sw_stat_desc efx_sw_stat_desc[] = { #define EFX_ETHTOOL_SW_STAT_COUNT ARRAY_SIZE(efx_sw_stat_desc) +static const char efx_ethtool_priv_flags_strings[][ETH_GSTRING_LEN] = { + "log-tc-errors", +}; + +#define EFX_ETHTOOL_PRIV_FLAGS_LOG_TC_ERRS BIT(0) + +#define EFX_ETHTOOL_PRIV_FLAGS_COUNT ARRAY_SIZE(efx_ethtool_priv_flags_strings) + void efx_ethtool_get_drvinfo(struct net_device *net_dev, struct ethtool_drvinfo *info) { struct efx_nic *efx = efx_netdev_priv(net_dev); - strlcpy(info->driver, KBUILD_MODNAME, sizeof(info->driver)); + strscpy(info->driver, KBUILD_MODNAME, sizeof(info->driver)); efx_mcdi_print_fwver(efx, info->fw_version, sizeof(info->fw_version)); - strlcpy(info->bus_info, pci_name(efx->pci_dev), sizeof(info->bus_info)); + strscpy(info->bus_info, pci_name(efx->pci_dev), sizeof(info->bus_info)); } u32 efx_ethtool_get_msglevel(struct net_device *net_dev) @@ -452,6 +460,8 @@ int efx_ethtool_get_sset_count(struct net_device *net_dev, int string_set) efx_ptp_describe_stats(efx, NULL); case ETH_SS_TEST: return efx_ethtool_fill_self_tests(efx, NULL, NULL, NULL); + case ETH_SS_PRIV_FLAGS: + return EFX_ETHTOOL_PRIV_FLAGS_COUNT; default: return -EINVAL; } @@ -468,7 +478,7 @@ void efx_ethtool_get_strings(struct net_device *net_dev, strings += (efx->type->describe_stats(efx, strings) * ETH_GSTRING_LEN); for (i = 0; i < EFX_ETHTOOL_SW_STAT_COUNT; i++) - strlcpy(strings + i * ETH_GSTRING_LEN, + strscpy(strings + i * ETH_GSTRING_LEN, efx_sw_stat_desc[i].name, ETH_GSTRING_LEN); strings += EFX_ETHTOOL_SW_STAT_COUNT * ETH_GSTRING_LEN; strings += (efx_describe_per_queue_stats(efx, strings) * @@ -478,12 +488,39 @@ void efx_ethtool_get_strings(struct net_device *net_dev, case ETH_SS_TEST: efx_ethtool_fill_self_tests(efx, NULL, strings, NULL); break; + case ETH_SS_PRIV_FLAGS: + for (i = 0; i < EFX_ETHTOOL_PRIV_FLAGS_COUNT; i++) + strscpy(strings + i * ETH_GSTRING_LEN, + efx_ethtool_priv_flags_strings[i], + ETH_GSTRING_LEN); + break; default: /* No other string sets */ break; } } +u32 efx_ethtool_get_priv_flags(struct net_device *net_dev) +{ + struct efx_nic *efx = efx_netdev_priv(net_dev); + u32 ret_flags = 0; + + if (efx->log_tc_errs) + ret_flags |= EFX_ETHTOOL_PRIV_FLAGS_LOG_TC_ERRS; + + return ret_flags; +} + +int efx_ethtool_set_priv_flags(struct net_device *net_dev, u32 flags) +{ + struct efx_nic *efx = efx_netdev_priv(net_dev); + + efx->log_tc_errs = + !!(flags & EFX_ETHTOOL_PRIV_FLAGS_LOG_TC_ERRS); + + return 0; +} + void efx_ethtool_get_stats(struct net_device *net_dev, struct ethtool_stats *stats, u64 *data) diff --git a/drivers/net/ethernet/sfc/ethtool_common.h b/drivers/net/ethernet/sfc/ethtool_common.h index 659491932101..0afc74021a5e 100644 --- a/drivers/net/ethernet/sfc/ethtool_common.h +++ b/drivers/net/ethernet/sfc/ethtool_common.h @@ -27,6 +27,8 @@ int efx_ethtool_fill_self_tests(struct efx_nic *efx, int efx_ethtool_get_sset_count(struct net_device *net_dev, int string_set); void efx_ethtool_get_strings(struct net_device *net_dev, u32 string_set, u8 *strings); +u32 efx_ethtool_get_priv_flags(struct net_device *net_dev); +int efx_ethtool_set_priv_flags(struct net_device *net_dev, u32 flags); void efx_ethtool_get_stats(struct net_device *net_dev, struct ethtool_stats *stats __attribute__ ((unused)), u64 *data); diff --git a/drivers/net/ethernet/sfc/falcon/efx.c b/drivers/net/ethernet/sfc/falcon/efx.c index a63f40b09856..e151b0957751 100644 --- a/drivers/net/ethernet/sfc/falcon/efx.c +++ b/drivers/net/ethernet/sfc/falcon/efx.c @@ -2012,7 +2012,7 @@ static void ef4_init_napi_channel(struct ef4_channel *channel) struct ef4_nic *efx = channel->efx; channel->napi_dev = efx->net_dev; - netif_napi_add(channel->napi_dev, &channel->napi_str, ef4_poll, 64); + netif_napi_add(channel->napi_dev, &channel->napi_str, ef4_poll); } static void ef4_init_napi(struct ef4_nic *efx) @@ -2329,7 +2329,7 @@ static void ef4_unregister_netdev(struct ef4_nic *efx) BUG_ON(netdev_priv(efx->net_dev) != efx); if (ef4_dev_registered(efx)) { - strlcpy(efx->name, pci_name(efx->pci_dev), sizeof(efx->name)); + strscpy(efx->name, pci_name(efx->pci_dev), sizeof(efx->name)); device_remove_file(&efx->pci_dev->dev, &dev_attr_phy_type); unregister_netdev(efx->net_dev); } @@ -2640,7 +2640,7 @@ static int ef4_init_struct(struct ef4_nic *efx, efx->pci_dev = pci_dev; efx->msg_enable = debug; efx->state = STATE_UNINIT; - strlcpy(efx->name, pci_name(pci_dev), sizeof(efx->name)); + strscpy(efx->name, pci_name(pci_dev), sizeof(efx->name)); efx->net_dev = net_dev; efx->rx_prefix_size = efx->type->rx_prefix_size; diff --git a/drivers/net/ethernet/sfc/falcon/ethtool.c b/drivers/net/ethernet/sfc/falcon/ethtool.c index 907254b36663..3976a333f7e3 100644 --- a/drivers/net/ethernet/sfc/falcon/ethtool.c +++ b/drivers/net/ethernet/sfc/falcon/ethtool.c @@ -162,9 +162,9 @@ static void ef4_ethtool_get_drvinfo(struct net_device *net_dev, { struct ef4_nic *efx = netdev_priv(net_dev); - strlcpy(info->driver, KBUILD_MODNAME, sizeof(info->driver)); - strlcpy(info->version, EF4_DRIVER_VERSION, sizeof(info->version)); - strlcpy(info->bus_info, pci_name(efx->pci_dev), sizeof(info->bus_info)); + strscpy(info->driver, KBUILD_MODNAME, sizeof(info->driver)); + strscpy(info->version, EF4_DRIVER_VERSION, sizeof(info->version)); + strscpy(info->bus_info, pci_name(efx->pci_dev), sizeof(info->bus_info)); } static int ef4_ethtool_get_regs_len(struct net_device *net_dev) @@ -412,7 +412,7 @@ static void ef4_ethtool_get_strings(struct net_device *net_dev, strings += (efx->type->describe_stats(efx, strings) * ETH_GSTRING_LEN); for (i = 0; i < EF4_ETHTOOL_SW_STAT_COUNT; i++) - strlcpy(strings + i * ETH_GSTRING_LEN, + strscpy(strings + i * ETH_GSTRING_LEN, ef4_sw_stat_desc[i].name, ETH_GSTRING_LEN); strings += EF4_ETHTOOL_SW_STAT_COUNT * ETH_GSTRING_LEN; strings += (ef4_describe_per_queue_stats(efx, strings) * diff --git a/drivers/net/ethernet/sfc/falcon/falcon.c b/drivers/net/ethernet/sfc/falcon/falcon.c index 3324a6219a09..7a1c9337081b 100644 --- a/drivers/net/ethernet/sfc/falcon/falcon.c +++ b/drivers/net/ethernet/sfc/falcon/falcon.c @@ -2387,7 +2387,7 @@ static int falcon_probe_nic(struct ef4_nic *efx) board->i2c_data.data = efx; board->i2c_adap.algo_data = &board->i2c_data; board->i2c_adap.dev.parent = &efx->pci_dev->dev; - strlcpy(board->i2c_adap.name, "SFC4000 GPIO", + strscpy(board->i2c_adap.name, "SFC4000 GPIO", sizeof(board->i2c_adap.name)); rc = i2c_bit_add_bus(&board->i2c_adap); if (rc) diff --git a/drivers/net/ethernet/sfc/falcon/nic.c b/drivers/net/ethernet/sfc/falcon/nic.c index 156da315ec89..78c851b5a56f 100644 --- a/drivers/net/ethernet/sfc/falcon/nic.c +++ b/drivers/net/ethernet/sfc/falcon/nic.c @@ -452,7 +452,7 @@ size_t ef4_nic_describe_stats(const struct ef4_hw_stat_desc *desc, size_t count, for_each_set_bit(index, mask, count) { if (desc[index].name) { if (names) { - strlcpy(names, desc[index].name, + strscpy(names, desc[index].name, ETH_GSTRING_LEN); names += ETH_GSTRING_LEN; } diff --git a/drivers/net/ethernet/sfc/filter.h b/drivers/net/ethernet/sfc/filter.h index 4d928839d292..be72e71da027 100644 --- a/drivers/net/ethernet/sfc/filter.h +++ b/drivers/net/ethernet/sfc/filter.h @@ -9,6 +9,7 @@ #include <linux/types.h> #include <linux/if_ether.h> +#include <linux/in6.h> #include <asm/byteorder.h> /** @@ -224,6 +225,27 @@ efx_filter_set_ipv4_local(struct efx_filter_spec *spec, u8 proto, } /** + * efx_filter_set_ipv6_local - specify IPv6 host, transport protocol and port + * @spec: Specification to initialise + * @proto: Transport layer protocol number + * @host: Local host address (network byte order) + * @port: Local port (network byte order) + */ +static inline int +efx_filter_set_ipv6_local(struct efx_filter_spec *spec, u8 proto, + const struct in6_addr *host, __be16 port) +{ + spec->match_flags |= + EFX_FILTER_MATCH_ETHER_TYPE | EFX_FILTER_MATCH_IP_PROTO | + EFX_FILTER_MATCH_LOC_HOST | EFX_FILTER_MATCH_LOC_PORT; + spec->ether_type = htons(ETH_P_IPV6); + spec->ip_proto = proto; + memcpy(spec->loc_host, host, sizeof(spec->loc_host)); + spec->loc_port = port; + return 0; +} + +/** * efx_filter_set_ipv4_full - specify IPv4 hosts, transport protocol and ports * @spec: Specification to initialise * @proto: Transport layer protocol number diff --git a/drivers/net/ethernet/sfc/mae.c b/drivers/net/ethernet/sfc/mae.c index 97627f5e3674..874c765b2465 100644 --- a/drivers/net/ethernet/sfc/mae.c +++ b/drivers/net/ethernet/sfc/mae.c @@ -112,6 +112,167 @@ int efx_mae_lookup_mport(struct efx_nic *efx, u32 selector, u32 *id) return 0; } +static int efx_mae_get_basic_caps(struct efx_nic *efx, struct mae_caps *caps) +{ + MCDI_DECLARE_BUF(outbuf, MC_CMD_MAE_GET_CAPS_OUT_LEN); + size_t outlen; + int rc; + + BUILD_BUG_ON(MC_CMD_MAE_GET_CAPS_IN_LEN); + + rc = efx_mcdi_rpc(efx, MC_CMD_MAE_GET_CAPS, NULL, 0, outbuf, + sizeof(outbuf), &outlen); + if (rc) + return rc; + if (outlen < sizeof(outbuf)) + return -EIO; + caps->match_field_count = MCDI_DWORD(outbuf, MAE_GET_CAPS_OUT_MATCH_FIELD_COUNT); + caps->action_prios = MCDI_DWORD(outbuf, MAE_GET_CAPS_OUT_ACTION_PRIOS); + return 0; +} + +static int efx_mae_get_rule_fields(struct efx_nic *efx, u32 cmd, + u8 *field_support) +{ + MCDI_DECLARE_BUF(outbuf, MC_CMD_MAE_GET_AR_CAPS_OUT_LEN(MAE_NUM_FIELDS)); + MCDI_DECLARE_STRUCT_PTR(caps); + unsigned int count; + size_t outlen; + int rc, i; + + BUILD_BUG_ON(MC_CMD_MAE_GET_AR_CAPS_IN_LEN); + + rc = efx_mcdi_rpc(efx, cmd, NULL, 0, outbuf, sizeof(outbuf), &outlen); + if (rc) + return rc; + count = MCDI_DWORD(outbuf, MAE_GET_AR_CAPS_OUT_COUNT); + memset(field_support, MAE_FIELD_UNSUPPORTED, MAE_NUM_FIELDS); + caps = _MCDI_DWORD(outbuf, MAE_GET_AR_CAPS_OUT_FIELD_FLAGS); + /* We're only interested in the support status enum, not any other + * flags, so just extract that from each entry. + */ + for (i = 0; i < count; i++) + if (i * sizeof(*outbuf) + MC_CMD_MAE_GET_AR_CAPS_OUT_FIELD_FLAGS_OFST < outlen) + field_support[i] = EFX_DWORD_FIELD(caps[i], MAE_FIELD_FLAGS_SUPPORT_STATUS); + return 0; +} + +int efx_mae_get_caps(struct efx_nic *efx, struct mae_caps *caps) +{ + int rc; + + rc = efx_mae_get_basic_caps(efx, caps); + if (rc) + return rc; + return efx_mae_get_rule_fields(efx, MC_CMD_MAE_GET_AR_CAPS, + caps->action_rule_fields); +} + +/* Bit twiddling: + * Prefix: 1...110...0 + * ~: 0...001...1 + * + 1: 0...010...0 is power of two + * so (~x) & ((~x) + 1) == 0. Converse holds also. + */ +#define is_prefix_byte(_x) !(((_x) ^ 0xff) & (((_x) ^ 0xff) + 1)) + +enum mask_type { MASK_ONES, MASK_ZEROES, MASK_PREFIX, MASK_OTHER }; + +static const char *mask_type_name(enum mask_type typ) +{ + switch (typ) { + case MASK_ONES: + return "all-1s"; + case MASK_ZEROES: + return "all-0s"; + case MASK_PREFIX: + return "prefix"; + case MASK_OTHER: + return "arbitrary"; + default: /* can't happen */ + return "unknown"; + } +} + +/* Checks a (big-endian) bytestring is a bit prefix */ +static enum mask_type classify_mask(const u8 *mask, size_t len) +{ + bool zeroes = true; /* All bits seen so far are zeroes */ + bool ones = true; /* All bits seen so far are ones */ + bool prefix = true; /* Valid prefix so far */ + size_t i; + + for (i = 0; i < len; i++) { + if (ones) { + if (!is_prefix_byte(mask[i])) + prefix = false; + } else if (mask[i]) { + prefix = false; + } + if (mask[i] != 0xff) + ones = false; + if (mask[i]) + zeroes = false; + } + if (ones) + return MASK_ONES; + if (zeroes) + return MASK_ZEROES; + if (prefix) + return MASK_PREFIX; + return MASK_OTHER; +} + +static int efx_mae_match_check_cap_typ(u8 support, enum mask_type typ) +{ + switch (support) { + case MAE_FIELD_UNSUPPORTED: + case MAE_FIELD_SUPPORTED_MATCH_NEVER: + if (typ == MASK_ZEROES) + return 0; + return -EOPNOTSUPP; + case MAE_FIELD_SUPPORTED_MATCH_OPTIONAL: + if (typ == MASK_ZEROES) + return 0; + fallthrough; + case MAE_FIELD_SUPPORTED_MATCH_ALWAYS: + if (typ == MASK_ONES) + return 0; + return -EINVAL; + case MAE_FIELD_SUPPORTED_MATCH_PREFIX: + if (typ == MASK_OTHER) + return -EOPNOTSUPP; + return 0; + case MAE_FIELD_SUPPORTED_MATCH_MASK: + return 0; + default: + return -EIO; + } +} + +int efx_mae_match_check_caps(struct efx_nic *efx, + const struct efx_tc_match_fields *mask, + struct netlink_ext_ack *extack) +{ + const u8 *supported_fields = efx->tc->caps->action_rule_fields; + __be32 ingress_port = cpu_to_be32(mask->ingress_port); + enum mask_type ingress_port_mask_type; + int rc; + + /* Check for _PREFIX assumes big-endian, so we need to convert */ + ingress_port_mask_type = classify_mask((const u8 *)&ingress_port, + sizeof(ingress_port)); + rc = efx_mae_match_check_cap_typ(supported_fields[MAE_FIELD_INGRESS_PORT], + ingress_port_mask_type); + if (rc) { + efx_tc_err(efx, "No support for %s mask in field ingress_port\n", + mask_type_name(ingress_port_mask_type)); + NL_SET_ERR_MSG_MOD(extack, "Unsupported mask type for ingress_port"); + return rc; + } + return 0; +} + static bool efx_mae_asl_id(u32 id) { return !!(id & BIT(31)); @@ -279,6 +440,10 @@ static int efx_mae_populate_match_criteria(MCDI_DECLARE_STRUCT_PTR(match_crit), } MCDI_STRUCT_SET_DWORD(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_INGRESS_MPORT_SELECTOR_MASK, match->mask.ingress_port); + MCDI_STRUCT_SET_BYTE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_RECIRC_ID, + match->value.recirc_id); + MCDI_STRUCT_SET_BYTE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_RECIRC_ID_MASK, + match->mask.recirc_id); return 0; } diff --git a/drivers/net/ethernet/sfc/mae.h b/drivers/net/ethernet/sfc/mae.h index 0369be4d8983..3e0cd238d523 100644 --- a/drivers/net/ethernet/sfc/mae.h +++ b/drivers/net/ethernet/sfc/mae.h @@ -27,6 +27,20 @@ void efx_mae_mport_mport(struct efx_nic *efx, u32 mport_id, u32 *out); int efx_mae_lookup_mport(struct efx_nic *efx, u32 selector, u32 *id); +#define MAE_NUM_FIELDS (MAE_FIELD_ENC_VNET_ID + 1) + +struct mae_caps { + u32 match_field_count; + u32 action_prios; + u8 action_rule_fields[MAE_NUM_FIELDS]; +}; + +int efx_mae_get_caps(struct efx_nic *efx, struct mae_caps *caps); + +int efx_mae_match_check_caps(struct efx_nic *efx, + const struct efx_tc_match_fields *mask, + struct netlink_ext_ack *extack); + int efx_mae_alloc_action_set(struct efx_nic *efx, struct efx_tc_action_set *act); int efx_mae_free_action_set(struct efx_nic *efx, u32 fw_id); diff --git a/drivers/net/ethernet/sfc/mcdi.h b/drivers/net/ethernet/sfc/mcdi.h index 26bc69f76801..1f18e9dc62e8 100644 --- a/drivers/net/ethernet/sfc/mcdi.h +++ b/drivers/net/ethernet/sfc/mcdi.h @@ -201,6 +201,12 @@ void efx_mcdi_sensor_event(struct efx_nic *efx, efx_qword_t *ev); ((u8 *)(_buf) + (_offset)) #define MCDI_PTR(_buf, _field) \ _MCDI_PTR(_buf, MC_CMD_ ## _field ## _OFST) +/* Use MCDI_STRUCT_ functions to access members of MCDI structuredefs. + * _buf should point to the start of the structure, typically obtained with + * MCDI_DECLARE_STRUCT_PTR(structure) = _MCDI_DWORD(mcdi_buf, FIELD_WHICH_IS_STRUCT); + */ +#define MCDI_STRUCT_PTR(_buf, _field) \ + _MCDI_PTR(_buf, _field ## _OFST) #define _MCDI_CHECK_ALIGN(_ofst, _align) \ ((_ofst) + BUILD_BUG_ON_ZERO((_ofst) & (_align - 1))) #define _MCDI_DWORD(_buf, _field) \ @@ -208,6 +214,10 @@ void efx_mcdi_sensor_event(struct efx_nic *efx, efx_qword_t *ev); #define _MCDI_STRUCT_DWORD(_buf, _field) \ ((_buf) + (_MCDI_CHECK_ALIGN(_field ## _OFST, 4) >> 2)) +#define MCDI_STRUCT_SET_BYTE(_buf, _field, _value) do { \ + BUILD_BUG_ON(_field ## _LEN != 1); \ + *(u8 *)MCDI_STRUCT_PTR(_buf, _field) = _value; \ + } while (0) #define MCDI_BYTE(_buf, _field) \ ((void)BUILD_BUG_ON_ZERO(MC_CMD_ ## _field ## _LEN != 1), \ *MCDI_PTR(_buf, _field)) diff --git a/drivers/net/ethernet/sfc/mcdi_mon.c b/drivers/net/ethernet/sfc/mcdi_mon.c index 5954fcfee2b1..f5128db7c7e7 100644 --- a/drivers/net/ethernet/sfc/mcdi_mon.c +++ b/drivers/net/ethernet/sfc/mcdi_mon.c @@ -285,7 +285,7 @@ efx_mcdi_mon_add_attr(struct efx_nic *efx, const char *name, struct efx_mcdi_mon *hwmon = efx_mcdi_mon(efx); struct efx_mcdi_mon_attribute *attr = &hwmon->attrs[hwmon->n_attrs]; - strlcpy(attr->name, name, sizeof(attr->name)); + strscpy(attr->name, name, sizeof(attr->name)); attr->index = index; attr->type = type; if (type < ARRAY_SIZE(efx_mcdi_sensor_type)) diff --git a/drivers/net/ethernet/sfc/net_driver.h b/drivers/net/ethernet/sfc/net_driver.h index 7ef823d7a89a..2e9ba0cfe848 100644 --- a/drivers/net/ethernet/sfc/net_driver.h +++ b/drivers/net/ethernet/sfc/net_driver.h @@ -855,6 +855,7 @@ enum efx_xdp_tx_queues_mode { * @timer_max_ns: Interrupt timer maximum value, in nanoseconds * @irq_rx_adaptive: Adaptive IRQ moderation enabled for RX event queues * @irqs_hooked: Channel interrupts are hooked + * @log_tc_errs: Error logging for TC filter insertion is enabled * @irq_rx_mod_step_us: Step size for IRQ moderation for RX event queues * @irq_rx_moderation_us: IRQ moderation time for RX event queues * @msg_enable: Log message enable flags @@ -1017,6 +1018,7 @@ struct efx_nic { unsigned int timer_max_ns; bool irq_rx_adaptive; bool irqs_hooked; + bool log_tc_errs; unsigned int irq_mod_step_us; unsigned int irq_rx_moderation_us; u32 msg_enable; diff --git a/drivers/net/ethernet/sfc/nic.c b/drivers/net/ethernet/sfc/nic.c index 22fbb0ae77fb..63e2394382bb 100644 --- a/drivers/net/ethernet/sfc/nic.c +++ b/drivers/net/ethernet/sfc/nic.c @@ -465,7 +465,7 @@ size_t efx_nic_describe_stats(const struct efx_hw_stat_desc *desc, size_t count, for_each_set_bit(index, mask, count) { if (desc[index].name) { if (names) { - strlcpy(names, desc[index].name, + strscpy(names, desc[index].name, ETH_GSTRING_LEN); names += ETH_GSTRING_LEN; } diff --git a/drivers/net/ethernet/sfc/ptp.c b/drivers/net/ethernet/sfc/ptp.c index 10ad0b93d283..eaef4a15008a 100644 --- a/drivers/net/ethernet/sfc/ptp.c +++ b/drivers/net/ethernet/sfc/ptp.c @@ -118,9 +118,14 @@ #define PTP_MIN_LENGTH 63 -#define PTP_ADDRESS 0xe0000181 /* 224.0.1.129 */ +#define PTP_RXFILTERS_LEN 5 + +#define PTP_ADDR_IPV4 0xe0000181 /* 224.0.1.129 */ +#define PTP_ADDR_IPV6 {0xff, 0x0e, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \ + 0, 0x01, 0x81} /* ff0e::181 */ #define PTP_EVENT_PORT 319 #define PTP_GENERAL_PORT 320 +#define PTP_ADDR_ETHER {0x01, 0x1b, 0x19, 0, 0, 0} /* 01-1B-19-00-00-00 */ /* Annoyingly the format of the version numbers are different between * versions 1 and 2 so it isn't possible to simply look for 1 or 2. @@ -224,9 +229,8 @@ struct efx_ptp_timeset { * @work: Work task * @reset_required: A serious error has occurred and the PTP task needs to be * reset (disable, enable). - * @rxfilter_event: Receive filter when operating - * @rxfilter_general: Receive filter when operating - * @rxfilter_installed: Receive filter installed + * @rxfilters: Receive filters when operating + * @rxfilters_count: Num of installed rxfilters, should be == PTP_RXFILTERS_LEN * @config: Current timestamp configuration * @enabled: PTP operation enabled * @mode: Mode in which PTP operating (PTP version) @@ -295,9 +299,8 @@ struct efx_ptp_data { struct workqueue_struct *workwq; struct work_struct work; bool reset_required; - u32 rxfilter_event; - u32 rxfilter_general; - bool rxfilter_installed; + u32 rxfilters[PTP_RXFILTERS_LEN]; + size_t rxfilters_count; struct hwtstamp_config config; bool enabled; unsigned int mode; @@ -1290,61 +1293,108 @@ static void efx_ptp_remove_multicast_filters(struct efx_nic *efx) { struct efx_ptp_data *ptp = efx->ptp_data; - if (ptp->rxfilter_installed) { - efx_filter_remove_id_safe(efx, EFX_FILTER_PRI_REQUIRED, - ptp->rxfilter_general); + while (ptp->rxfilters_count) { + ptp->rxfilters_count--; efx_filter_remove_id_safe(efx, EFX_FILTER_PRI_REQUIRED, - ptp->rxfilter_event); - ptp->rxfilter_installed = false; + ptp->rxfilters[ptp->rxfilters_count]); } } -static int efx_ptp_insert_multicast_filters(struct efx_nic *efx) +static void efx_ptp_init_filter(struct efx_nic *efx, + struct efx_filter_spec *rxfilter) +{ + struct efx_channel *channel = efx->ptp_data->channel; + struct efx_rx_queue *queue = efx_channel_get_rx_queue(channel); + + efx_filter_init_rx(rxfilter, EFX_FILTER_PRI_REQUIRED, 0, + efx_rx_queue_index(queue)); +} + +static int efx_ptp_insert_filter(struct efx_nic *efx, + struct efx_filter_spec *rxfilter) { struct efx_ptp_data *ptp = efx->ptp_data; + + int rc = efx_filter_insert_filter(efx, rxfilter, true); + if (rc < 0) + return rc; + ptp->rxfilters[ptp->rxfilters_count] = rc; + ptp->rxfilters_count++; + return 0; +} + +static int efx_ptp_insert_ipv4_filter(struct efx_nic *efx, u16 port) +{ struct efx_filter_spec rxfilter; + + efx_ptp_init_filter(efx, &rxfilter); + efx_filter_set_ipv4_local(&rxfilter, IPPROTO_UDP, htonl(PTP_ADDR_IPV4), + htons(port)); + return efx_ptp_insert_filter(efx, &rxfilter); +} + +static int efx_ptp_insert_ipv6_filter(struct efx_nic *efx, u16 port) +{ + const struct in6_addr addr = {{PTP_ADDR_IPV6}}; + struct efx_filter_spec rxfilter; + + efx_ptp_init_filter(efx, &rxfilter); + efx_filter_set_ipv6_local(&rxfilter, IPPROTO_UDP, &addr, htons(port)); + return efx_ptp_insert_filter(efx, &rxfilter); +} + +static int efx_ptp_insert_eth_filter(struct efx_nic *efx) +{ + const u8 addr[ETH_ALEN] = PTP_ADDR_ETHER; + struct efx_filter_spec rxfilter; + + efx_ptp_init_filter(efx, &rxfilter); + efx_filter_set_eth_local(&rxfilter, EFX_FILTER_VID_UNSPEC, addr); + rxfilter.match_flags |= EFX_FILTER_MATCH_ETHER_TYPE; + rxfilter.ether_type = htons(ETH_P_1588); + return efx_ptp_insert_filter(efx, &rxfilter); +} + +static int efx_ptp_insert_multicast_filters(struct efx_nic *efx) +{ + struct efx_ptp_data *ptp = efx->ptp_data; int rc; - if (!ptp->channel || ptp->rxfilter_installed) + if (!ptp->channel || ptp->rxfilters_count) return 0; /* Must filter on both event and general ports to ensure * that there is no packet re-ordering. */ - efx_filter_init_rx(&rxfilter, EFX_FILTER_PRI_REQUIRED, 0, - efx_rx_queue_index( - efx_channel_get_rx_queue(ptp->channel))); - rc = efx_filter_set_ipv4_local(&rxfilter, IPPROTO_UDP, - htonl(PTP_ADDRESS), - htons(PTP_EVENT_PORT)); - if (rc != 0) - return rc; - - rc = efx_filter_insert_filter(efx, &rxfilter, true); + rc = efx_ptp_insert_ipv4_filter(efx, PTP_EVENT_PORT); if (rc < 0) - return rc; - ptp->rxfilter_event = rc; - - efx_filter_init_rx(&rxfilter, EFX_FILTER_PRI_REQUIRED, 0, - efx_rx_queue_index( - efx_channel_get_rx_queue(ptp->channel))); - rc = efx_filter_set_ipv4_local(&rxfilter, IPPROTO_UDP, - htonl(PTP_ADDRESS), - htons(PTP_GENERAL_PORT)); - if (rc != 0) goto fail; - rc = efx_filter_insert_filter(efx, &rxfilter, true); + rc = efx_ptp_insert_ipv4_filter(efx, PTP_GENERAL_PORT); if (rc < 0) goto fail; - ptp->rxfilter_general = rc; - ptp->rxfilter_installed = true; + /* if the NIC supports hw timestamps by the MAC, we can support + * PTP over IPv6 and Ethernet + */ + if (efx_ptp_use_mac_tx_timestamps(efx)) { + rc = efx_ptp_insert_ipv6_filter(efx, PTP_EVENT_PORT); + if (rc < 0) + goto fail; + + rc = efx_ptp_insert_ipv6_filter(efx, PTP_GENERAL_PORT); + if (rc < 0) + goto fail; + + rc = efx_ptp_insert_eth_filter(efx); + if (rc < 0) + goto fail; + } + return 0; fail: - efx_filter_remove_id_safe(efx, EFX_FILTER_PRI_REQUIRED, - ptp->rxfilter_event); + efx_ptp_remove_multicast_filters(efx); return rc; } diff --git a/drivers/net/ethernet/sfc/siena/efx.c b/drivers/net/ethernet/sfc/siena/efx.c index 63d999e63960..60e5b7c8ccf9 100644 --- a/drivers/net/ethernet/sfc/siena/efx.c +++ b/drivers/net/ethernet/sfc/siena/efx.c @@ -775,7 +775,7 @@ static void efx_unregister_netdev(struct efx_nic *efx) BUG_ON(netdev_priv(efx->net_dev) != efx); if (efx_dev_registered(efx)) { - strlcpy(efx->name, pci_name(efx->pci_dev), sizeof(efx->name)); + strscpy(efx->name, pci_name(efx->pci_dev), sizeof(efx->name)); efx_siena_fini_mcdi_logging(efx); device_remove_file(&efx->pci_dev->dev, &dev_attr_phy_type); unregister_netdev(efx->net_dev); @@ -1148,6 +1148,17 @@ static int efx_pm_freeze(struct device *dev) return 0; } +static void efx_pci_shutdown(struct pci_dev *pci_dev) +{ + struct efx_nic *efx = pci_get_drvdata(pci_dev); + + if (!efx) + return; + + efx_pm_freeze(&pci_dev->dev); + pci_disable_device(pci_dev); +} + static int efx_pm_thaw(struct device *dev) { int rc; @@ -1252,6 +1263,7 @@ static struct pci_driver efx_pci_driver = { .probe = efx_pci_probe, .remove = efx_pci_remove, .driver.pm = &efx_pm_ops, + .shutdown = efx_pci_shutdown, .err_handler = &efx_siena_err_handlers, #ifdef CONFIG_SFC_SIENA_SRIOV .sriov_configure = efx_pci_sriov_configure, diff --git a/drivers/net/ethernet/sfc/siena/efx_channels.c b/drivers/net/ethernet/sfc/siena/efx_channels.c index f54ebd007286..06ed74994e36 100644 --- a/drivers/net/ethernet/sfc/siena/efx_channels.c +++ b/drivers/net/ethernet/sfc/siena/efx_channels.c @@ -1317,7 +1317,7 @@ static void efx_init_napi_channel(struct efx_channel *channel) struct efx_nic *efx = channel->efx; channel->napi_dev = efx->net_dev; - netif_napi_add(channel->napi_dev, &channel->napi_str, efx_poll, 64); + netif_napi_add(channel->napi_dev, &channel->napi_str, efx_poll); } void efx_siena_init_napi(struct efx_nic *efx) diff --git a/drivers/net/ethernet/sfc/siena/efx_common.c b/drivers/net/ethernet/sfc/siena/efx_common.c index 954daf464abb..1fd396b00bfb 100644 --- a/drivers/net/ethernet/sfc/siena/efx_common.c +++ b/drivers/net/ethernet/sfc/siena/efx_common.c @@ -1006,7 +1006,7 @@ int efx_siena_init_struct(struct efx_nic *efx, efx->pci_dev = pci_dev; efx->msg_enable = debug; efx->state = STATE_UNINIT; - strlcpy(efx->name, pci_name(pci_dev), sizeof(efx->name)); + strscpy(efx->name, pci_name(pci_dev), sizeof(efx->name)); efx->net_dev = net_dev; efx->rx_prefix_size = efx->type->rx_prefix_size; diff --git a/drivers/net/ethernet/sfc/siena/ethtool_common.c b/drivers/net/ethernet/sfc/siena/ethtool_common.c index 0207d07f54e3..f590e87e5a23 100644 --- a/drivers/net/ethernet/sfc/siena/ethtool_common.c +++ b/drivers/net/ethernet/sfc/siena/ethtool_common.c @@ -105,10 +105,10 @@ void efx_siena_ethtool_get_drvinfo(struct net_device *net_dev, { struct efx_nic *efx = netdev_priv(net_dev); - strlcpy(info->driver, KBUILD_MODNAME, sizeof(info->driver)); + strscpy(info->driver, KBUILD_MODNAME, sizeof(info->driver)); efx_siena_mcdi_print_fwver(efx, info->fw_version, sizeof(info->fw_version)); - strlcpy(info->bus_info, pci_name(efx->pci_dev), sizeof(info->bus_info)); + strscpy(info->bus_info, pci_name(efx->pci_dev), sizeof(info->bus_info)); } u32 efx_siena_ethtool_get_msglevel(struct net_device *net_dev) @@ -467,7 +467,7 @@ void efx_siena_ethtool_get_strings(struct net_device *net_dev, strings += (efx->type->describe_stats(efx, strings) * ETH_GSTRING_LEN); for (i = 0; i < EFX_ETHTOOL_SW_STAT_COUNT; i++) - strlcpy(strings + i * ETH_GSTRING_LEN, + strscpy(strings + i * ETH_GSTRING_LEN, efx_sw_stat_desc[i].name, ETH_GSTRING_LEN); strings += EFX_ETHTOOL_SW_STAT_COUNT * ETH_GSTRING_LEN; strings += (efx_describe_per_queue_stats(efx, strings) * diff --git a/drivers/net/ethernet/sfc/siena/mcdi_mon.c b/drivers/net/ethernet/sfc/siena/mcdi_mon.c index c7ea703c5d7a..56a9c56ed9e3 100644 --- a/drivers/net/ethernet/sfc/siena/mcdi_mon.c +++ b/drivers/net/ethernet/sfc/siena/mcdi_mon.c @@ -285,7 +285,7 @@ efx_mcdi_mon_add_attr(struct efx_nic *efx, const char *name, struct efx_mcdi_mon *hwmon = efx_mcdi_mon(efx); struct efx_mcdi_mon_attribute *attr = &hwmon->attrs[hwmon->n_attrs]; - strlcpy(attr->name, name, sizeof(attr->name)); + strscpy(attr->name, name, sizeof(attr->name)); attr->index = index; attr->type = type; if (type < ARRAY_SIZE(efx_mcdi_sensor_type)) diff --git a/drivers/net/ethernet/sfc/siena/nic.c b/drivers/net/ethernet/sfc/siena/nic.c index abf9a4adf139..0ea0433a6230 100644 --- a/drivers/net/ethernet/sfc/siena/nic.c +++ b/drivers/net/ethernet/sfc/siena/nic.c @@ -458,7 +458,7 @@ size_t efx_siena_describe_stats(const struct efx_hw_stat_desc *desc, size_t coun for_each_set_bit(index, mask, count) { if (desc[index].name) { if (names) { - strlcpy(names, desc[index].name, + strscpy(names, desc[index].name, ETH_GSTRING_LEN); names += ETH_GSTRING_LEN; } diff --git a/drivers/net/ethernet/sfc/tc.c b/drivers/net/ethernet/sfc/tc.c index 0c0aeb91f500..3478860d4023 100644 --- a/drivers/net/ethernet/sfc/tc.c +++ b/drivers/net/ethernet/sfc/tc.c @@ -9,11 +9,60 @@ * by the Free Software Foundation, incorporated herein by reference. */ +#include <net/pkt_cls.h> #include "tc.h" +#include "tc_bindings.h" #include "mae.h" #include "ef100_rep.h" #include "efx.h" +#define EFX_EFV_PF NULL +/* Look up the representor information (efv) for a device. + * May return NULL for the PF (us), or an error pointer for a device that + * isn't supported as a TC offload endpoint + */ +static struct efx_rep *efx_tc_flower_lookup_efv(struct efx_nic *efx, + struct net_device *dev) +{ + struct efx_rep *efv; + + if (!dev) + return ERR_PTR(-EOPNOTSUPP); + /* Is it us (the PF)? */ + if (dev == efx->net_dev) + return EFX_EFV_PF; + /* Is it an efx vfrep at all? */ + if (dev->netdev_ops != &efx_ef100_rep_netdev_ops) + return ERR_PTR(-EOPNOTSUPP); + /* Is it ours? We don't support TC rules that include another + * EF100's netdevices (not even on another port of the same NIC). + */ + efv = netdev_priv(dev); + if (efv->parent != efx) + return ERR_PTR(-EOPNOTSUPP); + return efv; +} + +/* Convert a driver-internal vport ID into an external device (wire or VF) */ +static s64 efx_tc_flower_external_mport(struct efx_nic *efx, struct efx_rep *efv) +{ + u32 mport; + + if (IS_ERR(efv)) + return PTR_ERR(efv); + if (!efv) /* device is PF (us) */ + efx_mae_mport_wire(efx, &mport); + else /* device is repr */ + efx_mae_mport_mport(efx, efv->mport, &mport); + return mport; +} + +static const struct rhashtable_params efx_tc_match_action_ht_params = { + .key_len = sizeof(unsigned long), + .key_offset = offsetof(struct efx_tc_flow_rule, cookie), + .head_offset = offsetof(struct efx_tc_flow_rule, linkage), +}; + static void efx_tc_free_action_set(struct efx_nic *efx, struct efx_tc_action_set *act, bool in_hw) { @@ -58,6 +107,333 @@ static void efx_tc_delete_rule(struct efx_nic *efx, struct efx_tc_flow_rule *rul rule->fw_id = MC_CMD_MAE_ACTION_RULE_INSERT_OUT_ACTION_RULE_ID_NULL; } +static void efx_tc_flow_free(void *ptr, void *arg) +{ + struct efx_tc_flow_rule *rule = ptr; + struct efx_nic *efx = arg; + + netif_err(efx, drv, efx->net_dev, + "tc rule %lx still present at teardown, removing\n", + rule->cookie); + + efx_mae_delete_rule(efx, rule->fw_id); + + /* Release entries in subsidiary tables */ + efx_tc_free_action_set_list(efx, &rule->acts, true); + + kfree(rule); +} + +static int efx_tc_flower_parse_match(struct efx_nic *efx, + struct flow_rule *rule, + struct efx_tc_match *match, + struct netlink_ext_ack *extack) +{ + struct flow_dissector *dissector = rule->match.dissector; + + if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_CONTROL)) { + struct flow_match_control fm; + + flow_rule_match_control(rule, &fm); + + if (fm.mask->flags) { + efx_tc_err(efx, "Unsupported match on control.flags %#x\n", + fm.mask->flags); + NL_SET_ERR_MSG_MOD(extack, "Unsupported match on control.flags"); + return -EOPNOTSUPP; + } + } + if (dissector->used_keys & + ~(BIT(FLOW_DISSECTOR_KEY_CONTROL) | + BIT(FLOW_DISSECTOR_KEY_BASIC))) { + efx_tc_err(efx, "Unsupported flower keys %#x\n", dissector->used_keys); + NL_SET_ERR_MSG_MOD(extack, "Unsupported flower keys encountered"); + return -EOPNOTSUPP; + } + + if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_BASIC)) { + struct flow_match_basic fm; + + flow_rule_match_basic(rule, &fm); + if (fm.mask->n_proto) { + EFX_TC_ERR_MSG(efx, extack, "Unsupported eth_proto match\n"); + return -EOPNOTSUPP; + } + if (fm.mask->ip_proto) { + EFX_TC_ERR_MSG(efx, extack, "Unsupported ip_proto match\n"); + return -EOPNOTSUPP; + } + } + + return 0; +} + +static int efx_tc_flower_replace(struct efx_nic *efx, + struct net_device *net_dev, + struct flow_cls_offload *tc, + struct efx_rep *efv) +{ + struct flow_rule *fr = flow_cls_offload_flow_rule(tc); + struct netlink_ext_ack *extack = tc->common.extack; + struct efx_tc_flow_rule *rule = NULL, *old; + struct efx_tc_action_set *act = NULL; + const struct flow_action_entry *fa; + struct efx_rep *from_efv, *to_efv; + struct efx_tc_match match; + s64 rc; + int i; + + if (!tc_can_offload_extack(efx->net_dev, extack)) + return -EOPNOTSUPP; + if (WARN_ON(!efx->tc)) + return -ENETDOWN; + if (WARN_ON(!efx->tc->up)) + return -ENETDOWN; + + from_efv = efx_tc_flower_lookup_efv(efx, net_dev); + if (IS_ERR(from_efv)) { + /* Might be a tunnel decap rule from an indirect block. + * Support for those not implemented yet. + */ + return -EOPNOTSUPP; + } + + if (efv != from_efv) { + /* can't happen */ + efx_tc_err(efx, "for %s efv is %snull but from_efv is %snull\n", + netdev_name(net_dev), efv ? "non-" : "", + from_efv ? "non-" : ""); + if (efv) + NL_SET_ERR_MSG_MOD(extack, "vfrep filter has PF net_dev (can't happen)"); + else + NL_SET_ERR_MSG_MOD(extack, "PF filter has vfrep net_dev (can't happen)"); + return -EINVAL; + } + + /* Parse match */ + memset(&match, 0, sizeof(match)); + rc = efx_tc_flower_external_mport(efx, from_efv); + if (rc < 0) { + EFX_TC_ERR_MSG(efx, extack, "Failed to identify ingress m-port"); + return rc; + } + match.value.ingress_port = rc; + match.mask.ingress_port = ~0; + rc = efx_tc_flower_parse_match(efx, fr, &match, extack); + if (rc) + return rc; + + if (tc->common.chain_index) { + EFX_TC_ERR_MSG(efx, extack, "No support for nonzero chain_index"); + return -EOPNOTSUPP; + } + match.mask.recirc_id = 0xff; + + rc = efx_mae_match_check_caps(efx, &match.mask, extack); + if (rc) + return rc; + + rule = kzalloc(sizeof(*rule), GFP_USER); + if (!rule) + return -ENOMEM; + INIT_LIST_HEAD(&rule->acts.list); + rule->cookie = tc->cookie; + old = rhashtable_lookup_get_insert_fast(&efx->tc->match_action_ht, + &rule->linkage, + efx_tc_match_action_ht_params); + if (old) { + netif_dbg(efx, drv, efx->net_dev, + "Already offloaded rule (cookie %lx)\n", tc->cookie); + rc = -EEXIST; + NL_SET_ERR_MSG_MOD(extack, "Rule already offloaded"); + goto release; + } + + /* Parse actions */ + act = kzalloc(sizeof(*act), GFP_USER); + if (!act) { + rc = -ENOMEM; + goto release; + } + + flow_action_for_each(i, fa, &fr->action) { + struct efx_tc_action_set save; + + if (!act) { + /* more actions after a non-pipe action */ + EFX_TC_ERR_MSG(efx, extack, "Action follows non-pipe action"); + rc = -EINVAL; + goto release; + } + + switch (fa->id) { + case FLOW_ACTION_DROP: + rc = efx_mae_alloc_action_set(efx, act); + if (rc) { + EFX_TC_ERR_MSG(efx, extack, "Failed to write action set to hw (drop)"); + goto release; + } + list_add_tail(&act->list, &rule->acts.list); + act = NULL; /* end of the line */ + break; + case FLOW_ACTION_REDIRECT: + case FLOW_ACTION_MIRRED: + save = *act; + to_efv = efx_tc_flower_lookup_efv(efx, fa->dev); + if (IS_ERR(to_efv)) { + EFX_TC_ERR_MSG(efx, extack, "Mirred egress device not on switch"); + rc = PTR_ERR(to_efv); + goto release; + } + rc = efx_tc_flower_external_mport(efx, to_efv); + if (rc < 0) { + EFX_TC_ERR_MSG(efx, extack, "Failed to identify egress m-port"); + goto release; + } + act->dest_mport = rc; + act->deliver = 1; + rc = efx_mae_alloc_action_set(efx, act); + if (rc) { + EFX_TC_ERR_MSG(efx, extack, "Failed to write action set to hw (mirred)"); + goto release; + } + list_add_tail(&act->list, &rule->acts.list); + act = NULL; + if (fa->id == FLOW_ACTION_REDIRECT) + break; /* end of the line */ + /* Mirror, so continue on with saved act */ + act = kzalloc(sizeof(*act), GFP_USER); + if (!act) { + rc = -ENOMEM; + goto release; + } + *act = save; + break; + default: + efx_tc_err(efx, "Unhandled action %u\n", fa->id); + rc = -EOPNOTSUPP; + NL_SET_ERR_MSG_MOD(extack, "Unsupported action"); + goto release; + } + } + + if (act) { + /* Not shot/redirected, so deliver to default dest */ + if (from_efv == EFX_EFV_PF) + /* Rule applies to traffic from the wire, + * and default dest is thus the PF + */ + efx_mae_mport_uplink(efx, &act->dest_mport); + else + /* Representor, so rule applies to traffic from + * representee, and default dest is thus the rep. + * All reps use the same mport for delivery + */ + efx_mae_mport_mport(efx, efx->tc->reps_mport_id, + &act->dest_mport); + act->deliver = 1; + rc = efx_mae_alloc_action_set(efx, act); + if (rc) { + EFX_TC_ERR_MSG(efx, extack, "Failed to write action set to hw (deliver)"); + goto release; + } + list_add_tail(&act->list, &rule->acts.list); + act = NULL; /* Prevent double-free in error path */ + } + + netif_dbg(efx, drv, efx->net_dev, + "Successfully parsed filter (cookie %lx)\n", + tc->cookie); + + rule->match = match; + + rc = efx_mae_alloc_action_set_list(efx, &rule->acts); + if (rc) { + EFX_TC_ERR_MSG(efx, extack, "Failed to write action set list to hw"); + goto release; + } + rc = efx_mae_insert_rule(efx, &rule->match, EFX_TC_PRIO_TC, + rule->acts.fw_id, &rule->fw_id); + if (rc) { + EFX_TC_ERR_MSG(efx, extack, "Failed to insert rule in hw"); + goto release_acts; + } + return 0; + +release_acts: + efx_mae_free_action_set_list(efx, &rule->acts); +release: + /* We failed to insert the rule, so free up any entries we created in + * subsidiary tables. + */ + if (act) + efx_tc_free_action_set(efx, act, false); + if (rule) { + rhashtable_remove_fast(&efx->tc->match_action_ht, + &rule->linkage, + efx_tc_match_action_ht_params); + efx_tc_free_action_set_list(efx, &rule->acts, false); + } + kfree(rule); + return rc; +} + +static int efx_tc_flower_destroy(struct efx_nic *efx, + struct net_device *net_dev, + struct flow_cls_offload *tc) +{ + struct netlink_ext_ack *extack = tc->common.extack; + struct efx_tc_flow_rule *rule; + + rule = rhashtable_lookup_fast(&efx->tc->match_action_ht, &tc->cookie, + efx_tc_match_action_ht_params); + if (!rule) { + /* Only log a message if we're the ingress device. Otherwise + * it's a foreign filter and we might just not have been + * interested (e.g. we might not have been the egress device + * either). + */ + if (!IS_ERR(efx_tc_flower_lookup_efv(efx, net_dev))) + netif_warn(efx, drv, efx->net_dev, + "Filter %lx not found to remove\n", tc->cookie); + NL_SET_ERR_MSG_MOD(extack, "Flow cookie not found in offloaded rules"); + return -ENOENT; + } + + /* Remove it from HW */ + efx_tc_delete_rule(efx, rule); + /* Delete it from SW */ + rhashtable_remove_fast(&efx->tc->match_action_ht, &rule->linkage, + efx_tc_match_action_ht_params); + netif_dbg(efx, drv, efx->net_dev, "Removed filter %lx\n", rule->cookie); + kfree(rule); + return 0; +} + +int efx_tc_flower(struct efx_nic *efx, struct net_device *net_dev, + struct flow_cls_offload *tc, struct efx_rep *efv) +{ + int rc; + + if (!efx->tc) + return -EOPNOTSUPP; + + mutex_lock(&efx->tc->mutex); + switch (tc->command) { + case FLOW_CLS_REPLACE: + rc = efx_tc_flower_replace(efx, net_dev, tc, efv); + break; + case FLOW_CLS_DESTROY: + rc = efx_tc_flower_destroy(efx, net_dev, tc); + break; + default: + rc = -EOPNOTSUPP; + break; + } + mutex_unlock(&efx->tc->mutex); + return rc; +} + static int efx_tc_configure_default_rule(struct efx_nic *efx, u32 ing_port, u32 eg_port, struct efx_tc_flow_rule *rule) { @@ -201,13 +577,37 @@ int efx_init_tc(struct efx_nic *efx) { int rc; + rc = efx_mae_get_caps(efx, efx->tc->caps); + if (rc) + return rc; + if (efx->tc->caps->match_field_count > MAE_NUM_FIELDS) + /* Firmware supports some match fields the driver doesn't know + * about. Not fatal, unless any of those fields are required + * (MAE_FIELD_SUPPORTED_MATCH_ALWAYS) but if so we don't know. + */ + netif_warn(efx, probe, efx->net_dev, + "FW reports additional match fields %u\n", + efx->tc->caps->match_field_count); + if (efx->tc->caps->action_prios < EFX_TC_PRIO__NUM) { + netif_err(efx, probe, efx->net_dev, + "Too few action prios supported (have %u, need %u)\n", + efx->tc->caps->action_prios, EFX_TC_PRIO__NUM); + return -EIO; + } rc = efx_tc_configure_default_rule_pf(efx); if (rc) return rc; rc = efx_tc_configure_default_rule_wire(efx); if (rc) return rc; - return efx_tc_configure_rep_mport(efx); + rc = efx_tc_configure_rep_mport(efx); + if (rc) + return rc; + efx->tc->up = true; + rc = flow_indr_dev_register(efx_tc_indr_setup_cb, efx); + if (rc) + return rc; + return 0; } void efx_fini_tc(struct efx_nic *efx) @@ -215,20 +615,35 @@ void efx_fini_tc(struct efx_nic *efx) /* We can get called even if efx_init_struct_tc() failed */ if (!efx->tc) return; + if (efx->tc->up) + flow_indr_dev_unregister(efx_tc_indr_setup_cb, efx, efx_tc_block_unbind); efx_tc_deconfigure_rep_mport(efx); efx_tc_deconfigure_default_rule(efx, &efx->tc->dflt.pf); efx_tc_deconfigure_default_rule(efx, &efx->tc->dflt.wire); + efx->tc->up = false; } int efx_init_struct_tc(struct efx_nic *efx) { + int rc; + if (efx->type->is_vf) return 0; efx->tc = kzalloc(sizeof(*efx->tc), GFP_KERNEL); if (!efx->tc) return -ENOMEM; + efx->tc->caps = kzalloc(sizeof(struct mae_caps), GFP_KERNEL); + if (!efx->tc->caps) { + rc = -ENOMEM; + goto fail_alloc_caps; + } + INIT_LIST_HEAD(&efx->tc->block_list); + mutex_init(&efx->tc->mutex); + rc = rhashtable_init(&efx->tc->match_action_ht, &efx_tc_match_action_ht_params); + if (rc < 0) + goto fail_match_action_ht; efx->tc->reps_filter_uc = -1; efx->tc->reps_filter_mc = -1; INIT_LIST_HEAD(&efx->tc->dflt.pf.acts.list); @@ -236,6 +651,13 @@ int efx_init_struct_tc(struct efx_nic *efx) INIT_LIST_HEAD(&efx->tc->dflt.wire.acts.list); efx->tc->dflt.wire.fw_id = MC_CMD_MAE_ACTION_RULE_INSERT_OUT_ACTION_RULE_ID_NULL; return 0; +fail_match_action_ht: + mutex_destroy(&efx->tc->mutex); + kfree(efx->tc->caps); +fail_alloc_caps: + kfree(efx->tc); + efx->tc = NULL; + return rc; } void efx_fini_struct_tc(struct efx_nic *efx) @@ -243,10 +665,16 @@ void efx_fini_struct_tc(struct efx_nic *efx) if (!efx->tc) return; + mutex_lock(&efx->tc->mutex); EFX_WARN_ON_PARANOID(efx->tc->dflt.pf.fw_id != MC_CMD_MAE_ACTION_RULE_INSERT_OUT_ACTION_RULE_ID_NULL); EFX_WARN_ON_PARANOID(efx->tc->dflt.wire.fw_id != MC_CMD_MAE_ACTION_RULE_INSERT_OUT_ACTION_RULE_ID_NULL); + rhashtable_free_and_destroy(&efx->tc->match_action_ht, efx_tc_flow_free, + efx); + mutex_unlock(&efx->tc->mutex); + mutex_destroy(&efx->tc->mutex); + kfree(efx->tc->caps); kfree(efx->tc); efx->tc = NULL; } diff --git a/drivers/net/ethernet/sfc/tc.h b/drivers/net/ethernet/sfc/tc.h index 309123c6b386..196fd74ed973 100644 --- a/drivers/net/ethernet/sfc/tc.h +++ b/drivers/net/ethernet/sfc/tc.h @@ -11,8 +11,28 @@ #ifndef EFX_TC_H #define EFX_TC_H +#include <net/flow_offload.h> +#include <linux/rhashtable.h> #include "net_driver.h" +/* Error reporting: convenience macros. For indicating why a given filter + * insertion is not supported; errors in internal operation or in the + * hardware should be netif_err()s instead. + */ +/* Used when error message is constant. */ +#define EFX_TC_ERR_MSG(efx, extack, message) do { \ + NL_SET_ERR_MSG_MOD(extack, message); \ + if (efx->log_tc_errs) \ + netif_info(efx, drv, efx->net_dev, "%s\n", message); \ +} while (0) +/* Used when error message is not constant; caller should also supply a + * constant extack message with NL_SET_ERR_MSG_MOD(). + */ +#define efx_tc_err(efx, fmt, args...) do { \ +if (efx->log_tc_errs) \ + netif_info(efx, drv, efx->net_dev, fmt, ##args);\ +} while (0) + struct efx_tc_action_set { u16 deliver:1; u32 dest_mport; @@ -23,6 +43,7 @@ struct efx_tc_action_set { struct efx_tc_match_fields { /* L1 */ u32 ingress_port; + u8 recirc_id; }; struct efx_tc_match { @@ -36,12 +57,15 @@ struct efx_tc_action_set_list { }; struct efx_tc_flow_rule { + unsigned long cookie; + struct rhash_head linkage; struct efx_tc_match match; struct efx_tc_action_set_list acts; u32 fw_id; }; enum efx_tc_rule_prios { + EFX_TC_PRIO_TC, /* Rule inserted by TC */ EFX_TC_PRIO_DFLT, /* Default switch rule; one of efx_tc_default_rules */ EFX_TC_PRIO__NUM }; @@ -49,6 +73,10 @@ enum efx_tc_rule_prios { /** * struct efx_tc_state - control plane data for TC offload * + * @caps: MAE capabilities reported by MCDI + * @block_list: List of &struct efx_tc_block_binding + * @mutex: Used to serialise operations on TC hashtables + * @match_action_ht: Hashtable of TC match-action rules * @reps_mport_id: MAE port allocated for representor RX * @reps_filter_uc: VNIC filter for representor unicast RX (promisc) * @reps_filter_mc: VNIC filter for representor multicast RX (allmulti) @@ -57,14 +85,20 @@ enum efx_tc_rule_prios { * %EFX_TC_PRIO_DFLT. Named by *ingress* port * @dflt.pf: rule for traffic ingressing from PF (egresses to wire) * @dflt.wire: rule for traffic ingressing from wire (egresses to PF) + * @up: have TC datastructures been set up? */ struct efx_tc_state { + struct mae_caps *caps; + struct list_head block_list; + struct mutex mutex; + struct rhashtable match_action_ht; u32 reps_mport_id, reps_mport_vport_id; s32 reps_filter_uc, reps_filter_mc; struct { struct efx_tc_flow_rule pf; struct efx_tc_flow_rule wire; } dflt; + bool up; }; struct efx_rep; @@ -72,6 +106,8 @@ struct efx_rep; int efx_tc_configure_default_rule_rep(struct efx_rep *efv); void efx_tc_deconfigure_default_rule(struct efx_nic *efx, struct efx_tc_flow_rule *rule); +int efx_tc_flower(struct efx_nic *efx, struct net_device *net_dev, + struct flow_cls_offload *tc, struct efx_rep *efv); int efx_tc_insert_rep_filters(struct efx_nic *efx); void efx_tc_remove_rep_filters(struct efx_nic *efx); diff --git a/drivers/net/ethernet/sfc/tc_bindings.c b/drivers/net/ethernet/sfc/tc_bindings.c new file mode 100644 index 000000000000..c18d64519c2d --- /dev/null +++ b/drivers/net/ethernet/sfc/tc_bindings.c @@ -0,0 +1,228 @@ +// SPDX-License-Identifier: GPL-2.0-only +/**************************************************************************** + * Driver for Solarflare network controllers and boards + * Copyright 2022 Xilinx Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation, incorporated herein by reference. + */ + +#include "tc_bindings.h" +#include "tc.h" + +struct efx_tc_block_binding { + struct list_head list; + struct efx_nic *efx; + struct efx_rep *efv; + struct net_device *otherdev; /* may actually be us */ + struct flow_block *block; +}; + +static struct efx_tc_block_binding *efx_tc_find_binding(struct efx_nic *efx, + struct net_device *otherdev) +{ + struct efx_tc_block_binding *binding; + + ASSERT_RTNL(); + list_for_each_entry(binding, &efx->tc->block_list, list) + if (binding->otherdev == otherdev) + return binding; + return NULL; +} + +static int efx_tc_block_cb(enum tc_setup_type type, void *type_data, + void *cb_priv) +{ + struct efx_tc_block_binding *binding = cb_priv; + struct flow_cls_offload *tcf = type_data; + + switch (type) { + case TC_SETUP_CLSFLOWER: + return efx_tc_flower(binding->efx, binding->otherdev, + tcf, binding->efv); + default: + return -EOPNOTSUPP; + } +} + +void efx_tc_block_unbind(void *cb_priv) +{ + struct efx_tc_block_binding *binding = cb_priv; + + list_del(&binding->list); + kfree(binding); +} + +static struct efx_tc_block_binding *efx_tc_create_binding( + struct efx_nic *efx, struct efx_rep *efv, + struct net_device *otherdev, struct flow_block *block) +{ + struct efx_tc_block_binding *binding = kmalloc(sizeof(*binding), GFP_KERNEL); + + if (!binding) + return ERR_PTR(-ENOMEM); + binding->efx = efx; + binding->efv = efv; + binding->otherdev = otherdev; + binding->block = block; + list_add(&binding->list, &efx->tc->block_list); + return binding; +} + +int efx_tc_setup_block(struct net_device *net_dev, struct efx_nic *efx, + struct flow_block_offload *tcb, struct efx_rep *efv) +{ + struct efx_tc_block_binding *binding; + struct flow_block_cb *block_cb; + int rc; + + if (tcb->binder_type != FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS) + return -EOPNOTSUPP; + + if (WARN_ON(!efx->tc)) + return -ENETDOWN; + + switch (tcb->command) { + case FLOW_BLOCK_BIND: + binding = efx_tc_create_binding(efx, efv, net_dev, tcb->block); + if (IS_ERR(binding)) + return PTR_ERR(binding); + block_cb = flow_block_cb_alloc(efx_tc_block_cb, binding, + binding, efx_tc_block_unbind); + rc = PTR_ERR_OR_ZERO(block_cb); + netif_dbg(efx, drv, efx->net_dev, + "bind %sdirect block for device %s, rc %d\n", + net_dev == efx->net_dev ? "" : + efv ? "semi" : "in", + net_dev ? net_dev->name : NULL, rc); + if (rc) { + list_del(&binding->list); + kfree(binding); + } else { + flow_block_cb_add(block_cb, tcb); + } + return rc; + case FLOW_BLOCK_UNBIND: + binding = efx_tc_find_binding(efx, net_dev); + if (binding) { + block_cb = flow_block_cb_lookup(tcb->block, + efx_tc_block_cb, + binding); + if (block_cb) { + flow_block_cb_remove(block_cb, tcb); + netif_dbg(efx, drv, efx->net_dev, + "unbound %sdirect block for device %s\n", + net_dev == efx->net_dev ? "" : + binding->efv ? "semi" : "in", + net_dev ? net_dev->name : NULL); + return 0; + } + } + /* If we're in driver teardown, then we expect to have + * already unbound all our blocks (we did it early while + * we still had MCDI to remove the filters), so getting + * unbind callbacks now isn't a problem. + */ + netif_cond_dbg(efx, drv, efx->net_dev, + !efx->tc->up, warn, + "%sdirect block unbind for device %s, was never bound\n", + net_dev == efx->net_dev ? "" : "in", + net_dev ? net_dev->name : NULL); + return -ENOENT; + default: + return -EOPNOTSUPP; + } +} + +int efx_tc_indr_setup_cb(struct net_device *net_dev, struct Qdisc *sch, + void *cb_priv, enum tc_setup_type type, + void *type_data, void *data, + void (*cleanup)(struct flow_block_cb *block_cb)) +{ + struct flow_block_offload *tcb = type_data; + struct efx_tc_block_binding *binding; + struct flow_block_cb *block_cb; + struct efx_nic *efx = cb_priv; + bool is_ovs_int_port; + int rc; + + if (!net_dev) + return -EOPNOTSUPP; + + if (tcb->binder_type != FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS && + tcb->binder_type != FLOW_BLOCK_BINDER_TYPE_CLSACT_EGRESS) + return -EOPNOTSUPP; + + is_ovs_int_port = netif_is_ovs_master(net_dev); + if (tcb->binder_type == FLOW_BLOCK_BINDER_TYPE_CLSACT_EGRESS && + !is_ovs_int_port) + return -EOPNOTSUPP; + + if (is_ovs_int_port) + return -EOPNOTSUPP; + + switch (type) { + case TC_SETUP_BLOCK: + switch (tcb->command) { + case FLOW_BLOCK_BIND: + binding = efx_tc_create_binding(efx, NULL, net_dev, tcb->block); + if (IS_ERR(binding)) + return PTR_ERR(binding); + block_cb = flow_indr_block_cb_alloc(efx_tc_block_cb, binding, + binding, efx_tc_block_unbind, + tcb, net_dev, sch, data, binding, + cleanup); + rc = PTR_ERR_OR_ZERO(block_cb); + netif_dbg(efx, drv, efx->net_dev, + "bind indr block for device %s, rc %d\n", + net_dev ? net_dev->name : NULL, rc); + if (rc) { + list_del(&binding->list); + kfree(binding); + } else { + flow_block_cb_add(block_cb, tcb); + } + return rc; + case FLOW_BLOCK_UNBIND: + binding = efx_tc_find_binding(efx, net_dev); + if (!binding) + return -ENOENT; + block_cb = flow_block_cb_lookup(tcb->block, + efx_tc_block_cb, + binding); + if (!block_cb) + return -ENOENT; + flow_indr_block_cb_remove(block_cb, tcb); + netif_dbg(efx, drv, efx->net_dev, + "unbind indr block for device %s\n", + net_dev ? net_dev->name : NULL); + return 0; + default: + return -EOPNOTSUPP; + } + default: + return -EOPNOTSUPP; + } +} + +/* .ndo_setup_tc implementation + * Entry point for flower block and filter management. + */ +int efx_tc_setup(struct net_device *net_dev, enum tc_setup_type type, + void *type_data) +{ + struct efx_nic *efx = efx_netdev_priv(net_dev); + + if (efx->type->is_vf) + return -EOPNOTSUPP; + if (!efx->tc) + return -EOPNOTSUPP; + + if (type == TC_SETUP_CLSFLOWER) + return efx_tc_flower(efx, net_dev, type_data, NULL); + if (type == TC_SETUP_BLOCK) + return efx_tc_setup_block(net_dev, efx, type_data, NULL); + + return -EOPNOTSUPP; +} diff --git a/drivers/net/ethernet/sfc/tc_bindings.h b/drivers/net/ethernet/sfc/tc_bindings.h new file mode 100644 index 000000000000..c210bb09150e --- /dev/null +++ b/drivers/net/ethernet/sfc/tc_bindings.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/**************************************************************************** + * Driver for Solarflare network controllers and boards + * Copyright 2022 Xilinx Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation, incorporated herein by reference. + */ + +#ifndef EFX_TC_BINDINGS_H +#define EFX_TC_BINDINGS_H +#include "net_driver.h" + +#include <net/sch_generic.h> + +struct efx_rep; + +void efx_tc_block_unbind(void *cb_priv); +int efx_tc_setup_block(struct net_device *net_dev, struct efx_nic *efx, + struct flow_block_offload *tcb, struct efx_rep *efv); +int efx_tc_setup(struct net_device *net_dev, enum tc_setup_type type, + void *type_data); + +int efx_tc_indr_setup_cb(struct net_device *net_dev, struct Qdisc *sch, + void *cb_priv, enum tc_setup_type type, + void *type_data, void *data, + void (*cleanup)(struct flow_block_cb *block_cb)); +#endif /* EFX_TC_BINDINGS_H */ diff --git a/drivers/net/ethernet/sgi/ioc3-eth.c b/drivers/net/ethernet/sgi/ioc3-eth.c index e2d009866a7b..8fc3f5272fa7 100644 --- a/drivers/net/ethernet/sgi/ioc3-eth.c +++ b/drivers/net/ethernet/sgi/ioc3-eth.c @@ -1158,9 +1158,9 @@ static inline unsigned int ioc3_hash(const unsigned char *addr) static void ioc3_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { - strlcpy(info->driver, IOC3_NAME, sizeof(info->driver)); - strlcpy(info->version, IOC3_VERSION, sizeof(info->version)); - strlcpy(info->bus_info, pci_name(to_pci_dev(dev->dev.parent)), + strscpy(info->driver, IOC3_NAME, sizeof(info->driver)); + strscpy(info->version, IOC3_VERSION, sizeof(info->version)); + strscpy(info->bus_info, pci_name(to_pci_dev(dev->dev.parent)), sizeof(info->bus_info)); } diff --git a/drivers/net/ethernet/sis/sis190.c b/drivers/net/ethernet/sis/sis190.c index 216bb2d34d7c..dda4e488c77a 100644 --- a/drivers/net/ethernet/sis/sis190.c +++ b/drivers/net/ethernet/sis/sis190.c @@ -1769,9 +1769,9 @@ static void sis190_get_drvinfo(struct net_device *dev, { struct sis190_private *tp = netdev_priv(dev); - strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); - strlcpy(info->version, DRV_VERSION, sizeof(info->version)); - strlcpy(info->bus_info, pci_name(tp->pci_dev), + strscpy(info->driver, DRV_NAME, sizeof(info->driver)); + strscpy(info->version, DRV_VERSION, sizeof(info->version)); + strscpy(info->bus_info, pci_name(tp->pci_dev), sizeof(info->bus_info)); } diff --git a/drivers/net/ethernet/sis/sis900.c b/drivers/net/ethernet/sis/sis900.c index 23a336c5096e..cb7fec226cab 100644 --- a/drivers/net/ethernet/sis/sis900.c +++ b/drivers/net/ethernet/sis/sis900.c @@ -2027,9 +2027,9 @@ static void sis900_get_drvinfo(struct net_device *net_dev, { struct sis900_private *sis_priv = netdev_priv(net_dev); - strlcpy(info->driver, SIS900_MODULE_NAME, sizeof(info->driver)); - strlcpy(info->version, SIS900_DRV_VERSION, sizeof(info->version)); - strlcpy(info->bus_info, pci_name(sis_priv->pci_dev), + strscpy(info->driver, SIS900_MODULE_NAME, sizeof(info->driver)); + strscpy(info->version, SIS900_DRV_VERSION, sizeof(info->version)); + strscpy(info->bus_info, pci_name(sis_priv->pci_dev), sizeof(info->bus_info)); } diff --git a/drivers/net/ethernet/smsc/epic100.c b/drivers/net/ethernet/smsc/epic100.c index 0329caf63279..013e90d69182 100644 --- a/drivers/net/ethernet/smsc/epic100.c +++ b/drivers/net/ethernet/smsc/epic100.c @@ -482,7 +482,7 @@ static int epic_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) dev->netdev_ops = &epic_netdev_ops; dev->ethtool_ops = &netdev_ethtool_ops; dev->watchdog_timeo = TX_TIMEOUT; - netif_napi_add(dev, &ep->napi, epic_poll, 64); + netif_napi_add(dev, &ep->napi, epic_poll); ret = register_netdev(dev); if (ret < 0) @@ -1392,9 +1392,9 @@ static void netdev_get_drvinfo (struct net_device *dev, struct ethtool_drvinfo * { struct epic_private *np = netdev_priv(dev); - strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); - strlcpy(info->version, DRV_VERSION, sizeof(info->version)); - strlcpy(info->bus_info, pci_name(np->pci_dev), sizeof(info->bus_info)); + strscpy(info->driver, DRV_NAME, sizeof(info->driver)); + strscpy(info->version, DRV_VERSION, sizeof(info->version)); + strscpy(info->bus_info, pci_name(np->pci_dev), sizeof(info->bus_info)); } static int netdev_get_link_ksettings(struct net_device *dev, diff --git a/drivers/net/ethernet/smsc/smc911x.c b/drivers/net/ethernet/smsc/smc911x.c index 24d66af797d4..52ecfb461c41 100644 --- a/drivers/net/ethernet/smsc/smc911x.c +++ b/drivers/net/ethernet/smsc/smc911x.c @@ -1509,9 +1509,9 @@ smc911x_ethtool_set_link_ksettings(struct net_device *dev, static void smc911x_ethtool_getdrvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { - strlcpy(info->driver, CARDNAME, sizeof(info->driver)); - strlcpy(info->version, version, sizeof(info->version)); - strlcpy(info->bus_info, dev_name(dev->dev.parent), + strscpy(info->driver, CARDNAME, sizeof(info->driver)); + strscpy(info->version, version, sizeof(info->version)); + strscpy(info->bus_info, dev_name(dev->dev.parent), sizeof(info->bus_info)); } diff --git a/drivers/net/ethernet/smsc/smc91c92_cs.c b/drivers/net/ethernet/smsc/smc91c92_cs.c index 37c822e27207..29bb19f42de9 100644 --- a/drivers/net/ethernet/smsc/smc91c92_cs.c +++ b/drivers/net/ethernet/smsc/smc91c92_cs.c @@ -1909,8 +1909,8 @@ static int check_if_running(struct net_device *dev) static void smc_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { - strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); - strlcpy(info->version, DRV_VERSION, sizeof(info->version)); + strscpy(info->driver, DRV_NAME, sizeof(info->driver)); + strscpy(info->version, DRV_VERSION, sizeof(info->version)); } static int smc_get_link_ksettings(struct net_device *dev, diff --git a/drivers/net/ethernet/smsc/smc91x.c b/drivers/net/ethernet/smsc/smc91x.c index a31c159e96ea..35e99bf0c401 100644 --- a/drivers/net/ethernet/smsc/smc91x.c +++ b/drivers/net/ethernet/smsc/smc91x.c @@ -1588,9 +1588,9 @@ smc_ethtool_set_link_ksettings(struct net_device *dev, static void smc_ethtool_getdrvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { - strlcpy(info->driver, CARDNAME, sizeof(info->driver)); - strlcpy(info->version, version, sizeof(info->version)); - strlcpy(info->bus_info, dev_name(dev->dev.parent), + strscpy(info->driver, CARDNAME, sizeof(info->driver)); + strscpy(info->version, version, sizeof(info->version)); + strscpy(info->bus_info, dev_name(dev->dev.parent), sizeof(info->bus_info)); } diff --git a/drivers/net/ethernet/smsc/smsc911x.c b/drivers/net/ethernet/smsc/smsc911x.c index 3829c2805b16..a2e511912e6a 100644 --- a/drivers/net/ethernet/smsc/smsc911x.c +++ b/drivers/net/ethernet/smsc/smsc911x.c @@ -1955,9 +1955,9 @@ static int smsc911x_set_mac_address(struct net_device *dev, void *p) static void smsc911x_ethtool_getdrvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { - strlcpy(info->driver, SMSC_CHIPNAME, sizeof(info->driver)); - strlcpy(info->version, SMSC_DRV_VERSION, sizeof(info->version)); - strlcpy(info->bus_info, dev_name(dev->dev.parent), + strscpy(info->driver, SMSC_CHIPNAME, sizeof(info->driver)); + strscpy(info->version, SMSC_DRV_VERSION, sizeof(info->version)); + strscpy(info->bus_info, dev_name(dev->dev.parent), sizeof(info->bus_info)); } diff --git a/drivers/net/ethernet/smsc/smsc9420.c b/drivers/net/ethernet/smsc/smsc9420.c index 0c68c7f8056d..71fbb358bb7d 100644 --- a/drivers/net/ethernet/smsc/smsc9420.c +++ b/drivers/net/ethernet/smsc/smsc9420.c @@ -215,10 +215,10 @@ static void smsc9420_ethtool_get_drvinfo(struct net_device *netdev, { struct smsc9420_pdata *pd = netdev_priv(netdev); - strlcpy(drvinfo->driver, DRV_NAME, sizeof(drvinfo->driver)); - strlcpy(drvinfo->bus_info, pci_name(pd->pdev), + strscpy(drvinfo->driver, DRV_NAME, sizeof(drvinfo->driver)); + strscpy(drvinfo->bus_info, pci_name(pd->pdev), sizeof(drvinfo->bus_info)); - strlcpy(drvinfo->version, DRV_VERSION, sizeof(drvinfo->version)); + strscpy(drvinfo->version, DRV_VERSION, sizeof(drvinfo->version)); } static u32 smsc9420_ethtool_get_msglevel(struct net_device *netdev) @@ -1585,7 +1585,7 @@ smsc9420_probe(struct pci_dev *pdev, const struct pci_device_id *id) dev->netdev_ops = &smsc9420_netdev_ops; dev->ethtool_ops = &smsc9420_ethtool_ops; - netif_napi_add(dev, &pd->napi, smsc9420_rx_poll, NAPI_POLL_WEIGHT); + netif_napi_add(dev, &pd->napi, smsc9420_rx_poll); result = register_netdev(dev); if (result) { diff --git a/drivers/net/ethernet/socionext/netsec.c b/drivers/net/ethernet/socionext/netsec.c index b0c5a44785fa..2240f6d0b89b 100644 --- a/drivers/net/ethernet/socionext/netsec.c +++ b/drivers/net/ethernet/socionext/netsec.c @@ -526,8 +526,8 @@ static int netsec_phy_read(struct mii_bus *bus, int phy_addr, int reg_addr) static void netsec_et_get_drvinfo(struct net_device *net_device, struct ethtool_drvinfo *info) { - strlcpy(info->driver, "netsec", sizeof(info->driver)); - strlcpy(info->bus_info, dev_name(net_device->dev.parent), + strscpy(info->driver, "netsec", sizeof(info->driver)); + strscpy(info->bus_info, dev_name(net_device->dev.parent), sizeof(info->bus_info)); } @@ -2093,7 +2093,7 @@ static int netsec_probe(struct platform_device *pdev) dev_info(&pdev->dev, "hardware revision %d.%d\n", hw_ver >> 16, hw_ver & 0xffff); - netif_napi_add(ndev, &priv->napi, netsec_napi_poll, NAPI_POLL_WEIGHT); + netif_napi_add(ndev, &priv->napi, netsec_napi_poll); ndev->netdev_ops = &netsec_netdev_ops; ndev->ethtool_ops = &netsec_ethtool_ops; diff --git a/drivers/net/ethernet/socionext/sni_ave.c b/drivers/net/ethernet/socionext/sni_ave.c index f0c8de2c6075..1fa09b49ba7f 100644 --- a/drivers/net/ethernet/socionext/sni_ave.c +++ b/drivers/net/ethernet/socionext/sni_ave.c @@ -395,8 +395,8 @@ static void ave_ethtool_get_drvinfo(struct net_device *ndev, { struct device *dev = ndev->dev.parent; - strlcpy(info->driver, dev->driver->name, sizeof(info->driver)); - strlcpy(info->bus_info, dev_name(dev), sizeof(info->bus_info)); + strscpy(info->driver, dev->driver->name, sizeof(info->driver)); + strscpy(info->bus_info, dev_name(dev), sizeof(info->bus_info)); ave_hw_read_version(ndev, info->fw_version, sizeof(info->fw_version)); } @@ -1687,8 +1687,7 @@ static int ave_probe(struct platform_device *pdev) pdev->name, pdev->id); /* Register as a NAPI supported driver */ - netif_napi_add(ndev, &priv->napi_rx, ave_napi_poll_rx, - NAPI_POLL_WEIGHT); + netif_napi_add(ndev, &priv->napi_rx, ave_napi_poll_rx); netif_napi_add_tx(ndev, &priv->napi_tx, ave_napi_poll_tx); platform_set_drvdata(pdev, ndev); diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-dwc-qos-eth.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-dwc-qos-eth.c index 358fc26f8d1f..80efdeeb0b59 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-dwc-qos-eth.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-dwc-qos-eth.c @@ -445,9 +445,7 @@ static int dwc_eth_dwmac_probe(struct platform_device *pdev) ret = data->probe(pdev, plat_dat, &stmmac_res); if (ret < 0) { - if (ret != -EPROBE_DEFER) - dev_err(&pdev->dev, "failed to probe subdriver: %d\n", - ret); + dev_err_probe(&pdev->dev, ret, "failed to probe subdriver\n"); goto remove_config; } diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c index 9af25be42401..0a2afc1a3124 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c @@ -610,7 +610,6 @@ static int intel_mgbe_common_data(struct pci_dev *pdev, plat->int_snapshot_num = AUX_SNAPSHOT1; plat->ext_snapshot_num = AUX_SNAPSHOT0; - plat->has_crossts = true; plat->crosststamp = intel_crosststamp; plat->int_snapshot_en = 0; diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c index c469abc91fa1..f7269d79a385 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c @@ -32,6 +32,8 @@ struct rk_gmac_ops { void (*set_to_rmii)(struct rk_priv_data *bsp_priv); void (*set_rgmii_speed)(struct rk_priv_data *bsp_priv, int speed); void (*set_rmii_speed)(struct rk_priv_data *bsp_priv, int speed); + void (*set_clock_selection)(struct rk_priv_data *bsp_priv, bool input, + bool enable); void (*integrated_phy_powerup)(struct rk_priv_data *bsp_priv); bool regs_valid; u32 regs[]; @@ -66,6 +68,7 @@ struct rk_priv_data { int rx_delay; struct regmap *grf; + struct regmap *php_grf; }; #define HIWORD_UPDATE(val, mask, shift) \ @@ -1101,6 +1104,147 @@ static const struct rk_gmac_ops rk3568_ops = { }, }; +/* sys_grf */ +#define RK3588_GRF_GMAC_CON7 0X031c +#define RK3588_GRF_GMAC_CON8 0X0320 +#define RK3588_GRF_GMAC_CON9 0X0324 + +#define RK3588_GMAC_RXCLK_DLY_ENABLE(id) GRF_BIT(2 * (id) + 3) +#define RK3588_GMAC_RXCLK_DLY_DISABLE(id) GRF_CLR_BIT(2 * (id) + 3) +#define RK3588_GMAC_TXCLK_DLY_ENABLE(id) GRF_BIT(2 * (id) + 2) +#define RK3588_GMAC_TXCLK_DLY_DISABLE(id) GRF_CLR_BIT(2 * (id) + 2) + +#define RK3588_GMAC_CLK_RX_DL_CFG(val) HIWORD_UPDATE(val, 0xFF, 8) +#define RK3588_GMAC_CLK_TX_DL_CFG(val) HIWORD_UPDATE(val, 0xFF, 0) + +/* php_grf */ +#define RK3588_GRF_GMAC_CON0 0X0008 +#define RK3588_GRF_CLK_CON1 0X0070 + +#define RK3588_GMAC_PHY_INTF_SEL_RGMII(id) \ + (GRF_BIT(3 + (id) * 6) | GRF_CLR_BIT(4 + (id) * 6) | GRF_CLR_BIT(5 + (id) * 6)) +#define RK3588_GMAC_PHY_INTF_SEL_RMII(id) \ + (GRF_CLR_BIT(3 + (id) * 6) | GRF_CLR_BIT(4 + (id) * 6) | GRF_BIT(5 + (id) * 6)) + +#define RK3588_GMAC_CLK_RMII_MODE(id) GRF_BIT(5 * (id)) +#define RK3588_GMAC_CLK_RGMII_MODE(id) GRF_CLR_BIT(5 * (id)) + +#define RK3588_GMAC_CLK_SELET_CRU(id) GRF_BIT(5 * (id) + 4) +#define RK3588_GMAC_CLK_SELET_IO(id) GRF_CLR_BIT(5 * (id) + 4) + +#define RK3588_GMA_CLK_RMII_DIV2(id) GRF_BIT(5 * (id) + 2) +#define RK3588_GMA_CLK_RMII_DIV20(id) GRF_CLR_BIT(5 * (id) + 2) + +#define RK3588_GMAC_CLK_RGMII_DIV1(id) \ + (GRF_CLR_BIT(5 * (id) + 2) | GRF_CLR_BIT(5 * (id) + 3)) +#define RK3588_GMAC_CLK_RGMII_DIV5(id) \ + (GRF_BIT(5 * (id) + 2) | GRF_BIT(5 * (id) + 3)) +#define RK3588_GMAC_CLK_RGMII_DIV50(id) \ + (GRF_CLR_BIT(5 * (id) + 2) | GRF_BIT(5 * (id) + 3)) + +#define RK3588_GMAC_CLK_RMII_GATE(id) GRF_BIT(5 * (id) + 1) +#define RK3588_GMAC_CLK_RMII_NOGATE(id) GRF_CLR_BIT(5 * (id) + 1) + +static void rk3588_set_to_rgmii(struct rk_priv_data *bsp_priv, + int tx_delay, int rx_delay) +{ + struct device *dev = &bsp_priv->pdev->dev; + u32 offset_con, id = bsp_priv->id; + + if (IS_ERR(bsp_priv->grf) || IS_ERR(bsp_priv->php_grf)) { + dev_err(dev, "Missing rockchip,grf or rockchip,php_grf property\n"); + return; + } + + offset_con = bsp_priv->id == 1 ? RK3588_GRF_GMAC_CON9 : + RK3588_GRF_GMAC_CON8; + + regmap_write(bsp_priv->php_grf, RK3588_GRF_GMAC_CON0, + RK3588_GMAC_PHY_INTF_SEL_RGMII(id)); + + regmap_write(bsp_priv->php_grf, RK3588_GRF_CLK_CON1, + RK3588_GMAC_CLK_RGMII_MODE(id)); + + regmap_write(bsp_priv->grf, RK3588_GRF_GMAC_CON7, + RK3588_GMAC_RXCLK_DLY_ENABLE(id) | + RK3588_GMAC_TXCLK_DLY_ENABLE(id)); + + regmap_write(bsp_priv->grf, offset_con, + RK3588_GMAC_CLK_RX_DL_CFG(rx_delay) | + RK3588_GMAC_CLK_TX_DL_CFG(tx_delay)); +} + +static void rk3588_set_to_rmii(struct rk_priv_data *bsp_priv) +{ + struct device *dev = &bsp_priv->pdev->dev; + + if (IS_ERR(bsp_priv->php_grf)) { + dev_err(dev, "%s: Missing rockchip,php_grf property\n", __func__); + return; + } + + regmap_write(bsp_priv->php_grf, RK3588_GRF_GMAC_CON0, + RK3588_GMAC_PHY_INTF_SEL_RMII(bsp_priv->id)); + + regmap_write(bsp_priv->php_grf, RK3588_GRF_CLK_CON1, + RK3588_GMAC_CLK_RMII_MODE(bsp_priv->id)); +} + +static void rk3588_set_gmac_speed(struct rk_priv_data *bsp_priv, int speed) +{ + struct device *dev = &bsp_priv->pdev->dev; + unsigned int val = 0, id = bsp_priv->id; + + switch (speed) { + case 10: + if (bsp_priv->phy_iface == PHY_INTERFACE_MODE_RMII) + val = RK3588_GMA_CLK_RMII_DIV20(id); + else + val = RK3588_GMAC_CLK_RGMII_DIV50(id); + break; + case 100: + if (bsp_priv->phy_iface == PHY_INTERFACE_MODE_RMII) + val = RK3588_GMA_CLK_RMII_DIV2(id); + else + val = RK3588_GMAC_CLK_RGMII_DIV5(id); + break; + case 1000: + if (bsp_priv->phy_iface != PHY_INTERFACE_MODE_RMII) + val = RK3588_GMAC_CLK_RGMII_DIV1(id); + else + goto err; + break; + default: + goto err; + } + + regmap_write(bsp_priv->php_grf, RK3588_GRF_CLK_CON1, val); + + return; +err: + dev_err(dev, "unknown speed value for GMAC speed=%d", speed); +} + +static void rk3588_set_clock_selection(struct rk_priv_data *bsp_priv, bool input, + bool enable) +{ + unsigned int val = input ? RK3588_GMAC_CLK_SELET_IO(bsp_priv->id) : + RK3588_GMAC_CLK_SELET_CRU(bsp_priv->id); + + val |= enable ? RK3588_GMAC_CLK_RMII_NOGATE(bsp_priv->id) : + RK3588_GMAC_CLK_RMII_GATE(bsp_priv->id); + + regmap_write(bsp_priv->php_grf, RK3588_GRF_CLK_CON1, val); +} + +static const struct rk_gmac_ops rk3588_ops = { + .set_to_rgmii = rk3588_set_to_rgmii, + .set_to_rmii = rk3588_set_to_rmii, + .set_rgmii_speed = rk3588_set_gmac_speed, + .set_rmii_speed = rk3588_set_gmac_speed, + .set_clock_selection = rk3588_set_clock_selection, +}; + #define RV1108_GRF_GMAC_CON0 0X0900 /* RV1108_GRF_GMAC_CON0 */ @@ -1153,6 +1297,130 @@ static const struct rk_gmac_ops rv1108_ops = { .set_rmii_speed = rv1108_set_rmii_speed, }; +#define RV1126_GRF_GMAC_CON0 0X0070 +#define RV1126_GRF_GMAC_CON1 0X0074 +#define RV1126_GRF_GMAC_CON2 0X0078 + +/* RV1126_GRF_GMAC_CON0 */ +#define RV1126_GMAC_PHY_INTF_SEL_RGMII \ + (GRF_BIT(4) | GRF_CLR_BIT(5) | GRF_CLR_BIT(6)) +#define RV1126_GMAC_PHY_INTF_SEL_RMII \ + (GRF_CLR_BIT(4) | GRF_CLR_BIT(5) | GRF_BIT(6)) +#define RV1126_GMAC_FLOW_CTRL GRF_BIT(7) +#define RV1126_GMAC_FLOW_CTRL_CLR GRF_CLR_BIT(7) +#define RV1126_GMAC_M0_RXCLK_DLY_ENABLE GRF_BIT(1) +#define RV1126_GMAC_M0_RXCLK_DLY_DISABLE GRF_CLR_BIT(1) +#define RV1126_GMAC_M0_TXCLK_DLY_ENABLE GRF_BIT(0) +#define RV1126_GMAC_M0_TXCLK_DLY_DISABLE GRF_CLR_BIT(0) +#define RV1126_GMAC_M1_RXCLK_DLY_ENABLE GRF_BIT(3) +#define RV1126_GMAC_M1_RXCLK_DLY_DISABLE GRF_CLR_BIT(3) +#define RV1126_GMAC_M1_TXCLK_DLY_ENABLE GRF_BIT(2) +#define RV1126_GMAC_M1_TXCLK_DLY_DISABLE GRF_CLR_BIT(2) + +/* RV1126_GRF_GMAC_CON1 */ +#define RV1126_GMAC_M0_CLK_RX_DL_CFG(val) HIWORD_UPDATE(val, 0x7F, 8) +#define RV1126_GMAC_M0_CLK_TX_DL_CFG(val) HIWORD_UPDATE(val, 0x7F, 0) +/* RV1126_GRF_GMAC_CON2 */ +#define RV1126_GMAC_M1_CLK_RX_DL_CFG(val) HIWORD_UPDATE(val, 0x7F, 8) +#define RV1126_GMAC_M1_CLK_TX_DL_CFG(val) HIWORD_UPDATE(val, 0x7F, 0) + +static void rv1126_set_to_rgmii(struct rk_priv_data *bsp_priv, + int tx_delay, int rx_delay) +{ + struct device *dev = &bsp_priv->pdev->dev; + + if (IS_ERR(bsp_priv->grf)) { + dev_err(dev, "Missing rockchip,grf property\n"); + return; + } + + regmap_write(bsp_priv->grf, RV1126_GRF_GMAC_CON0, + RV1126_GMAC_PHY_INTF_SEL_RGMII | + RV1126_GMAC_M0_RXCLK_DLY_ENABLE | + RV1126_GMAC_M0_TXCLK_DLY_ENABLE | + RV1126_GMAC_M1_RXCLK_DLY_ENABLE | + RV1126_GMAC_M1_TXCLK_DLY_ENABLE); + + regmap_write(bsp_priv->grf, RV1126_GRF_GMAC_CON1, + RV1126_GMAC_M0_CLK_RX_DL_CFG(rx_delay) | + RV1126_GMAC_M0_CLK_TX_DL_CFG(tx_delay)); + + regmap_write(bsp_priv->grf, RV1126_GRF_GMAC_CON2, + RV1126_GMAC_M1_CLK_RX_DL_CFG(rx_delay) | + RV1126_GMAC_M1_CLK_TX_DL_CFG(tx_delay)); +} + +static void rv1126_set_to_rmii(struct rk_priv_data *bsp_priv) +{ + struct device *dev = &bsp_priv->pdev->dev; + + if (IS_ERR(bsp_priv->grf)) { + dev_err(dev, "%s: Missing rockchip,grf property\n", __func__); + return; + } + + regmap_write(bsp_priv->grf, RV1126_GRF_GMAC_CON0, + RV1126_GMAC_PHY_INTF_SEL_RMII); +} + +static void rv1126_set_rgmii_speed(struct rk_priv_data *bsp_priv, int speed) +{ + struct device *dev = &bsp_priv->pdev->dev; + unsigned long rate; + int ret; + + switch (speed) { + case 10: + rate = 2500000; + break; + case 100: + rate = 25000000; + break; + case 1000: + rate = 125000000; + break; + default: + dev_err(dev, "unknown speed value for RGMII speed=%d", speed); + return; + } + + ret = clk_set_rate(bsp_priv->clk_mac_speed, rate); + if (ret) + dev_err(dev, "%s: set clk_mac_speed rate %ld failed %d\n", + __func__, rate, ret); +} + +static void rv1126_set_rmii_speed(struct rk_priv_data *bsp_priv, int speed) +{ + struct device *dev = &bsp_priv->pdev->dev; + unsigned long rate; + int ret; + + switch (speed) { + case 10: + rate = 2500000; + break; + case 100: + rate = 25000000; + break; + default: + dev_err(dev, "unknown speed value for RGMII speed=%d", speed); + return; + } + + ret = clk_set_rate(bsp_priv->clk_mac_speed, rate); + if (ret) + dev_err(dev, "%s: set clk_mac_speed rate %ld failed %d\n", + __func__, rate, ret); +} + +static const struct rk_gmac_ops rv1126_ops = { + .set_to_rgmii = rv1126_set_to_rgmii, + .set_to_rmii = rv1126_set_to_rmii, + .set_rgmii_speed = rv1126_set_rgmii_speed, + .set_rmii_speed = rv1126_set_rmii_speed, +}; + #define RK_GRF_MACPHY_CON0 0xb00 #define RK_GRF_MACPHY_CON1 0xb04 #define RK_GRF_MACPHY_CON2 0xb08 @@ -1304,6 +1572,10 @@ static int gmac_clk_enable(struct rk_priv_data *bsp_priv, bool enable) if (!IS_ERR(bsp_priv->clk_mac_speed)) clk_prepare_enable(bsp_priv->clk_mac_speed); + if (bsp_priv->ops && bsp_priv->ops->set_clock_selection) + bsp_priv->ops->set_clock_selection(bsp_priv, + bsp_priv->clock_input, true); + /** * if (!IS_ERR(bsp_priv->clk_mac)) * clk_prepare_enable(bsp_priv->clk_mac); @@ -1330,6 +1602,10 @@ static int gmac_clk_enable(struct rk_priv_data *bsp_priv, bool enable) clk_disable_unprepare(bsp_priv->mac_clk_tx); clk_disable_unprepare(bsp_priv->clk_mac_speed); + + if (bsp_priv->ops && bsp_priv->ops->set_clock_selection) + bsp_priv->ops->set_clock_selection(bsp_priv, + bsp_priv->clock_input, false); /** * if (!IS_ERR(bsp_priv->clk_mac)) * clk_disable_unprepare(bsp_priv->clk_mac); @@ -1444,6 +1720,8 @@ static struct rk_priv_data *rk_gmac_setup(struct platform_device *pdev, bsp_priv->grf = syscon_regmap_lookup_by_phandle(dev->of_node, "rockchip,grf"); + bsp_priv->php_grf = syscon_regmap_lookup_by_phandle(dev->of_node, + "rockchip,php-grf"); if (plat->phy_node) { bsp_priv->integrated_phy = of_property_read_bool(plat->phy_node, @@ -1680,7 +1958,9 @@ static const struct of_device_id rk_gmac_dwmac_match[] = { { .compatible = "rockchip,rk3368-gmac", .data = &rk3368_ops }, { .compatible = "rockchip,rk3399-gmac", .data = &rk3399_ops }, { .compatible = "rockchip,rk3568-gmac", .data = &rk3568_ops }, + { .compatible = "rockchip,rk3588-gmac", .data = &rk3588_ops }, { .compatible = "rockchip,rv1108-gmac", .data = &rv1108_ops }, + { .compatible = "rockchip,rv1126-gmac", .data = &rv1126_ops }, { } }; MODULE_DEVICE_TABLE(of, rk_gmac_dwmac_match); diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac100.h b/drivers/net/ethernet/stmicro/stmmac/dwmac100.h index 35ab8d0bdce7..7ab791c8d355 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac100.h +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac100.h @@ -56,7 +56,7 @@ #define MAC_CONTROL_TE 0x00000008 /* Transmitter Enable */ #define MAC_CONTROL_RE 0x00000004 /* Receiver Enable */ -#define MAC_CORE_INIT (MAC_CONTROL_HBD | MAC_CONTROL_ASTP) +#define MAC_CORE_INIT (MAC_CONTROL_HBD) /* MAC FLOW CTRL defines */ #define MAC_FLOW_CTRL_PT_MASK 0xffff0000 /* Pause Time Mask */ diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac1000.h b/drivers/net/ethernet/stmicro/stmmac/dwmac1000.h index 3c73453725f9..4296ddda8aaa 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac1000.h +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac1000.h @@ -126,7 +126,7 @@ enum inter_frame_gap { #define GMAC_CONTROL_TE 0x00000008 /* Transmitter Enable */ #define GMAC_CONTROL_RE 0x00000004 /* Receiver Enable */ -#define GMAC_CORE_INIT (GMAC_CONTROL_JD | GMAC_CONTROL_PS | GMAC_CONTROL_ACS | \ +#define GMAC_CORE_INIT (GMAC_CONTROL_JD | GMAC_CONTROL_PS | \ GMAC_CONTROL_BE | GMAC_CONTROL_DCRS) /* GMAC Frame Filter defines */ diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c index 76edb9b72675..0e00dd83d027 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c @@ -15,7 +15,6 @@ #include <linux/crc32.h> #include <linux/slab.h> #include <linux/ethtool.h> -#include <net/dsa.h> #include <asm/io.h> #include "stmmac.h" #include "stmmac_pcs.h" @@ -24,7 +23,6 @@ static void dwmac1000_core_init(struct mac_device_info *hw, struct net_device *dev) { - struct stmmac_priv *priv = netdev_priv(dev); void __iomem *ioaddr = hw->pcsr; u32 value = readl(ioaddr + GMAC_CONTROL); int mtu = dev->mtu; @@ -32,13 +30,6 @@ static void dwmac1000_core_init(struct mac_device_info *hw, /* Configure GMAC core */ value |= GMAC_CORE_INIT; - /* Clear ACS bit because Ethernet switch tagging formats such as - * Broadcom tags can look like invalid LLC/SNAP packets and cause the - * hardware to truncate packets on reception. - */ - if (netdev_uses_dsa(dev) || !priv->plat->enh_desc) - value &= ~GMAC_CONTROL_ACS; - if (mtu > 1500) value |= GMAC_CONTROL_2K; if (mtu > 2000) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac100_core.c b/drivers/net/ethernet/stmicro/stmmac/dwmac100_core.c index 75071a7d551a..a6e8d7bd9588 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac100_core.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac100_core.c @@ -15,7 +15,6 @@ *******************************************************************************/ #include <linux/crc32.h> -#include <net/dsa.h> #include <asm/io.h> #include "stmmac.h" #include "dwmac100.h" @@ -28,13 +27,6 @@ static void dwmac100_core_init(struct mac_device_info *hw, value |= MAC_CORE_INIT; - /* Clear ASTP bit because Ethernet switch tagging formats such as - * Broadcom tags can look like invalid LLC/SNAP packets and cause the - * hardware to truncate packets on reception. - */ - if (netdev_uses_dsa(dev)) - value &= ~MAC_CONTROL_ASTP; - writel(value, ioaddr + MAC_CONTROL); #ifdef STMMAC_VLAN_TAG_USED diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c index d8f1fbc25bdd..c25bfecb4a2d 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c @@ -14,7 +14,6 @@ #include <linux/slab.h> #include <linux/ethtool.h> #include <linux/io.h> -#include <net/dsa.h> #include "stmmac.h" #include "stmmac_pcs.h" #include "dwmac4.h" diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c index d6a44d53fe08..f453b0d09366 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c @@ -287,15 +287,15 @@ static void stmmac_ethtool_getdrvinfo(struct net_device *dev, struct stmmac_priv *priv = netdev_priv(dev); if (priv->plat->has_gmac || priv->plat->has_gmac4) - strlcpy(info->driver, GMAC_ETHTOOL_NAME, sizeof(info->driver)); + strscpy(info->driver, GMAC_ETHTOOL_NAME, sizeof(info->driver)); else if (priv->plat->has_xgmac) - strlcpy(info->driver, XGMAC_ETHTOOL_NAME, sizeof(info->driver)); + strscpy(info->driver, XGMAC_ETHTOOL_NAME, sizeof(info->driver)); else - strlcpy(info->driver, MAC100_ETHTOOL_NAME, + strscpy(info->driver, MAC100_ETHTOOL_NAME, sizeof(info->driver)); if (priv->plat->pdev) { - strlcpy(info->bus_info, pci_name(priv->plat->pdev), + strscpy(info->bus_info, pci_name(priv->plat->pdev), sizeof(info->bus_info)); } } diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 9083159b93f1..65c96773c6d2 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -5089,16 +5089,8 @@ read_again: buf1_len = stmmac_rx_buf1_len(priv, p, status, len); len += buf1_len; - /* ACS is set; GMAC core strips PAD/FCS for IEEE 802.3 - * Type frames (LLC/LLC-SNAP) - * - * llc_snap is never checked in GMAC >= 4, so this ACS - * feature is always disabled and packets need to be - * stripped manually. - */ - if (likely(!(status & rx_not_ls)) && - (likely(priv->synopsys_id >= DWMAC_CORE_4_00) || - unlikely(status != llc_snap))) { + /* ACS is disabled; strip manually. */ + if (likely(!(status & rx_not_ls))) { buf1_len -= ETH_FCS_LEN; len -= ETH_FCS_LEN; } @@ -5275,16 +5267,8 @@ read_again: buf2_len = stmmac_rx_buf2_len(priv, p, status, len); len += buf2_len; - /* ACS is set; GMAC core strips PAD/FCS for IEEE 802.3 - * Type frames (LLC/LLC-SNAP) - * - * llc_snap is never checked in GMAC >= 4, so this ACS - * feature is always disabled and packets need to be - * stripped manually. - */ - if (likely(!(status & rx_not_ls)) && - (likely(priv->synopsys_id >= DWMAC_CORE_4_00) || - unlikely(status != llc_snap))) { + /* ACS is disabled; strip manually. */ + if (likely(!(status & rx_not_ls))) { if (buf2_len) { buf2_len -= ETH_FCS_LEN; len -= ETH_FCS_LEN; @@ -6903,8 +6887,7 @@ static void stmmac_napi_add(struct net_device *dev) spin_lock_init(&ch->lock); if (queue < priv->plat->rx_queues_to_use) { - netif_napi_add(dev, &ch->rx_napi, stmmac_napi_poll_rx, - NAPI_POLL_WEIGHT); + netif_napi_add(dev, &ch->rx_napi, stmmac_napi_poll_rx); } if (queue < priv->plat->tx_queues_to_use) { netif_napi_add_tx(dev, &ch->tx_napi, @@ -6913,8 +6896,7 @@ static void stmmac_napi_add(struct net_device *dev) if (queue < priv->plat->rx_queues_to_use && queue < priv->plat->tx_queues_to_use) { netif_napi_add(dev, &ch->rxtx_napi, - stmmac_napi_poll_rxtx, - NAPI_POLL_WEIGHT); + stmmac_napi_poll_rxtx); } } } diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c index 9f5cac4000da..50f6b4a14be4 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c @@ -440,11 +440,12 @@ stmmac_probe_config_dt(struct platform_device *pdev, u8 *mac) /* Default to phy auto-detection */ plat->phy_addr = -1; - /* Default to get clk_csr from stmmac_clk_crs_set(), + /* Default to get clk_csr from stmmac_clk_csr_set(), * or get clk_csr from device tree. */ plat->clk_csr = -1; - of_property_read_u32(np, "clk_csr", &plat->clk_csr); + if (of_property_read_u32(np, "snps,clk-csr", &plat->clk_csr)) + of_property_read_u32(np, "clk_csr", &plat->clk_csr); /* "snps,phy-addr" is not a standard property. Mark it as deprecated * and warn of its use. Remove this when phy node support is added. diff --git a/drivers/net/ethernet/sun/cassini.c b/drivers/net/ethernet/sun/cassini.c index 0b08b0e085e8..0aca193d9550 100644 --- a/drivers/net/ethernet/sun/cassini.c +++ b/drivers/net/ethernet/sun/cassini.c @@ -4484,9 +4484,9 @@ static void cas_set_multicast(struct net_device *dev) static void cas_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { struct cas *cp = netdev_priv(dev); - strlcpy(info->driver, DRV_MODULE_NAME, sizeof(info->driver)); - strlcpy(info->version, DRV_MODULE_VERSION, sizeof(info->version)); - strlcpy(info->bus_info, pci_name(cp->pdev), sizeof(info->bus_info)); + strscpy(info->driver, DRV_MODULE_NAME, sizeof(info->driver)); + strscpy(info->version, DRV_MODULE_VERSION, sizeof(info->version)); + strscpy(info->bus_info, pci_name(cp->pdev), sizeof(info->bus_info)); } static int cas_get_link_ksettings(struct net_device *dev, @@ -5050,7 +5050,7 @@ static int cas_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) dev->watchdog_timeo = CAS_TX_TIMEOUT; #ifdef USE_NAPI - netif_napi_add(dev, &cp->napi, cas_poll, 64); + netif_napi_add(dev, &cp->napi, cas_poll); #endif dev->irq = pdev->irq; dev->dma = 0; diff --git a/drivers/net/ethernet/sun/ldmvsw.c b/drivers/net/ethernet/sun/ldmvsw.c index 0cd8493b810f..8addee6d04bd 100644 --- a/drivers/net/ethernet/sun/ldmvsw.c +++ b/drivers/net/ethernet/sun/ldmvsw.c @@ -63,8 +63,8 @@ static struct vio_version vsw_versions[] = { static void vsw_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { - strlcpy(info->driver, DRV_MODULE_NAME, sizeof(info->driver)); - strlcpy(info->version, DRV_MODULE_VERSION, sizeof(info->version)); + strscpy(info->driver, DRV_MODULE_NAME, sizeof(info->driver)); + strscpy(info->version, DRV_MODULE_VERSION, sizeof(info->version)); } static u32 vsw_get_msglevel(struct net_device *dev) @@ -354,8 +354,7 @@ static int vsw_port_probe(struct vio_dev *vdev, const struct vio_device_id *id) dev_set_drvdata(&vdev->dev, port); - netif_napi_add(dev, &port->napi, sunvnet_poll_common, - NAPI_POLL_WEIGHT); + netif_napi_add(dev, &port->napi, sunvnet_poll_common); spin_lock_irqsave(&vp->lock, flags); list_add_rcu(&port->list, &vp->port_list); diff --git a/drivers/net/ethernet/sun/niu.c b/drivers/net/ethernet/sun/niu.c index df70df29deea..e6144d963eaa 100644 --- a/drivers/net/ethernet/sun/niu.c +++ b/drivers/net/ethernet/sun/niu.c @@ -6798,12 +6798,12 @@ static void niu_get_drvinfo(struct net_device *dev, struct niu *np = netdev_priv(dev); struct niu_vpd *vpd = &np->vpd; - strlcpy(info->driver, DRV_MODULE_NAME, sizeof(info->driver)); - strlcpy(info->version, DRV_MODULE_VERSION, sizeof(info->version)); + strscpy(info->driver, DRV_MODULE_NAME, sizeof(info->driver)); + strscpy(info->version, DRV_MODULE_VERSION, sizeof(info->version)); snprintf(info->fw_version, sizeof(info->fw_version), "%d.%d", vpd->fcode_major, vpd->fcode_minor); if (np->parent->plat_type != PLAT_TYPE_NIU) - strlcpy(info->bus_info, pci_name(np->pdev), + strscpy(info->bus_info, pci_name(np->pdev), sizeof(info->bus_info)); } @@ -9115,7 +9115,7 @@ static int niu_ldg_init(struct niu *np) for (i = 0; i < np->num_ldg; i++) { struct niu_ldg *lp = &np->ldg[i]; - netif_napi_add(np->dev, &lp->napi, niu_poll, 64); + netif_napi_add(np->dev, &lp->napi, niu_poll); lp->np = np; lp->ldg_num = ldg_num_map[i]; diff --git a/drivers/net/ethernet/sun/sunbmac.c b/drivers/net/ethernet/sun/sunbmac.c index 531a6f449afa..34b94153bf0c 100644 --- a/drivers/net/ethernet/sun/sunbmac.c +++ b/drivers/net/ethernet/sun/sunbmac.c @@ -1038,8 +1038,8 @@ static void bigmac_set_multicast(struct net_device *dev) /* Ethtool support... */ static void bigmac_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { - strlcpy(info->driver, "sunbmac", sizeof(info->driver)); - strlcpy(info->version, "2.0", sizeof(info->version)); + strscpy(info->driver, "sunbmac", sizeof(info->driver)); + strscpy(info->version, "2.0", sizeof(info->version)); } static u32 bigmac_get_link(struct net_device *dev) diff --git a/drivers/net/ethernet/sun/sungem.c b/drivers/net/ethernet/sun/sungem.c index a14591b41acb..4154e68639ac 100644 --- a/drivers/net/ethernet/sun/sungem.c +++ b/drivers/net/ethernet/sun/sungem.c @@ -2521,9 +2521,9 @@ static void gem_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info { struct gem *gp = netdev_priv(dev); - strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); - strlcpy(info->version, DRV_VERSION, sizeof(info->version)); - strlcpy(info->bus_info, pci_name(gp->pdev), sizeof(info->bus_info)); + strscpy(info->driver, DRV_NAME, sizeof(info->driver)); + strscpy(info->version, DRV_VERSION, sizeof(info->version)); + strscpy(info->bus_info, pci_name(gp->pdev), sizeof(info->bus_info)); } static int gem_get_link_ksettings(struct net_device *dev, @@ -2980,7 +2980,7 @@ static int gem_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) goto err_out_free_consistent; dev->netdev_ops = &gem_netdev_ops; - netif_napi_add(dev, &gp->napi, gem_poll, 64); + netif_napi_add(dev, &gp->napi, gem_poll); dev->ethtool_ops = &gem_ethtool_ops; dev->watchdog_timeo = 5 * HZ; dev->dma = 0; diff --git a/drivers/net/ethernet/sun/sunhme.c b/drivers/net/ethernet/sun/sunhme.c index 88aa0d310aee..62deed210a95 100644 --- a/drivers/net/ethernet/sun/sunhme.c +++ b/drivers/net/ethernet/sun/sunhme.c @@ -61,15 +61,8 @@ #include "sunhme.h" #define DRV_NAME "sunhme" -#define DRV_VERSION "3.10" -#define DRV_RELDATE "August 26, 2008" -#define DRV_AUTHOR "David S. Miller (davem@davemloft.net)" -static char version[] = - DRV_NAME ".c:v" DRV_VERSION " " DRV_RELDATE " " DRV_AUTHOR "\n"; - -MODULE_VERSION(DRV_VERSION); -MODULE_AUTHOR(DRV_AUTHOR); +MODULE_AUTHOR("David S. Miller (davem@davemloft.net)"); MODULE_DESCRIPTION("Sun HappyMealEthernet(HME) 10/100baseT ethernet driver"); MODULE_LICENSE("GPL"); @@ -87,13 +80,17 @@ static struct quattro *qfe_sbus_list; static struct quattro *qfe_pci_list; #endif -#undef HMEDEBUG -#undef SXDEBUG -#undef RXDEBUG -#undef TXDEBUG -#undef TXLOGGING +#define hme_debug(fmt, ...) pr_debug("%s: " fmt, __func__, ##__VA_ARGS__) +#define HMD hme_debug + +/* "Auto Switch Debug" aka phy debug */ +#if 1 +#define ASD hme_debug +#else +#define ASD(...) +#endif -#ifdef TXLOGGING +#if 0 struct hme_tx_logent { unsigned int tstamp; int tx_new, tx_old; @@ -128,46 +125,16 @@ static __inline__ void tx_dump_log(void) this = txlog_cur_entry; for (i = 0; i < TX_LOG_LEN; i++) { - printk("TXLOG[%d]: j[%08x] tx[N(%d)O(%d)] action[%08x] stat[%08x]\n", i, + pr_err("TXLOG[%d]: j[%08x] tx[N(%d)O(%d)] action[%08x] stat[%08x]\n", i, tx_log[this].tstamp, tx_log[this].tx_new, tx_log[this].tx_old, tx_log[this].action, tx_log[this].status); this = (this + 1) & (TX_LOG_LEN - 1); } } -static __inline__ void tx_dump_ring(struct happy_meal *hp) -{ - struct hmeal_init_block *hb = hp->happy_block; - struct happy_meal_txd *tp = &hb->happy_meal_txd[0]; - int i; - - for (i = 0; i < TX_RING_SIZE; i+=4) { - printk("TXD[%d..%d]: [%08x:%08x] [%08x:%08x] [%08x:%08x] [%08x:%08x]\n", - i, i + 4, - le32_to_cpu(tp[i].tx_flags), le32_to_cpu(tp[i].tx_addr), - le32_to_cpu(tp[i + 1].tx_flags), le32_to_cpu(tp[i + 1].tx_addr), - le32_to_cpu(tp[i + 2].tx_flags), le32_to_cpu(tp[i + 2].tx_addr), - le32_to_cpu(tp[i + 3].tx_flags), le32_to_cpu(tp[i + 3].tx_addr)); - } -} -#else -#define tx_add_log(hp, a, s) do { } while(0) -#define tx_dump_log() do { } while(0) -#define tx_dump_ring(hp) do { } while(0) -#endif - -#ifdef HMEDEBUG -#define HMD(x) printk x -#else -#define HMD(x) -#endif - -/* #define AUTO_SWITCH_DEBUG */ - -#ifdef AUTO_SWITCH_DEBUG -#define ASD(x) printk x #else -#define ASD(x) +#define tx_add_log(hp, a, s) +#define tx_dump_log() #endif #define DEFAULT_IPG0 16 /* For lance-mode only */ @@ -343,8 +310,6 @@ static int happy_meal_bb_read(struct happy_meal *hp, int retval = 0; int i; - ASD(("happy_meal_bb_read: reg=%d ", reg)); - /* Enable the MIF BitBang outputs. */ hme_write32(hp, tregs + TCVR_BBOENAB, 1); @@ -378,7 +343,7 @@ static int happy_meal_bb_read(struct happy_meal *hp, (void) BB_GET_BIT2(hp, tregs, (hp->tcvr_type == internal)); (void) BB_GET_BIT2(hp, tregs, (hp->tcvr_type == internal)); (void) BB_GET_BIT2(hp, tregs, (hp->tcvr_type == internal)); - ASD(("value=%x\n", retval)); + ASD("reg=%d value=%x\n", reg, retval); return retval; } @@ -389,7 +354,7 @@ static void happy_meal_bb_write(struct happy_meal *hp, u32 tmp; int i; - ASD(("happy_meal_bb_write: reg=%d value=%x\n", reg, value)); + ASD("reg=%d value=%x\n", reg, value); /* Enable the MIF BitBang outputs. */ hme_write32(hp, tregs + TCVR_BBOENAB, 1); @@ -433,14 +398,13 @@ static int happy_meal_tcvr_read(struct happy_meal *hp, int tries = TCVR_READ_TRIES; int retval; - ASD(("happy_meal_tcvr_read: reg=0x%02x ", reg)); if (hp->tcvr_type == none) { - ASD(("no transceiver, value=TCVR_FAILURE\n")); + ASD("no transceiver, value=TCVR_FAILURE\n"); return TCVR_FAILURE; } if (!(hp->happy_flags & HFLAG_FENABLE)) { - ASD(("doing bit bang\n")); + ASD("doing bit bang\n"); return happy_meal_bb_read(hp, tregs, reg); } @@ -449,11 +413,11 @@ static int happy_meal_tcvr_read(struct happy_meal *hp, while (!(hme_read32(hp, tregs + TCVR_FRAME) & 0x10000) && --tries) udelay(20); if (!tries) { - printk(KERN_ERR "happy meal: Aieee, transceiver MIF read bolixed\n"); + netdev_err(hp->dev, "Aieee, transceiver MIF read bolixed\n"); return TCVR_FAILURE; } retval = hme_read32(hp, tregs + TCVR_FRAME) & 0xffff; - ASD(("value=%04x\n", retval)); + ASD("reg=0x%02x value=%04x\n", reg, retval); return retval; } @@ -465,7 +429,7 @@ static void happy_meal_tcvr_write(struct happy_meal *hp, { int tries = TCVR_WRITE_TRIES; - ASD(("happy_meal_tcvr_write: reg=0x%02x value=%04x\n", reg, value)); + ASD("reg=0x%02x value=%04x\n", reg, value); /* Welcome to Sun Microsystems, can I take your order please? */ if (!(hp->happy_flags & HFLAG_FENABLE)) { @@ -482,7 +446,7 @@ static void happy_meal_tcvr_write(struct happy_meal *hp, /* Anything else? */ if (!tries) - printk(KERN_ERR "happy meal: Aieee, transceiver MIF write bolixed\n"); + netdev_err(hp->dev, "Aieee, transceiver MIF write bolixed\n"); /* Fifty-two cents is your change, have a nice day. */ } @@ -660,8 +624,8 @@ static void happy_meal_timer(struct timer_list *t) /* Enter force mode. */ do_force_mode: hp->sw_bmcr = happy_meal_tcvr_read(hp, tregs, MII_BMCR); - printk(KERN_NOTICE "%s: Auto-Negotiation unsuccessful, trying force link mode\n", - hp->dev->name); + netdev_notice(hp->dev, + "Auto-Negotiation unsuccessful, trying force link mode\n"); hp->sw_bmcr = BMCR_SPEED100; happy_meal_tcvr_write(hp, tregs, MII_BMCR, hp->sw_bmcr); @@ -720,8 +684,8 @@ static void happy_meal_timer(struct timer_list *t) restart_timer = 0; } else { if (hp->timer_ticks >= 10) { - printk(KERN_NOTICE "%s: Auto negotiation successful, link still " - "not completely up.\n", hp->dev->name); + netdev_notice(hp->dev, + "Auto negotiation successful, link still not completely up.\n"); hp->timer_ticks = 0; restart_timer = 1; } else { @@ -776,14 +740,14 @@ static void happy_meal_timer(struct timer_list *t) */ /* Let the user know... */ - printk(KERN_NOTICE "%s: Link down, cable problem?\n", - hp->dev->name); + netdev_notice(hp->dev, + "Link down, cable problem?\n"); ret = happy_meal_init(hp); if (ret) { /* ho hum... */ - printk(KERN_ERR "%s: Error, cannot re-init the " - "Happy Meal.\n", hp->dev->name); + netdev_err(hp->dev, + "Error, cannot re-init the Happy Meal.\n"); } goto out; } @@ -805,8 +769,8 @@ static void happy_meal_timer(struct timer_list *t) case asleep: default: /* Can't happens.... */ - printk(KERN_ERR "%s: Aieee, link timer is asleep but we got one anyways!\n", - hp->dev->name); + netdev_err(hp->dev, + "Aieee, link timer is asleep but we got one anyways!\n"); restart_timer = 0; hp->timer_ticks = 0; hp->timer_state = asleep; /* foo on you */ @@ -830,7 +794,7 @@ static void happy_meal_tx_reset(struct happy_meal *hp, void __iomem *bregs) { int tries = TX_RESET_TRIES; - HMD(("happy_meal_tx_reset: reset, ")); + HMD("reset...\n"); /* Would you like to try our SMCC Delux? */ hme_write32(hp, bregs + BMAC_TXSWRESET, 0); @@ -839,10 +803,10 @@ static void happy_meal_tx_reset(struct happy_meal *hp, void __iomem *bregs) /* Lettuce, tomato, buggy hardware (no extra charge)? */ if (!tries) - printk(KERN_ERR "happy meal: Transceiver BigMac ATTACK!"); + netdev_err(hp->dev, "Transceiver BigMac ATTACK!"); /* Take care. */ - HMD(("done\n")); + HMD("done\n"); } /* hp->happy_lock must be held */ @@ -850,7 +814,7 @@ static void happy_meal_rx_reset(struct happy_meal *hp, void __iomem *bregs) { int tries = RX_RESET_TRIES; - HMD(("happy_meal_rx_reset: reset, ")); + HMD("reset...\n"); /* We have a special on GNU/Viking hardware bugs today. */ hme_write32(hp, bregs + BMAC_RXSWRESET, 0); @@ -859,10 +823,10 @@ static void happy_meal_rx_reset(struct happy_meal *hp, void __iomem *bregs) /* Will that be all? */ if (!tries) - printk(KERN_ERR "happy meal: Receiver BigMac ATTACK!"); + netdev_err(hp->dev, "Receiver BigMac ATTACK!\n"); /* Don't forget your vik_1137125_wa. Have a nice day. */ - HMD(("done\n")); + HMD("done\n"); } #define STOP_TRIES 16 @@ -872,7 +836,7 @@ static void happy_meal_stop(struct happy_meal *hp, void __iomem *gregs) { int tries = STOP_TRIES; - HMD(("happy_meal_stop: reset, ")); + HMD("reset...\n"); /* We're consolidating our STB products, it's your lucky day. */ hme_write32(hp, gregs + GREG_SWRESET, GREG_RESET_ALL); @@ -881,10 +845,10 @@ static void happy_meal_stop(struct happy_meal *hp, void __iomem *gregs) /* Come back next week when we are "Sun Microelectronics". */ if (!tries) - printk(KERN_ERR "happy meal: Fry guys."); + netdev_err(hp->dev, "Fry guys.\n"); /* Remember: "Different name, same old buggy as shit hardware." */ - HMD(("done\n")); + HMD("done\n"); } /* hp->happy_lock must be held */ @@ -913,21 +877,18 @@ static void happy_meal_get_counters(struct happy_meal *hp, void __iomem *bregs) /* hp->happy_lock must be held */ static void happy_meal_poll_stop(struct happy_meal *hp, void __iomem *tregs) { - ASD(("happy_meal_poll_stop: ")); - /* If polling disabled or not polling already, nothing to do. */ if ((hp->happy_flags & (HFLAG_POLLENABLE | HFLAG_POLL)) != (HFLAG_POLLENABLE | HFLAG_POLL)) { - HMD(("not polling, return\n")); + ASD("not polling, return\n"); return; } /* Shut up the MIF. */ - ASD(("were polling, mif ints off, ")); + ASD("were polling, mif ints off, polling off\n"); hme_write32(hp, tregs + TCVR_IMASK, 0xffff); /* Turn off polling. */ - ASD(("polling off, ")); hme_write32(hp, tregs + TCVR_CFG, hme_read32(hp, tregs + TCVR_CFG) & ~(TCV_CFG_PENABLE)); @@ -936,7 +897,7 @@ static void happy_meal_poll_stop(struct happy_meal *hp, void __iomem *tregs) /* Let the bits set. */ udelay(200); - ASD(("done\n")); + ASD("done\n"); } /* Only Sun can take such nice parts and fuck up the programming interface @@ -952,44 +913,40 @@ static int happy_meal_tcvr_reset(struct happy_meal *hp, void __iomem *tregs) int result, tries = TCVR_RESET_TRIES; tconfig = hme_read32(hp, tregs + TCVR_CFG); - ASD(("happy_meal_tcvr_reset: tcfg<%08lx> ", tconfig)); + ASD("tcfg=%08x\n", tconfig); if (hp->tcvr_type == external) { - ASD(("external<")); hme_write32(hp, tregs + TCVR_CFG, tconfig & ~(TCV_CFG_PSELECT)); hp->tcvr_type = internal; hp->paddr = TCV_PADDR_ITX; - ASD(("ISOLATE,")); happy_meal_tcvr_write(hp, tregs, MII_BMCR, (BMCR_LOOPBACK|BMCR_PDOWN|BMCR_ISOLATE)); result = happy_meal_tcvr_read(hp, tregs, MII_BMCR); if (result == TCVR_FAILURE) { - ASD(("phyread_fail>\n")); + ASD("phyread_fail\n"); return -1; } - ASD(("phyread_ok,PSELECT>")); + ASD("external: ISOLATE, phyread_ok, PSELECT\n"); hme_write32(hp, tregs + TCVR_CFG, tconfig | TCV_CFG_PSELECT); hp->tcvr_type = external; hp->paddr = TCV_PADDR_ETX; } else { if (tconfig & TCV_CFG_MDIO1) { - ASD(("internal<PSELECT,")); hme_write32(hp, tregs + TCVR_CFG, (tconfig | TCV_CFG_PSELECT)); - ASD(("ISOLATE,")); happy_meal_tcvr_write(hp, tregs, MII_BMCR, (BMCR_LOOPBACK|BMCR_PDOWN|BMCR_ISOLATE)); result = happy_meal_tcvr_read(hp, tregs, MII_BMCR); if (result == TCVR_FAILURE) { - ASD(("phyread_fail>\n")); + ASD("phyread_fail>\n"); return -1; } - ASD(("phyread_ok,~PSELECT>")); + ASD("internal: PSELECT, ISOLATE, phyread_ok, ~PSELECT\n"); hme_write32(hp, tregs + TCVR_CFG, (tconfig & ~(TCV_CFG_PSELECT))); hp->tcvr_type = internal; hp->paddr = TCV_PADDR_ITX; } } - ASD(("BMCR_RESET ")); + ASD("BMCR_RESET...\n"); happy_meal_tcvr_write(hp, tregs, MII_BMCR, BMCR_RESET); while (--tries) { @@ -1002,10 +959,10 @@ static int happy_meal_tcvr_reset(struct happy_meal *hp, void __iomem *tregs) udelay(20); } if (!tries) { - ASD(("BMCR RESET FAILED!\n")); + ASD("BMCR RESET FAILED!\n"); return -1; } - ASD(("RESET_OK\n")); + ASD("RESET_OK\n"); /* Get fresh copies of the PHY registers. */ hp->sw_bmsr = happy_meal_tcvr_read(hp, tregs, MII_BMSR); @@ -1013,7 +970,7 @@ static int happy_meal_tcvr_reset(struct happy_meal *hp, void __iomem *tregs) hp->sw_physid2 = happy_meal_tcvr_read(hp, tregs, MII_PHYSID2); hp->sw_advertise = happy_meal_tcvr_read(hp, tregs, MII_ADVERTISE); - ASD(("UNISOLATE")); + ASD("UNISOLATE...\n"); hp->sw_bmcr &= ~(BMCR_ISOLATE); happy_meal_tcvr_write(hp, tregs, MII_BMCR, hp->sw_bmcr); @@ -1027,10 +984,10 @@ static int happy_meal_tcvr_reset(struct happy_meal *hp, void __iomem *tregs) udelay(20); } if (!tries) { - ASD((" FAILED!\n")); + ASD("UNISOLATE FAILED!\n"); return -1; } - ASD((" SUCCESS and CSCONFIG_DFBYPASS\n")); + ASD("SUCCESS and CSCONFIG_DFBYPASS\n"); if (!is_lucent_phy(hp)) { result = happy_meal_tcvr_read(hp, tregs, DP83840_CSCONFIG); @@ -1048,60 +1005,55 @@ static void happy_meal_transceiver_check(struct happy_meal *hp, void __iomem *tr { unsigned long tconfig = hme_read32(hp, tregs + TCVR_CFG); - ASD(("happy_meal_transceiver_check: tcfg=%08lx ", tconfig)); + ASD("tcfg=%08lx\n", tconfig); if (hp->happy_flags & HFLAG_POLL) { /* If we are polling, we must stop to get the transceiver type. */ - ASD(("<polling> ")); if (hp->tcvr_type == internal) { if (tconfig & TCV_CFG_MDIO1) { - ASD(("<internal> <poll stop> ")); happy_meal_poll_stop(hp, tregs); hp->paddr = TCV_PADDR_ETX; hp->tcvr_type = external; - ASD(("<external>\n")); tconfig &= ~(TCV_CFG_PENABLE); tconfig |= TCV_CFG_PSELECT; hme_write32(hp, tregs + TCVR_CFG, tconfig); + ASD("poll stop, internal->external\n"); } } else { if (hp->tcvr_type == external) { - ASD(("<external> ")); if (!(hme_read32(hp, tregs + TCVR_STATUS) >> 16)) { - ASD(("<poll stop> ")); happy_meal_poll_stop(hp, tregs); hp->paddr = TCV_PADDR_ITX; hp->tcvr_type = internal; - ASD(("<internal>\n")); hme_write32(hp, tregs + TCVR_CFG, hme_read32(hp, tregs + TCVR_CFG) & ~(TCV_CFG_PSELECT)); + ASD("poll stop, external->internal\n"); } - ASD(("\n")); } else { - ASD(("<none>\n")); + ASD("polling, none\n"); } } } else { u32 reread = hme_read32(hp, tregs + TCVR_CFG); /* Else we can just work off of the MDIO bits. */ - ASD(("<not polling> ")); if (reread & TCV_CFG_MDIO1) { hme_write32(hp, tregs + TCVR_CFG, tconfig | TCV_CFG_PSELECT); hp->paddr = TCV_PADDR_ETX; hp->tcvr_type = external; - ASD(("<external>\n")); + ASD("not polling, external\n"); } else { if (reread & TCV_CFG_MDIO0) { hme_write32(hp, tregs + TCVR_CFG, tconfig & ~(TCV_CFG_PSELECT)); hp->paddr = TCV_PADDR_ITX; hp->tcvr_type = internal; - ASD(("<internal>\n")); + ASD("not polling, internal\n"); } else { - printk(KERN_ERR "happy meal: Transceiver and a coke please."); + netdev_err(hp->dev, + "Transceiver and a coke please."); hp->tcvr_type = none; /* Grrr... */ - ASD(("<none>\n")); + ASD("not polling, none\n"); } } } @@ -1208,15 +1160,14 @@ static void happy_meal_init_rings(struct happy_meal *hp) struct hmeal_init_block *hb = hp->happy_block; int i; - HMD(("happy_meal_init_rings: counters to zero, ")); + HMD("counters to zero\n"); hp->rx_new = hp->rx_old = hp->tx_new = hp->tx_old = 0; /* Free any skippy bufs left around in the rings. */ - HMD(("clean, ")); happy_meal_clean_rings(hp); /* Now get new skippy bufs for the receive ring. */ - HMD(("init rxring, ")); + HMD("init rxring\n"); for (i = 0; i < RX_RING_SIZE; i++) { struct sk_buff *skb; u32 mapping; @@ -1243,11 +1194,11 @@ static void happy_meal_init_rings(struct happy_meal *hp) skb_reserve(skb, RX_OFFSET); } - HMD(("init txring, ")); + HMD("init txring\n"); for (i = 0; i < TX_RING_SIZE; i++) hme_write_txd(hp, &hb->happy_meal_txd[i], 0, 0); - HMD(("done\n")); + HMD("done\n"); } /* hp->happy_lock must be held */ @@ -1294,17 +1245,11 @@ happy_meal_begin_auto_negotiation(struct happy_meal *hp, * XXX so I completely skip checking for it in the BMSR for now. */ -#ifdef AUTO_SWITCH_DEBUG - ASD(("%s: Advertising [ ", hp->dev->name)); - if (hp->sw_advertise & ADVERTISE_10HALF) - ASD(("10H ")); - if (hp->sw_advertise & ADVERTISE_10FULL) - ASD(("10F ")); - if (hp->sw_advertise & ADVERTISE_100HALF) - ASD(("100H ")); - if (hp->sw_advertise & ADVERTISE_100FULL) - ASD(("100F ")); -#endif + ASD("Advertising [ %s%s%s%s]\n", + hp->sw_advertise & ADVERTISE_10HALF ? "10H " : "", + hp->sw_advertise & ADVERTISE_10FULL ? "10F " : "", + hp->sw_advertise & ADVERTISE_100HALF ? "100H " : "", + hp->sw_advertise & ADVERTISE_100FULL ? "100F " : ""); /* Enable Auto-Negotiation, this is usually on already... */ hp->sw_bmcr |= BMCR_ANENABLE; @@ -1324,10 +1269,11 @@ happy_meal_begin_auto_negotiation(struct happy_meal *hp, udelay(10); } if (!timeout) { - printk(KERN_ERR "%s: Happy Meal would not start auto negotiation " - "BMCR=0x%04x\n", hp->dev->name, hp->sw_bmcr); - printk(KERN_NOTICE "%s: Performing force link detection.\n", - hp->dev->name); + netdev_err(hp->dev, + "Happy Meal would not start auto negotiation BMCR=0x%04x\n", + hp->sw_bmcr); + netdev_notice(hp->dev, + "Performing force link detection.\n"); goto force_link; } else { hp->timer_state = arbwait; @@ -1382,70 +1328,69 @@ static int happy_meal_init(struct happy_meal *hp) void __iomem *erxregs = hp->erxregs; void __iomem *bregs = hp->bigmacregs; void __iomem *tregs = hp->tcvregs; + const char *bursts; u32 regtmp, rxcfg; /* If auto-negotiation timer is running, kill it. */ del_timer(&hp->happy_timer); - HMD(("happy_meal_init: happy_flags[%08x] ", - hp->happy_flags)); + HMD("happy_flags[%08x]\n", hp->happy_flags); if (!(hp->happy_flags & HFLAG_INIT)) { - HMD(("set HFLAG_INIT, ")); + HMD("set HFLAG_INIT\n"); hp->happy_flags |= HFLAG_INIT; happy_meal_get_counters(hp, bregs); } /* Stop polling. */ - HMD(("to happy_meal_poll_stop\n")); + HMD("to happy_meal_poll_stop\n"); happy_meal_poll_stop(hp, tregs); /* Stop transmitter and receiver. */ - HMD(("happy_meal_init: to happy_meal_stop\n")); + HMD("to happy_meal_stop\n"); happy_meal_stop(hp, gregs); /* Alloc and reset the tx/rx descriptor chains. */ - HMD(("happy_meal_init: to happy_meal_init_rings\n")); + HMD("to happy_meal_init_rings\n"); happy_meal_init_rings(hp); /* Shut up the MIF. */ - HMD(("happy_meal_init: Disable all MIF irqs (old[%08x]), ", - hme_read32(hp, tregs + TCVR_IMASK))); + HMD("Disable all MIF irqs (old[%08x])\n", + hme_read32(hp, tregs + TCVR_IMASK)); hme_write32(hp, tregs + TCVR_IMASK, 0xffff); /* See if we can enable the MIF frame on this card to speak to the DP83840. */ if (hp->happy_flags & HFLAG_FENABLE) { - HMD(("use frame old[%08x], ", - hme_read32(hp, tregs + TCVR_CFG))); + HMD("use frame old[%08x]\n", + hme_read32(hp, tregs + TCVR_CFG)); hme_write32(hp, tregs + TCVR_CFG, hme_read32(hp, tregs + TCVR_CFG) & ~(TCV_CFG_BENABLE)); } else { - HMD(("use bitbang old[%08x], ", - hme_read32(hp, tregs + TCVR_CFG))); + HMD("use bitbang old[%08x]\n", + hme_read32(hp, tregs + TCVR_CFG)); hme_write32(hp, tregs + TCVR_CFG, hme_read32(hp, tregs + TCVR_CFG) | TCV_CFG_BENABLE); } /* Check the state of the transceiver. */ - HMD(("to happy_meal_transceiver_check\n")); + HMD("to happy_meal_transceiver_check\n"); happy_meal_transceiver_check(hp, tregs); /* Put the Big Mac into a sane state. */ - HMD(("happy_meal_init: ")); switch(hp->tcvr_type) { case none: /* Cannot operate if we don't know the transceiver type! */ - HMD(("AAIEEE no transceiver type, EAGAIN")); + HMD("AAIEEE no transceiver type, EAGAIN\n"); return -EAGAIN; case internal: /* Using the MII buffers. */ - HMD(("internal, using MII, ")); + HMD("internal, using MII\n"); hme_write32(hp, bregs + BMAC_XIFCFG, 0); break; case external: /* Not using the MII, disable it. */ - HMD(("external, disable MII, ")); + HMD("external, disable MII\n"); hme_write32(hp, bregs + BMAC_XIFCFG, BIGMAC_XCFG_MIIDISAB); break; } @@ -1454,18 +1399,16 @@ static int happy_meal_init(struct happy_meal *hp) return -EAGAIN; /* Reset the Happy Meal Big Mac transceiver and the receiver. */ - HMD(("tx/rx reset, ")); + HMD("tx/rx reset\n"); happy_meal_tx_reset(hp, bregs); happy_meal_rx_reset(hp, bregs); /* Set jam size and inter-packet gaps to reasonable defaults. */ - HMD(("jsize/ipg1/ipg2, ")); hme_write32(hp, bregs + BMAC_JSIZE, DEFAULT_JAMSIZE); hme_write32(hp, bregs + BMAC_IGAP1, DEFAULT_IPG1); hme_write32(hp, bregs + BMAC_IGAP2, DEFAULT_IPG2); /* Load up the MAC address and random seed. */ - HMD(("rseed/macaddr, ")); /* The docs recommend to use the 10LSB of our MAC here. */ hme_write32(hp, bregs + BMAC_RSEED, ((e[5] | e[4]<<8)&0x3ff)); @@ -1474,7 +1417,6 @@ static int happy_meal_init(struct happy_meal *hp) hme_write32(hp, bregs + BMAC_MACADDR1, ((e[2] << 8) | e[3])); hme_write32(hp, bregs + BMAC_MACADDR0, ((e[0] << 8) | e[1])); - HMD(("htable, ")); if ((hp->dev->flags & IFF_ALLMULTI) || (netdev_mc_count(hp->dev) > 64)) { hme_write32(hp, bregs + BMAC_HTABLE0, 0xffff); @@ -1504,9 +1446,9 @@ static int happy_meal_init(struct happy_meal *hp) } /* Set the RX and TX ring ptrs. */ - HMD(("ring ptrs rxr[%08x] txr[%08x]\n", - ((__u32)hp->hblock_dvma + hblock_offset(happy_meal_rxd, 0)), - ((__u32)hp->hblock_dvma + hblock_offset(happy_meal_txd, 0)))); + HMD("ring ptrs rxr[%08x] txr[%08x]\n", + ((__u32)hp->hblock_dvma + hblock_offset(happy_meal_rxd, 0)), + ((__u32)hp->hblock_dvma + hblock_offset(happy_meal_txd, 0))); hme_write32(hp, erxregs + ERX_RING, ((__u32)hp->hblock_dvma + hblock_offset(happy_meal_rxd, 0))); hme_write32(hp, etxregs + ETX_RING, @@ -1524,9 +1466,6 @@ static int happy_meal_init(struct happy_meal *hp) | 0x4); /* Set the supported burst sizes. */ - HMD(("happy_meal_init: old[%08x] bursts<", - hme_read32(hp, gregs + GREG_CFG))); - #ifndef CONFIG_SPARC /* It is always PCI and can handle 64byte bursts. */ hme_write32(hp, gregs + GREG_CFG, GREG_CFG_BURST64); @@ -1554,34 +1493,35 @@ static int happy_meal_init(struct happy_meal *hp) } #endif - HMD(("64>")); + bursts = "64"; hme_write32(hp, gregs + GREG_CFG, gcfg); } else if (hp->happy_bursts & DMA_BURST32) { - HMD(("32>")); + bursts = "32"; hme_write32(hp, gregs + GREG_CFG, GREG_CFG_BURST32); } else if (hp->happy_bursts & DMA_BURST16) { - HMD(("16>")); + bursts = "16"; hme_write32(hp, gregs + GREG_CFG, GREG_CFG_BURST16); } else { - HMD(("XXX>")); + bursts = "XXX"; hme_write32(hp, gregs + GREG_CFG, 0); } #endif /* CONFIG_SPARC */ + HMD("old[%08x] bursts<%s>\n", + hme_read32(hp, gregs + GREG_CFG), bursts); + /* Turn off interrupts we do not want to hear. */ - HMD((", enable global interrupts, ")); hme_write32(hp, gregs + GREG_IMASK, (GREG_IMASK_GOTFRAME | GREG_IMASK_RCNTEXP | GREG_IMASK_SENTFRAME | GREG_IMASK_TXPERR)); /* Set the transmit ring buffer size. */ - HMD(("tx rsize=%d oreg[%08x], ", (int)TX_RING_SIZE, - hme_read32(hp, etxregs + ETX_RSIZE))); + HMD("tx rsize=%d oreg[%08x]\n", (int)TX_RING_SIZE, + hme_read32(hp, etxregs + ETX_RSIZE)); hme_write32(hp, etxregs + ETX_RSIZE, (TX_RING_SIZE >> ETX_RSIZE_SHIFT) - 1); /* Enable transmitter DVMA. */ - HMD(("tx dma enable old[%08x], ", - hme_read32(hp, etxregs + ETX_CFG))); + HMD("tx dma enable old[%08x]\n", hme_read32(hp, etxregs + ETX_CFG)); hme_write32(hp, etxregs + ETX_CFG, hme_read32(hp, etxregs + ETX_CFG) | ETX_CFG_DMAENABLE); @@ -1590,21 +1530,23 @@ static int happy_meal_init(struct happy_meal *hp) * properly. I cannot think of a sane way to provide complete * coverage for this hardware bug yet. */ - HMD(("erx regs bug old[%08x]\n", - hme_read32(hp, erxregs + ERX_CFG))); + HMD("erx regs bug old[%08x]\n", + hme_read32(hp, erxregs + ERX_CFG)); hme_write32(hp, erxregs + ERX_CFG, ERX_CFG_DEFAULT(RX_OFFSET)); regtmp = hme_read32(hp, erxregs + ERX_CFG); hme_write32(hp, erxregs + ERX_CFG, ERX_CFG_DEFAULT(RX_OFFSET)); if (hme_read32(hp, erxregs + ERX_CFG) != ERX_CFG_DEFAULT(RX_OFFSET)) { - printk(KERN_ERR "happy meal: Eieee, rx config register gets greasy fries.\n"); - printk(KERN_ERR "happy meal: Trying to set %08x, reread gives %08x\n", - ERX_CFG_DEFAULT(RX_OFFSET), regtmp); + netdev_err(hp->dev, + "Eieee, rx config register gets greasy fries.\n"); + netdev_err(hp->dev, + "Trying to set %08x, reread gives %08x\n", + ERX_CFG_DEFAULT(RX_OFFSET), regtmp); /* XXX Should return failure here... */ } /* Enable Big Mac hash table filter. */ - HMD(("happy_meal_init: enable hash rx_cfg_old[%08x], ", - hme_read32(hp, bregs + BMAC_RXCFG))); + HMD("enable hash rx_cfg_old[%08x]\n", + hme_read32(hp, bregs + BMAC_RXCFG)); rxcfg = BIGMAC_RXCFG_HENABLE | BIGMAC_RXCFG_REJME; if (hp->dev->flags & IFF_PROMISC) rxcfg |= BIGMAC_RXCFG_PMISC; @@ -1614,7 +1556,7 @@ static int happy_meal_init(struct happy_meal *hp) udelay(10); /* Ok, configure the Big Mac transmitter. */ - HMD(("BIGMAC init, ")); + HMD("BIGMAC init\n"); regtmp = 0; if (hp->happy_flags & HFLAG_FULL) regtmp |= BIGMAC_TXCFG_FULLDPLX; @@ -1638,14 +1580,13 @@ static int happy_meal_init(struct happy_meal *hp) if (hp->tcvr_type == external) regtmp |= BIGMAC_XCFG_MIIDISAB; - HMD(("XIF config old[%08x], ", - hme_read32(hp, bregs + BMAC_XIFCFG))); + HMD("XIF config old[%08x]\n", hme_read32(hp, bregs + BMAC_XIFCFG)); hme_write32(hp, bregs + BMAC_XIFCFG, regtmp); /* Start things up. */ - HMD(("tx old[%08x] and rx [%08x] ON!\n", - hme_read32(hp, bregs + BMAC_TXCFG), - hme_read32(hp, bregs + BMAC_RXCFG))); + HMD("tx old[%08x] and rx [%08x] ON!\n", + hme_read32(hp, bregs + BMAC_TXCFG), + hme_read32(hp, bregs + BMAC_RXCFG)); /* Set larger TX/RX size to allow for 802.1q */ hme_write32(hp, bregs + BMAC_TXMAX, ETH_FRAME_LEN + 8); @@ -1735,25 +1676,26 @@ static int happy_meal_is_not_so_happy(struct happy_meal *hp, u32 status) GREG_STAT_MIFIRQ | GREG_STAT_TXEACK | GREG_STAT_TXLERR | GREG_STAT_TXPERR | GREG_STAT_TXTERR | GREG_STAT_SLVERR | GREG_STAT_SLVPERR)) - printk(KERN_ERR "%s: Error interrupt for happy meal, status = %08x\n", - hp->dev->name, status); + netdev_err(hp->dev, + "Error interrupt for happy meal, status = %08x\n", + status); if (status & GREG_STAT_RFIFOVF) { /* Receive FIFO overflow is harmless and the hardware will take care of it, just some packets are lost. Who cares. */ - printk(KERN_DEBUG "%s: Happy Meal receive FIFO overflow.\n", hp->dev->name); + netdev_dbg(hp->dev, "Happy Meal receive FIFO overflow.\n"); } if (status & GREG_STAT_STSTERR) { /* BigMAC SQE link test failed. */ - printk(KERN_ERR "%s: Happy Meal BigMAC SQE test failed.\n", hp->dev->name); + netdev_err(hp->dev, "Happy Meal BigMAC SQE test failed.\n"); reset = 1; } if (status & GREG_STAT_TFIFO_UND) { /* Transmit FIFO underrun, again DMA error likely. */ - printk(KERN_ERR "%s: Happy Meal transmitter FIFO underrun, DMA error.\n", - hp->dev->name); + netdev_err(hp->dev, + "Happy Meal transmitter FIFO underrun, DMA error.\n"); reset = 1; } @@ -1761,7 +1703,7 @@ static int happy_meal_is_not_so_happy(struct happy_meal *hp, u32 status) /* Driver error, tried to transmit something larger * than ethernet max mtu. */ - printk(KERN_ERR "%s: Happy Meal MAX Packet size error.\n", hp->dev->name); + netdev_err(hp->dev, "Happy Meal MAX Packet size error.\n"); reset = 1; } @@ -1771,21 +1713,16 @@ static int happy_meal_is_not_so_happy(struct happy_meal *hp, u32 status) * faster than the interrupt handler could keep up * with. */ - printk(KERN_INFO "%s: Happy Meal out of receive " - "descriptors, packet dropped.\n", - hp->dev->name); + netdev_info(hp->dev, + "Happy Meal out of receive descriptors, packet dropped.\n"); } if (status & (GREG_STAT_RXERR|GREG_STAT_RXPERR|GREG_STAT_RXTERR)) { /* All sorts of DMA receive errors. */ - printk(KERN_ERR "%s: Happy Meal rx DMA errors [ ", hp->dev->name); - if (status & GREG_STAT_RXERR) - printk("GenericError "); - if (status & GREG_STAT_RXPERR) - printk("ParityError "); - if (status & GREG_STAT_RXTERR) - printk("RxTagBotch "); - printk("]\n"); + netdev_err(hp->dev, "Happy Meal rx DMA errors [ %s%s%s]\n", + status & GREG_STAT_RXERR ? "GenericError " : "", + status & GREG_STAT_RXPERR ? "ParityError " : "", + status & GREG_STAT_RXTERR ? "RxTagBotch " : ""); reset = 1; } @@ -1793,29 +1730,24 @@ static int happy_meal_is_not_so_happy(struct happy_meal *hp, u32 status) /* Driver bug, didn't set EOP bit in tx descriptor given * to the happy meal. */ - printk(KERN_ERR "%s: EOP not set in happy meal transmit descriptor!\n", - hp->dev->name); + netdev_err(hp->dev, + "EOP not set in happy meal transmit descriptor!\n"); reset = 1; } if (status & GREG_STAT_MIFIRQ) { /* MIF signalled an interrupt, were we polling it? */ - printk(KERN_ERR "%s: Happy Meal MIF interrupt.\n", hp->dev->name); + netdev_err(hp->dev, "Happy Meal MIF interrupt.\n"); } if (status & (GREG_STAT_TXEACK|GREG_STAT_TXLERR|GREG_STAT_TXPERR|GREG_STAT_TXTERR)) { /* All sorts of transmit DMA errors. */ - printk(KERN_ERR "%s: Happy Meal tx DMA errors [ ", hp->dev->name); - if (status & GREG_STAT_TXEACK) - printk("GenericError "); - if (status & GREG_STAT_TXLERR) - printk("LateError "); - if (status & GREG_STAT_TXPERR) - printk("ParityError "); - if (status & GREG_STAT_TXTERR) - printk("TagBotch "); - printk("]\n"); + netdev_err(hp->dev, "Happy Meal tx DMA errors [ %s%s%s%s]\n", + status & GREG_STAT_TXEACK ? "GenericError " : "", + status & GREG_STAT_TXLERR ? "LateError " : "", + status & GREG_STAT_TXPERR ? "ParityError " : "", + status & GREG_STAT_TXTERR ? "TagBotch " : ""); reset = 1; } @@ -1823,14 +1755,14 @@ static int happy_meal_is_not_so_happy(struct happy_meal *hp, u32 status) /* Bus or parity error when cpu accessed happy meal registers * or it's internal FIFO's. Should never see this. */ - printk(KERN_ERR "%s: Happy Meal register access SBUS slave (%s) error.\n", - hp->dev->name, - (status & GREG_STAT_SLVPERR) ? "parity" : "generic"); + netdev_err(hp->dev, + "Happy Meal register access SBUS slave (%s) error.\n", + (status & GREG_STAT_SLVPERR) ? "parity" : "generic"); reset = 1; } if (reset) { - printk(KERN_NOTICE "%s: Resetting...\n", hp->dev->name); + netdev_notice(hp->dev, "Resetting...\n"); happy_meal_init(hp); return 1; } @@ -1842,22 +1774,22 @@ static void happy_meal_mif_interrupt(struct happy_meal *hp) { void __iomem *tregs = hp->tcvregs; - printk(KERN_INFO "%s: Link status change.\n", hp->dev->name); + netdev_info(hp->dev, "Link status change.\n"); hp->sw_bmcr = happy_meal_tcvr_read(hp, tregs, MII_BMCR); hp->sw_lpa = happy_meal_tcvr_read(hp, tregs, MII_LPA); /* Use the fastest transmission protocol possible. */ if (hp->sw_lpa & LPA_100FULL) { - printk(KERN_INFO "%s: Switching to 100Mbps at full duplex.", hp->dev->name); + netdev_info(hp->dev, "Switching to 100Mbps at full duplex.\n"); hp->sw_bmcr |= (BMCR_FULLDPLX | BMCR_SPEED100); } else if (hp->sw_lpa & LPA_100HALF) { - printk(KERN_INFO "%s: Switching to 100MBps at half duplex.", hp->dev->name); + netdev_info(hp->dev, "Switching to 100MBps at half duplex.\n"); hp->sw_bmcr |= BMCR_SPEED100; } else if (hp->sw_lpa & LPA_10FULL) { - printk(KERN_INFO "%s: Switching to 10MBps at full duplex.", hp->dev->name); + netdev_info(hp->dev, "Switching to 10MBps at full duplex.\n"); hp->sw_bmcr |= BMCR_FULLDPLX; } else { - printk(KERN_INFO "%s: Using 10Mbps at half duplex.", hp->dev->name); + netdev_info(hp->dev, "Using 10Mbps at half duplex.\n"); } happy_meal_tcvr_write(hp, tregs, MII_BMCR, hp->sw_bmcr); @@ -1865,12 +1797,6 @@ static void happy_meal_mif_interrupt(struct happy_meal *hp) happy_meal_poll_stop(hp, tregs); } -#ifdef TXDEBUG -#define TXD(x) printk x -#else -#define TXD(x) -#endif - /* hp->happy_lock must be held */ static void happy_meal_tx(struct happy_meal *hp) { @@ -1880,13 +1806,12 @@ static void happy_meal_tx(struct happy_meal *hp) int elem; elem = hp->tx_old; - TXD(("TX<")); while (elem != hp->tx_new) { struct sk_buff *skb; u32 flags, dma_addr, dma_len; int frag; - TXD(("[%d]", elem)); + netdev_vdbg(hp->dev, "TX[%d]\n", elem); this = &txbase[elem]; flags = hme_read_desc32(hp, &this->tx_flags); if (flags & TXFLAG_OWN) @@ -1922,19 +1847,12 @@ static void happy_meal_tx(struct happy_meal *hp) dev->stats.tx_packets++; } hp->tx_old = elem; - TXD((">")); if (netif_queue_stopped(dev) && TX_BUFFS_AVAIL(hp) > (MAX_SKB_FRAGS + 1)) netif_wake_queue(dev); } -#ifdef RXDEBUG -#define RXD(x) printk x -#else -#define RXD(x) -#endif - /* Originally I used to handle the allocation failure by just giving back just * that one ring buffer to the happy meal. Problem is that usually when that * condition is triggered, the happy meal expects you to do something reasonable @@ -1951,7 +1869,6 @@ static void happy_meal_rx(struct happy_meal *hp, struct net_device *dev) int elem = hp->rx_new, drops = 0; u32 flags; - RXD(("RX<")); this = &rxbase[elem]; while (!((flags = hme_read_desc32(hp, &this->rx_flags)) & RXFLAG_OWN)) { struct sk_buff *skb; @@ -1959,11 +1876,9 @@ static void happy_meal_rx(struct happy_meal *hp, struct net_device *dev) u16 csum = flags & RXFLAG_CSUM; u32 dma_addr = hme_read_desc32(hp, &this->rx_addr); - RXD(("[%d ", elem)); - /* Check for errors. */ if ((len < ETH_ZLEN) || (flags & RXFLAG_OVERFLOW)) { - RXD(("ERR(%08x)]", flags)); + netdev_vdbg(dev, "RX[%d ERR(%08x)]", elem, flags); dev->stats.rx_errors++; if (len < ETH_ZLEN) dev->stats.rx_length_errors++; @@ -2035,7 +1950,7 @@ static void happy_meal_rx(struct happy_meal *hp, struct net_device *dev) skb->csum = csum_unfold(~(__force __sum16)htons(csum)); skb->ip_summed = CHECKSUM_COMPLETE; - RXD(("len=%d csum=%4x]", len, csum)); + netdev_vdbg(dev, "RX[%d len=%d csum=%4x]", elem, len, csum); skb->protocol = eth_type_trans(skb, dev); netif_rx(skb); @@ -2047,8 +1962,7 @@ static void happy_meal_rx(struct happy_meal *hp, struct net_device *dev) } hp->rx_new = elem; if (drops) - printk(KERN_INFO "%s: Memory squeeze, deferring packet.\n", hp->dev->name); - RXD((">")); + netdev_info(hp->dev, "Memory squeeze, deferring packet.\n"); } static irqreturn_t happy_meal_interrupt(int irq, void *dev_id) @@ -2057,32 +1971,25 @@ static irqreturn_t happy_meal_interrupt(int irq, void *dev_id) struct happy_meal *hp = netdev_priv(dev); u32 happy_status = hme_read32(hp, hp->gregs + GREG_STAT); - HMD(("happy_meal_interrupt: status=%08x ", happy_status)); + HMD("status=%08x\n", happy_status); spin_lock(&hp->happy_lock); if (happy_status & GREG_STAT_ERRORS) { - HMD(("ERRORS ")); if (happy_meal_is_not_so_happy(hp, /* un- */ happy_status)) goto out; } - if (happy_status & GREG_STAT_MIFIRQ) { - HMD(("MIFIRQ ")); + if (happy_status & GREG_STAT_MIFIRQ) happy_meal_mif_interrupt(hp); - } - if (happy_status & GREG_STAT_TXALL) { - HMD(("TXALL ")); + if (happy_status & GREG_STAT_TXALL) happy_meal_tx(hp); - } - if (happy_status & GREG_STAT_RXTOHOST) { - HMD(("RXTOHOST ")); + if (happy_status & GREG_STAT_RXTOHOST) happy_meal_rx(hp, dev); - } - HMD(("done\n")); + HMD("done\n"); out: spin_unlock(&hp->happy_lock); @@ -2100,7 +2007,7 @@ static irqreturn_t quattro_sbus_interrupt(int irq, void *cookie) struct happy_meal *hp = netdev_priv(dev); u32 happy_status = hme_read32(hp, hp->gregs + GREG_STAT); - HMD(("quattro_interrupt: status=%08x ", happy_status)); + HMD("status=%08x\n", happy_status); if (!(happy_status & (GREG_STAT_ERRORS | GREG_STAT_MIFIRQ | @@ -2110,31 +2017,23 @@ static irqreturn_t quattro_sbus_interrupt(int irq, void *cookie) spin_lock(&hp->happy_lock); - if (happy_status & GREG_STAT_ERRORS) { - HMD(("ERRORS ")); + if (happy_status & GREG_STAT_ERRORS) if (happy_meal_is_not_so_happy(hp, happy_status)) goto next; - } - if (happy_status & GREG_STAT_MIFIRQ) { - HMD(("MIFIRQ ")); + if (happy_status & GREG_STAT_MIFIRQ) happy_meal_mif_interrupt(hp); - } - if (happy_status & GREG_STAT_TXALL) { - HMD(("TXALL ")); + if (happy_status & GREG_STAT_TXALL) happy_meal_tx(hp); - } - if (happy_status & GREG_STAT_RXTOHOST) { - HMD(("RXTOHOST ")); + if (happy_status & GREG_STAT_RXTOHOST) happy_meal_rx(hp, dev); - } next: spin_unlock(&hp->happy_lock); } - HMD(("done\n")); + HMD("done\n"); return IRQ_HANDLED; } @@ -2145,8 +2044,6 @@ static int happy_meal_open(struct net_device *dev) struct happy_meal *hp = netdev_priv(dev); int res; - HMD(("happy_meal_open: ")); - /* On SBUS Quattro QFE cards, all hme interrupts are concentrated * into a single source which we register handling at probe time. */ @@ -2154,15 +2051,14 @@ static int happy_meal_open(struct net_device *dev) res = request_irq(hp->irq, happy_meal_interrupt, IRQF_SHARED, dev->name, dev); if (res) { - HMD(("EAGAIN\n")); - printk(KERN_ERR "happy_meal(SBUS): Can't order irq %d to go.\n", - hp->irq); + HMD("EAGAIN\n"); + netdev_err(dev, "Can't order irq %d to go.\n", hp->irq); return -EAGAIN; } } - HMD(("to happy_meal_init\n")); + HMD("to happy_meal_init\n"); spin_lock_irq(&hp->happy_lock); res = happy_meal_init(hp); @@ -2196,22 +2092,16 @@ static int happy_meal_close(struct net_device *dev) return 0; } -#ifdef SXDEBUG -#define SXD(x) printk x -#else -#define SXD(x) -#endif - static void happy_meal_tx_timeout(struct net_device *dev, unsigned int txqueue) { struct happy_meal *hp = netdev_priv(dev); - printk (KERN_ERR "%s: transmit timed out, resetting\n", dev->name); + netdev_err(dev, "transmit timed out, resetting\n"); tx_dump_log(); - printk (KERN_ERR "%s: Happy Status %08x TX[%08x:%08x]\n", dev->name, - hme_read32(hp, hp->gregs + GREG_STAT), - hme_read32(hp, hp->etxregs + ETX_CFG), - hme_read32(hp, hp->bigmacregs + BMAC_TXCFG)); + netdev_err(dev, "Happy Status %08x TX[%08x:%08x]\n", + hme_read32(hp, hp->gregs + GREG_STAT), + hme_read32(hp, hp->etxregs + ETX_CFG), + hme_read32(hp, hp->bigmacregs + BMAC_TXCFG)); spin_lock_irq(&hp->happy_lock); happy_meal_init(hp); @@ -2261,13 +2151,12 @@ static netdev_tx_t happy_meal_start_xmit(struct sk_buff *skb, if (TX_BUFFS_AVAIL(hp) <= (skb_shinfo(skb)->nr_frags + 1)) { netif_stop_queue(dev); spin_unlock_irq(&hp->happy_lock); - printk(KERN_ERR "%s: BUG! Tx Ring full when queue awake!\n", - dev->name); + netdev_err(dev, "BUG! Tx Ring full when queue awake!\n"); return NETDEV_TX_BUSY; } entry = hp->tx_new; - SXD(("SX<l[%d]e[%d]>", len, entry)); + netdev_vdbg(dev, "SX<l[%d]e[%d]>\n", skb->len, entry); hp->tx_skbs[entry] = skb; if (skb_shinfo(skb)->nr_frags == 0) { @@ -2467,11 +2356,10 @@ static void hme_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info { struct happy_meal *hp = netdev_priv(dev); - strlcpy(info->driver, "sunhme", sizeof(info->driver)); - strlcpy(info->version, "2.02", sizeof(info->version)); + strscpy(info->driver, DRV_NAME, sizeof(info->driver)); if (hp->happy_flags & HFLAG_PCI) { struct pci_dev *pdev = hp->happy_dev; - strlcpy(info->bus_info, pci_name(pdev), sizeof(info->bus_info)); + strscpy(info->bus_info, pci_name(pdev), sizeof(info->bus_info)); } #ifdef CONFIG_SBUS else { @@ -2504,8 +2392,6 @@ static const struct ethtool_ops hme_ethtool_ops = { .set_link_ksettings = hme_set_link_ksettings, }; -static int hme_version_printed; - #ifdef CONFIG_SBUS /* Given a happy meal sbus device, find it's quattro parent. * If none exist, allocate and return a new one. @@ -2523,19 +2409,15 @@ static struct quattro *quattro_sbus_find(struct platform_device *child) if (qp) return qp; - qp = kmalloc(sizeof(struct quattro), GFP_KERNEL); - if (qp != NULL) { - int i; - - for (i = 0; i < 4; i++) - qp->happy_meals[i] = NULL; + qp = kzalloc(sizeof(*qp), GFP_KERNEL); + if (!qp) + return NULL; - qp->quattro_dev = child; - qp->next = qfe_sbus_list; - qfe_sbus_list = qp; + qp->quattro_dev = child; + qp->next = qfe_sbus_list; + qfe_sbus_list = qp; - platform_set_drvdata(op, qp); - } + platform_set_drvdata(op, qp); return qp; } @@ -2563,8 +2445,9 @@ static int __init quattro_sbus_register_irqs(void) IRQF_SHARED, "Quattro", qp); if (err != 0) { - printk(KERN_ERR "Quattro HME: IRQ registration " - "error %d.\n", err); + dev_err(&op->dev, + "Quattro HME: IRQ registration error %d.\n", + err); return err; } } @@ -2595,30 +2478,33 @@ static void quattro_sbus_free_irqs(void) #ifdef CONFIG_PCI static struct quattro *quattro_pci_find(struct pci_dev *pdev) { + int i; struct pci_dev *bdev = pdev->bus->self; struct quattro *qp; - if (!bdev) return NULL; + if (!bdev) + return ERR_PTR(-ENODEV); + for (qp = qfe_pci_list; qp != NULL; qp = qp->next) { struct pci_dev *qpdev = qp->quattro_dev; if (qpdev == bdev) return qp; } + qp = kmalloc(sizeof(struct quattro), GFP_KERNEL); - if (qp != NULL) { - int i; + if (!qp) + return ERR_PTR(-ENOMEM); - for (i = 0; i < 4; i++) - qp->happy_meals[i] = NULL; + for (i = 0; i < 4; i++) + qp->happy_meals[i] = NULL; - qp->quattro_dev = bdev; - qp->next = qfe_pci_list; - qfe_pci_list = qp; + qp->quattro_dev = bdev; + qp->next = qfe_pci_list; + qfe_pci_list = qp; - /* No range tricks necessary on PCI. */ - qp->nranges = 0; - } + /* No range tricks necessary on PCI. */ + qp->nranges = 0; return qp; } #endif /* CONFIG_PCI */ @@ -2668,9 +2554,6 @@ static int happy_meal_sbus_probe_one(struct platform_device *op, int is_qfe) goto err_out; SET_NETDEV_DEV(dev, &op->dev); - if (hme_version_printed++ == 0) - printk(KERN_INFO "%s", version); - /* If user did not specify a MAC address specifically, use * the Quattro local-mac-address property... */ @@ -2712,35 +2595,35 @@ static int happy_meal_sbus_probe_one(struct platform_device *op, int is_qfe) hp->gregs = of_ioremap(&op->resource[0], 0, GREG_REG_SIZE, "HME Global Regs"); if (!hp->gregs) { - printk(KERN_ERR "happymeal: Cannot map global registers.\n"); + dev_err(&op->dev, "Cannot map global registers.\n"); goto err_out_free_netdev; } hp->etxregs = of_ioremap(&op->resource[1], 0, ETX_REG_SIZE, "HME TX Regs"); if (!hp->etxregs) { - printk(KERN_ERR "happymeal: Cannot map MAC TX registers.\n"); + dev_err(&op->dev, "Cannot map MAC TX registers.\n"); goto err_out_iounmap; } hp->erxregs = of_ioremap(&op->resource[2], 0, ERX_REG_SIZE, "HME RX Regs"); if (!hp->erxregs) { - printk(KERN_ERR "happymeal: Cannot map MAC RX registers.\n"); + dev_err(&op->dev, "Cannot map MAC RX registers.\n"); goto err_out_iounmap; } hp->bigmacregs = of_ioremap(&op->resource[3], 0, BMAC_REG_SIZE, "HME BIGMAC Regs"); if (!hp->bigmacregs) { - printk(KERN_ERR "happymeal: Cannot map BIGMAC registers.\n"); + dev_err(&op->dev, "Cannot map BIGMAC registers.\n"); goto err_out_iounmap; } hp->tcvregs = of_ioremap(&op->resource[4], 0, TCVR_REG_SIZE, "HME Tranceiver Regs"); if (!hp->tcvregs) { - printk(KERN_ERR "happymeal: Cannot map TCVR registers.\n"); + dev_err(&op->dev, "Cannot map TCVR registers.\n"); goto err_out_iounmap; } @@ -2807,21 +2690,19 @@ static int happy_meal_sbus_probe_one(struct platform_device *op, int is_qfe) err = register_netdev(hp->dev); if (err) { - printk(KERN_ERR "happymeal: Cannot register net device, " - "aborting.\n"); + dev_err(&op->dev, "Cannot register net device, aborting.\n"); goto err_out_free_coherent; } platform_set_drvdata(op, hp); if (qfe_slot != -1) - printk(KERN_INFO "%s: Quattro HME slot %d (SBUS) 10/100baseT Ethernet ", - dev->name, qfe_slot); + netdev_info(dev, + "Quattro HME slot %d (SBUS) 10/100baseT Ethernet %pM\n", + qfe_slot, dev->dev_addr); else - printk(KERN_INFO "%s: HAPPY MEAL (SBUS) 10/100baseT Ethernet ", - dev->name); - - printk("%pM\n", dev->dev_addr); + netdev_info(dev, "HAPPY MEAL (SBUS) 10/100baseT Ethernet %pM\n", + dev->dev_addr); return 0; @@ -2949,7 +2830,7 @@ static int happy_meal_pci_probe(struct pci_dev *pdev, struct happy_meal *hp; struct net_device *dev; void __iomem *hpreg_base; - unsigned long hpreg_res; + struct resource *hpreg_res; int i, qfe_slot = -1; char prom_name[64]; u8 addr[ETH_ALEN]; @@ -2966,32 +2847,33 @@ static int happy_meal_pci_probe(struct pci_dev *pdev, strcpy(prom_name, "SUNW,hme"); #endif - err = -ENODEV; - - if (pci_enable_device(pdev)) + err = pcim_enable_device(pdev); + if (err) goto err_out; pci_set_master(pdev); if (!strcmp(prom_name, "SUNW,qfe") || !strcmp(prom_name, "qfe")) { qp = quattro_pci_find(pdev); - if (qp == NULL) + if (IS_ERR(qp)) { + err = PTR_ERR(qp); goto err_out; + } + for (qfe_slot = 0; qfe_slot < 4; qfe_slot++) - if (qp->happy_meals[qfe_slot] == NULL) + if (!qp->happy_meals[qfe_slot]) break; + if (qfe_slot == 4) goto err_out; } - dev = alloc_etherdev(sizeof(struct happy_meal)); - err = -ENOMEM; - if (!dev) + dev = devm_alloc_etherdev(&pdev->dev, sizeof(struct happy_meal)); + if (!dev) { + err = -ENOMEM; goto err_out; + } SET_NETDEV_DEV(dev, &pdev->dev); - if (hme_version_printed++ == 0) - printk(KERN_INFO "%s", version); - hp = netdev_priv(dev); hp->happy_dev = pdev; @@ -3005,21 +2887,26 @@ static int happy_meal_pci_probe(struct pci_dev *pdev, qp->happy_meals[qfe_slot] = dev; } - hpreg_res = pci_resource_start(pdev, 0); - err = -ENODEV; + err = -EINVAL; if ((pci_resource_flags(pdev, 0) & IORESOURCE_IO) != 0) { - printk(KERN_ERR "happymeal(PCI): Cannot find proper PCI device base address.\n"); + dev_err(&pdev->dev, + "Cannot find proper PCI device base address.\n"); goto err_out_clear_quattro; } - if (pci_request_regions(pdev, DRV_NAME)) { - printk(KERN_ERR "happymeal(PCI): Cannot obtain PCI resources, " - "aborting.\n"); + + hpreg_res = devm_request_region(&pdev->dev, pci_resource_start(pdev, 0), + pci_resource_len(pdev, 0), DRV_NAME); + if (IS_ERR(hpreg_res)) { + err = PTR_ERR(hpreg_res); + dev_err(&pdev->dev, "Cannot obtain PCI resources, aborting.\n"); goto err_out_clear_quattro; } - if ((hpreg_base = ioremap(hpreg_res, 0x8000)) == NULL) { - printk(KERN_ERR "happymeal(PCI): Unable to remap card memory.\n"); - goto err_out_free_res; + hpreg_base = pcim_iomap(pdev, 0, 0x8000); + if (!hpreg_base) { + err = -ENOMEM; + dev_err(&pdev->dev, "Unable to remap card memory.\n"); + goto err_out_clear_quattro; } for (i = 0; i < 6; i++) { @@ -3085,11 +2972,12 @@ static int happy_meal_pci_probe(struct pci_dev *pdev, hp->happy_bursts = DMA_BURSTBITS; #endif - hp->happy_block = dma_alloc_coherent(&pdev->dev, PAGE_SIZE, - &hp->hblock_dvma, GFP_KERNEL); - err = -ENODEV; - if (!hp->happy_block) - goto err_out_iounmap; + hp->happy_block = dmam_alloc_coherent(&pdev->dev, PAGE_SIZE, + &hp->hblock_dvma, GFP_KERNEL); + if (!hp->happy_block) { + err = -ENOMEM; + goto err_out_clear_quattro; + } hp->linkcheck = 0; hp->timer_state = asleep; @@ -3123,11 +3011,10 @@ static int happy_meal_pci_probe(struct pci_dev *pdev, happy_meal_set_initial_advertisement(hp); spin_unlock_irq(&hp->happy_lock); - err = register_netdev(hp->dev); + err = devm_register_netdev(&pdev->dev, dev); if (err) { - printk(KERN_ERR "happymeal(PCI): Cannot register net device, " - "aborting.\n"); - goto err_out_free_coherent; + dev_err(&pdev->dev, "Cannot register net device, aborting.\n"); + goto err_out_clear_quattro; } pci_set_drvdata(pdev, hp); @@ -3140,61 +3027,30 @@ static int happy_meal_pci_probe(struct pci_dev *pdev, int i = simple_strtoul(dev->name + 3, NULL, 10); sprintf(prom_name, "-%d", i + 3); } - printk(KERN_INFO "%s%s: Quattro HME (PCI/CheerIO) 10/100baseT Ethernet ", dev->name, prom_name); - if (qpdev->vendor == PCI_VENDOR_ID_DEC && - qpdev->device == PCI_DEVICE_ID_DEC_21153) - printk("DEC 21153 PCI Bridge\n"); - else - printk("unknown bridge %04x.%04x\n", - qpdev->vendor, qpdev->device); + netdev_info(dev, + "%s: Quattro HME (PCI/CheerIO) 10/100baseT Ethernet bridge %04x.%04x\n", + prom_name, qpdev->vendor, qpdev->device); } if (qfe_slot != -1) - printk(KERN_INFO "%s: Quattro HME slot %d (PCI/CheerIO) 10/100baseT Ethernet ", - dev->name, qfe_slot); + netdev_info(dev, + "Quattro HME slot %d (PCI/CheerIO) 10/100baseT Ethernet %pM\n", + qfe_slot, dev->dev_addr); else - printk(KERN_INFO "%s: HAPPY MEAL (PCI/CheerIO) 10/100BaseT Ethernet ", - dev->name); - - printk("%pM\n", dev->dev_addr); + netdev_info(dev, + "HAPPY MEAL (PCI/CheerIO) 10/100BaseT Ethernet %pM\n", + dev->dev_addr); return 0; -err_out_free_coherent: - dma_free_coherent(hp->dma_dev, PAGE_SIZE, - hp->happy_block, hp->hblock_dvma); - -err_out_iounmap: - iounmap(hp->gregs); - -err_out_free_res: - pci_release_regions(pdev); - err_out_clear_quattro: if (qp != NULL) qp->happy_meals[qfe_slot] = NULL; - free_netdev(dev); - err_out: return err; } -static void happy_meal_pci_remove(struct pci_dev *pdev) -{ - struct happy_meal *hp = pci_get_drvdata(pdev); - struct net_device *net_dev = hp->dev; - - unregister_netdev(net_dev); - - dma_free_coherent(hp->dma_dev, PAGE_SIZE, - hp->happy_block, hp->hblock_dvma); - iounmap(hp->gregs); - pci_release_regions(hp->happy_dev); - - free_netdev(net_dev); -} - static const struct pci_device_id happymeal_pci_ids[] = { { PCI_DEVICE(PCI_VENDOR_ID_SUN, PCI_DEVICE_ID_SUN_HAPPYMEAL) }, { } /* Terminating entry */ @@ -3206,7 +3062,6 @@ static struct pci_driver hme_pci_driver = { .name = "hme", .id_table = happymeal_pci_ids, .probe = happy_meal_pci_probe, - .remove = happy_meal_pci_remove, }; static int __init happy_meal_pci_init(void) diff --git a/drivers/net/ethernet/sun/sunqe.c b/drivers/net/ethernet/sun/sunqe.c index efe0d33f6024..6418fcc3139f 100644 --- a/drivers/net/ethernet/sun/sunqe.c +++ b/drivers/net/ethernet/sun/sunqe.c @@ -684,8 +684,8 @@ static void qe_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) struct sunqe *qep = netdev_priv(dev); struct platform_device *op; - strlcpy(info->driver, "sunqe", sizeof(info->driver)); - strlcpy(info->version, "3.0", sizeof(info->version)); + strscpy(info->driver, "sunqe", sizeof(info->driver)); + strscpy(info->version, "3.0", sizeof(info->version)); op = qep->op; regs = of_get_property(op->dev.of_node, "reg", NULL); diff --git a/drivers/net/ethernet/sun/sunvnet.c b/drivers/net/ethernet/sun/sunvnet.c index da8119625cf3..acda6cbd0238 100644 --- a/drivers/net/ethernet/sun/sunvnet.c +++ b/drivers/net/ethernet/sun/sunvnet.c @@ -60,8 +60,8 @@ static struct vio_version vnet_versions[] = { static void vnet_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { - strlcpy(info->driver, DRV_MODULE_NAME, sizeof(info->driver)); - strlcpy(info->version, DRV_MODULE_VERSION, sizeof(info->version)); + strscpy(info->driver, DRV_MODULE_NAME, sizeof(info->driver)); + strscpy(info->version, DRV_MODULE_VERSION, sizeof(info->version)); } static u32 vnet_get_msglevel(struct net_device *dev) @@ -467,8 +467,7 @@ static int vnet_port_probe(struct vio_dev *vdev, const struct vio_device_id *id) if (err) goto err_out_free_port; - netif_napi_add(port->vp->dev, &port->napi, sunvnet_poll_common, - NAPI_POLL_WEIGHT); + netif_napi_add(port->vp->dev, &port->napi, sunvnet_poll_common); INIT_HLIST_NODE(&port->hash); INIT_LIST_HEAD(&port->list); diff --git a/drivers/net/ethernet/sunplus/spl2sw_driver.c b/drivers/net/ethernet/sunplus/spl2sw_driver.c index 546206640492..9be585237277 100644 --- a/drivers/net/ethernet/sunplus/spl2sw_driver.c +++ b/drivers/net/ethernet/sunplus/spl2sw_driver.c @@ -62,7 +62,8 @@ static int spl2sw_ethernet_stop(struct net_device *ndev) return 0; } -static int spl2sw_ethernet_start_xmit(struct sk_buff *skb, struct net_device *ndev) +static netdev_tx_t spl2sw_ethernet_start_xmit(struct sk_buff *skb, + struct net_device *ndev) { struct spl2sw_mac *mac = netdev_priv(ndev); struct spl2sw_common *comm = mac->comm; @@ -248,8 +249,8 @@ static int spl2sw_nvmem_get_mac_address(struct device *dev, struct device_node * /* Check if mac address is valid */ if (!is_valid_ether_addr(mac)) { - kfree(mac); dev_info(dev, "Invalid mac address in nvmem (%pM)!\n", mac); + kfree(mac); return -EINVAL; } @@ -492,7 +493,7 @@ static int spl2sw_probe(struct platform_device *pdev) } /* Add and enable napi. */ - netif_napi_add(ndev, &comm->rx_napi, spl2sw_rx_poll, NAPI_POLL_WEIGHT); + netif_napi_add(ndev, &comm->rx_napi, spl2sw_rx_poll); napi_enable(&comm->rx_napi); netif_napi_add_tx(ndev, &comm->tx_napi, spl2sw_tx_poll); napi_enable(&comm->tx_napi); diff --git a/drivers/net/ethernet/synopsys/dwc-xlgmac-common.c b/drivers/net/ethernet/synopsys/dwc-xlgmac-common.c index 5c9b6c90942b..f8e133604146 100644 --- a/drivers/net/ethernet/synopsys/dwc-xlgmac-common.c +++ b/drivers/net/ethernet/synopsys/dwc-xlgmac-common.c @@ -54,8 +54,8 @@ static void xlgmac_default_config(struct xlgmac_pdata *pdata) pdata->phy_speed = SPEED_25000; pdata->sysclk_rate = XLGMAC_SYSCLOCK; - strlcpy(pdata->drv_name, XLGMAC_DRV_NAME, sizeof(pdata->drv_name)); - strlcpy(pdata->drv_ver, XLGMAC_DRV_VERSION, sizeof(pdata->drv_ver)); + strscpy(pdata->drv_name, XLGMAC_DRV_NAME, sizeof(pdata->drv_name)); + strscpy(pdata->drv_ver, XLGMAC_DRV_VERSION, sizeof(pdata->drv_ver)); } static void xlgmac_init_all_ops(struct xlgmac_pdata *pdata) diff --git a/drivers/net/ethernet/synopsys/dwc-xlgmac-ethtool.c b/drivers/net/ethernet/synopsys/dwc-xlgmac-ethtool.c index 49f8c6be9459..e794da727fe0 100644 --- a/drivers/net/ethernet/synopsys/dwc-xlgmac-ethtool.c +++ b/drivers/net/ethernet/synopsys/dwc-xlgmac-ethtool.c @@ -102,9 +102,9 @@ static void xlgmac_ethtool_get_drvinfo(struct net_device *netdev, u32 ver = pdata->hw_feat.version; u32 snpsver, devid, userver; - strlcpy(drvinfo->driver, pdata->drv_name, sizeof(drvinfo->driver)); - strlcpy(drvinfo->version, pdata->drv_ver, sizeof(drvinfo->version)); - strlcpy(drvinfo->bus_info, dev_name(pdata->dev), + strscpy(drvinfo->driver, pdata->drv_name, sizeof(drvinfo->driver)); + strscpy(drvinfo->version, pdata->drv_ver, sizeof(drvinfo->version)); + strscpy(drvinfo->bus_info, dev_name(pdata->dev), sizeof(drvinfo->bus_info)); /* S|SNPSVER: Synopsys-defined Version * D|DEVID: Indicates the Device family diff --git a/drivers/net/ethernet/synopsys/dwc-xlgmac-net.c b/drivers/net/ethernet/synopsys/dwc-xlgmac-net.c index e54ce73396ee..36b948820c1e 100644 --- a/drivers/net/ethernet/synopsys/dwc-xlgmac-net.c +++ b/drivers/net/ethernet/synopsys/dwc-xlgmac-net.c @@ -419,15 +419,14 @@ static void xlgmac_napi_enable(struct xlgmac_pdata *pdata, unsigned int add) for (i = 0; i < pdata->channel_count; i++, channel++) { if (add) netif_napi_add(pdata->netdev, &channel->napi, - xlgmac_one_poll, - NAPI_POLL_WEIGHT); + xlgmac_one_poll); napi_enable(&channel->napi); } } else { if (add) netif_napi_add(pdata->netdev, &pdata->napi, - xlgmac_all_poll, NAPI_POLL_WEIGHT); + xlgmac_all_poll); napi_enable(&pdata->napi); } diff --git a/drivers/net/ethernet/tehuti/tehuti.c b/drivers/net/ethernet/tehuti/tehuti.c index 985073eba3bd..ca409515ead5 100644 --- a/drivers/net/ethernet/tehuti/tehuti.c +++ b/drivers/net/ethernet/tehuti/tehuti.c @@ -1994,7 +1994,7 @@ bdx_probe(struct pci_dev *pdev, const struct pci_device_id *ent) priv->nic = nic; priv->msg_enable = BDX_DEF_MSG_ENABLE; - netif_napi_add(ndev, &priv->napi, bdx_poll, 64); + netif_napi_add(ndev, &priv->napi, bdx_poll); if ((readl(nic->regs + FPGA_VER) & 0xFFF) == 308) { DBG("HW statistics not supported\n"); @@ -2133,10 +2133,10 @@ bdx_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *drvinfo) { struct bdx_priv *priv = netdev_priv(netdev); - strlcpy(drvinfo->driver, BDX_DRV_NAME, sizeof(drvinfo->driver)); - strlcpy(drvinfo->version, BDX_DRV_VERSION, sizeof(drvinfo->version)); - strlcpy(drvinfo->fw_version, "N/A", sizeof(drvinfo->fw_version)); - strlcpy(drvinfo->bus_info, pci_name(priv->pdev), + strscpy(drvinfo->driver, BDX_DRV_NAME, sizeof(drvinfo->driver)); + strscpy(drvinfo->version, BDX_DRV_VERSION, sizeof(drvinfo->version)); + strscpy(drvinfo->fw_version, "N/A", sizeof(drvinfo->fw_version)); + strscpy(drvinfo->bus_info, pci_name(priv->pdev), sizeof(drvinfo->bus_info)); } diff --git a/drivers/net/ethernet/ti/Kconfig b/drivers/net/ethernet/ti/Kconfig index fb30bc5d56cb..fce06663e1e1 100644 --- a/drivers/net/ethernet/ti/Kconfig +++ b/drivers/net/ethernet/ti/Kconfig @@ -33,6 +33,7 @@ config TI_DAVINCI_MDIO tristate "TI DaVinci MDIO Support" depends on ARCH_DAVINCI || ARCH_OMAP2PLUS || ARCH_KEYSTONE || ARCH_K3 || COMPILE_TEST select PHYLIB + select MDIO_BITBANG help This driver supports TI's DaVinci MDIO module. diff --git a/drivers/net/ethernet/ti/am65-cpsw-ethtool.c b/drivers/net/ethernet/ti/am65-cpsw-ethtool.c index abc1e4276cf0..c51e2af91f69 100644 --- a/drivers/net/ethernet/ti/am65-cpsw-ethtool.c +++ b/drivers/net/ethernet/ti/am65-cpsw-ethtool.c @@ -402,9 +402,9 @@ static void am65_cpsw_get_drvinfo(struct net_device *ndev, { struct am65_cpsw_common *common = am65_ndev_to_common(ndev); - strlcpy(info->driver, dev_driver_string(common->dev), + strscpy(info->driver, dev_driver_string(common->dev), sizeof(info->driver)); - strlcpy(info->bus_info, dev_name(common->dev), sizeof(info->bus_info)); + strscpy(info->bus_info, dev_name(common->dev), sizeof(info->bus_info)); } static u32 am65_cpsw_get_msglevel(struct net_device *ndev) diff --git a/drivers/net/ethernet/ti/am65-cpsw-nuss.c b/drivers/net/ethernet/ti/am65-cpsw-nuss.c index f4a6b590a1e3..3cbe4ec46234 100644 --- a/drivers/net/ethernet/ti/am65-cpsw-nuss.c +++ b/drivers/net/ethernet/ti/am65-cpsw-nuss.c @@ -74,6 +74,9 @@ #define AM65_CPSW_PORTN_REG_TS_VLAN_LTYPE_REG 0x318 #define AM65_CPSW_PORTN_REG_TS_CTL_LTYPE2 0x31C +#define AM65_CPSW_SGMII_CONTROL_REG 0x010 +#define AM65_CPSW_SGMII_CONTROL_MR_AN_ENABLE BIT(0) + #define AM65_CPSW_CTL_VLAN_AWARE BIT(1) #define AM65_CPSW_CTL_P0_ENABLE BIT(2) #define AM65_CPSW_CTL_P0_TX_CRC_REMOVE BIT(13) @@ -360,8 +363,7 @@ static void am65_cpsw_init_host_port_emac(struct am65_cpsw_common *common); static void am65_cpsw_init_port_switch_ale(struct am65_cpsw_port *port); static void am65_cpsw_init_port_emac_ale(struct am65_cpsw_port *port); -static int am65_cpsw_nuss_common_open(struct am65_cpsw_common *common, - netdev_features_t features) +static int am65_cpsw_nuss_common_open(struct am65_cpsw_common *common) { struct am65_cpsw_host *host_p = am65_common_get_host(common); int port_idx, i, ret; @@ -574,7 +576,7 @@ static int am65_cpsw_nuss_ndo_slave_open(struct net_device *ndev) for (i = 0; i < common->tx_ch_num; i++) netdev_tx_reset_queue(netdev_get_tx_queue(ndev, i)); - ret = am65_cpsw_nuss_common_open(common, ndev->features); + ret = am65_cpsw_nuss_common_open(common); if (ret) return ret; @@ -590,11 +592,6 @@ static int am65_cpsw_nuss_ndo_slave_open(struct net_device *ndev) /* mac_sl should be configured via phy-link interface */ am65_cpsw_sl_ctl_reset(port); - ret = phy_set_mode_ext(port->slave.ifphy, PHY_MODE_ETHERNET, - port->slave.phy_if); - if (ret) - goto error_cleanup; - ret = phylink_of_phy_connect(port->slave.phylink, port->slave.phy_node, 0); if (ret) goto error_cleanup; @@ -1409,7 +1406,14 @@ static const struct net_device_ops am65_cpsw_nuss_netdev_ops = { static void am65_cpsw_nuss_mac_config(struct phylink_config *config, unsigned int mode, const struct phylink_link_state *state) { - /* Currently not used */ + struct am65_cpsw_slave_data *slave = container_of(config, struct am65_cpsw_slave_data, + phylink_config); + struct am65_cpsw_port *port = container_of(slave, struct am65_cpsw_port, slave); + struct am65_cpsw_common *common = port->common; + + if (common->pdata.extra_modes & BIT(state->interface)) + writel(AM65_CPSW_SGMII_CONTROL_MR_AN_ENABLE, + port->sgmii_base + AM65_CPSW_SGMII_CONTROL_REG); } static void am65_cpsw_nuss_mac_link_down(struct phylink_config *config, unsigned int mode, @@ -1847,6 +1851,8 @@ static int am65_cpsw_nuss_init_slave_ports(struct am65_cpsw_common *common) port->common = common; port->port_base = common->cpsw_base + AM65_CPSW_NU_PORTS_BASE + AM65_CPSW_NU_PORTS_OFFSET * (port_id); + if (common->pdata.extra_modes) + port->sgmii_base = common->ss_base + AM65_CPSW_SGMII_BASE * (port_id); port->stat_base = common->cpsw_base + AM65_CPSW_NU_STATS_BASE + (AM65_CPSW_NU_STATS_PORT_OFFSET * port_id); port->name = of_get_property(port_np, "label", NULL); @@ -1886,6 +1892,10 @@ static int am65_cpsw_nuss_init_slave_ports(struct am65_cpsw_common *common) goto of_node_put; } + ret = phy_set_mode_ext(port->slave.ifphy, PHY_MODE_ETHERNET, port->slave.phy_if); + if (ret) + goto of_node_put; + ret = of_get_mac_address(port_np, port->slave.mac_addr); if (ret) { am65_cpsw_am654_get_efuse_macid(port_np, @@ -1981,7 +1991,18 @@ am65_cpsw_nuss_init_port_ndev(struct am65_cpsw_common *common, u32 port_idx) port->slave.phylink_config.type = PHYLINK_NETDEV; port->slave.phylink_config.mac_capabilities = MAC_SYM_PAUSE | MAC_10 | MAC_100 | MAC_1000FD; - phy_interface_set_rgmii(port->slave.phylink_config.supported_interfaces); + if (phy_interface_mode_is_rgmii(port->slave.phy_if)) { + phy_interface_set_rgmii(port->slave.phylink_config.supported_interfaces); + } else if (port->slave.phy_if == PHY_INTERFACE_MODE_RMII) { + __set_bit(PHY_INTERFACE_MODE_RMII, + port->slave.phylink_config.supported_interfaces); + } else if (common->pdata.extra_modes & BIT(port->slave.phy_if)) { + __set_bit(PHY_INTERFACE_MODE_QSGMII, + port->slave.phylink_config.supported_interfaces); + } else { + dev_err(dev, "selected phy-mode is not supported\n"); + return -EOPNOTSUPP; + } phylink = phylink_create(&port->slave.phylink_config, of_node_to_fwnode(port->slave.phy_node), @@ -2023,7 +2044,7 @@ static int am65_cpsw_nuss_init_ndevs(struct am65_cpsw_common *common) } netif_napi_add(common->dma_ndev, &common->napi_rx, - am65_cpsw_nuss_rx_poll, NAPI_POLL_WEIGHT); + am65_cpsw_nuss_rx_poll); return ret; } @@ -2611,10 +2632,18 @@ static const struct am65_cpsw_pdata am64x_cpswxg_pdata = { .fdqring_mode = K3_RINGACC_RING_MODE_RING, }; +static const struct am65_cpsw_pdata j7200_cpswxg_pdata = { + .quirks = 0, + .ale_dev_id = "am64-cpswxg", + .fdqring_mode = K3_RINGACC_RING_MODE_RING, + .extra_modes = BIT(PHY_INTERFACE_MODE_QSGMII), +}; + static const struct of_device_id am65_cpsw_nuss_of_mtable[] = { { .compatible = "ti,am654-cpsw-nuss", .data = &am65x_sr1_0}, { .compatible = "ti,j721e-cpsw-nuss", .data = &j721e_pdata}, { .compatible = "ti,am642-cpsw-nuss", .data = &am64x_cpswxg_pdata}, + { .compatible = "ti,j7200-cpswxg-nuss", .data = &j7200_cpswxg_pdata}, { /* sentinel */ }, }; MODULE_DEVICE_TABLE(of, am65_cpsw_nuss_of_mtable); diff --git a/drivers/net/ethernet/ti/am65-cpsw-nuss.h b/drivers/net/ethernet/ti/am65-cpsw-nuss.h index ac945631bf2f..2c9850fdfcb6 100644 --- a/drivers/net/ethernet/ti/am65-cpsw-nuss.h +++ b/drivers/net/ethernet/ti/am65-cpsw-nuss.h @@ -46,6 +46,7 @@ struct am65_cpsw_port { const char *name; u32 port_id; void __iomem *port_base; + void __iomem *sgmii_base; void __iomem *stat_base; void __iomem *fetch_ram_base; bool disabled; @@ -88,6 +89,7 @@ struct am65_cpsw_rx_chn { struct am65_cpsw_pdata { u32 quirks; + u64 extra_modes; enum k3_ring_mode fdqring_mode; const char *ale_dev_id; }; diff --git a/drivers/net/ethernet/ti/am65-cpts.c b/drivers/net/ethernet/ti/am65-cpts.c index c30a6e510aa3..e2f0fb286143 100644 --- a/drivers/net/ethernet/ti/am65-cpts.c +++ b/drivers/net/ethernet/ti/am65-cpts.c @@ -943,9 +943,7 @@ struct am65_cpts *am65_cpts_create(struct device *dev, void __iomem *regs, cpts->irq = of_irq_get_byname(node, "cpts"); if (cpts->irq <= 0) { ret = cpts->irq ?: -ENXIO; - if (ret != -EPROBE_DEFER) - dev_err(dev, "Failed to get IRQ number (err = %d)\n", - ret); + dev_err_probe(dev, ret, "Failed to get IRQ number\n"); return ERR_PTR(ret); } @@ -965,8 +963,7 @@ struct am65_cpts *am65_cpts_create(struct device *dev, void __iomem *regs, cpts->refclk = devm_get_clk_from_child(dev, node, "cpts"); if (IS_ERR(cpts->refclk)) { ret = PTR_ERR(cpts->refclk); - if (ret != -EPROBE_DEFER) - dev_err(dev, "Failed to get refclk %d\n", ret); + dev_err_probe(dev, ret, "Failed to get refclk\n"); return ERR_PTR(ret); } diff --git a/drivers/net/ethernet/ti/cpmac.c b/drivers/net/ethernet/ti/cpmac.c index bef5e68dac31..80eeeb463c4f 100644 --- a/drivers/net/ethernet/ti/cpmac.c +++ b/drivers/net/ethernet/ti/cpmac.c @@ -851,8 +851,8 @@ static int cpmac_set_ringparam(struct net_device *dev, static void cpmac_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { - strlcpy(info->driver, "cpmac", sizeof(info->driver)); - strlcpy(info->version, CPMAC_VERSION, sizeof(info->version)); + strscpy(info->driver, "cpmac", sizeof(info->driver)); + strscpy(info->version, CPMAC_VERSION, sizeof(info->version)); snprintf(info->bus_info, sizeof(info->bus_info), "%s", "cpmac"); } @@ -1109,7 +1109,7 @@ static int cpmac_probe(struct platform_device *pdev) dev->netdev_ops = &cpmac_netdev_ops; dev->ethtool_ops = &cpmac_ethtool_ops; - netif_napi_add(dev, &priv->napi, cpmac_poll, 64); + netif_napi_add(dev, &priv->napi, cpmac_poll); spin_lock_init(&priv->lock); spin_lock_init(&priv->rx_lock); @@ -1169,7 +1169,7 @@ static struct platform_driver cpmac_driver = { .remove = cpmac_remove, }; -int cpmac_init(void) +int __init cpmac_init(void) { u32 mask; int i, res; @@ -1239,7 +1239,7 @@ fail_alloc: return res; } -void cpmac_exit(void) +void __exit cpmac_exit(void) { platform_driver_unregister(&cpmac_driver); mdiobus_unregister(cpmac_mii); diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index ed66c4d4d830..709ca6dd6ecb 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -1172,9 +1172,9 @@ static void cpsw_get_drvinfo(struct net_device *ndev, struct cpsw_common *cpsw = ndev_to_cpsw(ndev); struct platform_device *pdev = to_platform_device(cpsw->dev); - strlcpy(info->driver, "cpsw", sizeof(info->driver)); - strlcpy(info->version, "1.0", sizeof(info->version)); - strlcpy(info->bus_info, pdev->name, sizeof(info->bus_info)); + strscpy(info->driver, "cpsw", sizeof(info->driver)); + strscpy(info->version, "1.0", sizeof(info->version)); + strscpy(info->bus_info, pdev->name, sizeof(info->bus_info)); } static int cpsw_set_pauseparam(struct net_device *ndev, @@ -1319,8 +1319,7 @@ static int cpsw_probe_dt(struct cpsw_platform_data *data, */ ret = of_phy_register_fixed_link(slave_node); if (ret) { - if (ret != -EPROBE_DEFER) - dev_err(&pdev->dev, "failed to register fixed-link phy: %d\n", ret); + dev_err_probe(&pdev->dev, ret, "failed to register fixed-link phy\n"); goto err_node_put; } slave_data->phy_node = of_node_get(slave_node); @@ -1638,8 +1637,7 @@ static int cpsw_probe(struct platform_device *pdev) ndev->netdev_ops = &cpsw_netdev_ops; ndev->ethtool_ops = &cpsw_ethtool_ops; netif_napi_add(ndev, &cpsw->napi_rx, - cpsw->quirk_irq ? cpsw_rx_poll : cpsw_rx_mq_poll, - NAPI_POLL_WEIGHT); + cpsw->quirk_irq ? cpsw_rx_poll : cpsw_rx_mq_poll); netif_napi_add_tx(ndev, &cpsw->napi_tx, cpsw->quirk_irq ? cpsw_tx_poll : cpsw_tx_mq_poll); diff --git a/drivers/net/ethernet/ti/cpsw_new.c b/drivers/net/ethernet/ti/cpsw_new.c index 353e58b22c51..83596ec0c7cb 100644 --- a/drivers/net/ethernet/ti/cpsw_new.c +++ b/drivers/net/ethernet/ti/cpsw_new.c @@ -1146,9 +1146,9 @@ static void cpsw_get_drvinfo(struct net_device *ndev, struct platform_device *pdev; pdev = to_platform_device(cpsw->dev); - strlcpy(info->driver, "cpsw-switch", sizeof(info->driver)); - strlcpy(info->version, "2.0", sizeof(info->version)); - strlcpy(info->bus_info, pdev->name, sizeof(info->bus_info)); + strscpy(info->driver, "cpsw-switch", sizeof(info->driver)); + strscpy(info->version, "2.0", sizeof(info->version)); + strscpy(info->bus_info, pdev->name, sizeof(info->bus_info)); } static int cpsw_set_pauseparam(struct net_device *ndev, @@ -1288,9 +1288,8 @@ static int cpsw_probe_dt(struct cpsw_common *cpsw) if (of_phy_is_fixed_link(port_np)) { ret = of_phy_register_fixed_link(port_np); if (ret) { - if (ret != -EPROBE_DEFER) - dev_err(dev, "%pOF failed to register fixed-link phy: %d\n", - port_np, ret); + dev_err_probe(dev, ret, "%pOF failed to register fixed-link phy\n", + port_np); goto err_node_put; } slave_data->phy_node = of_node_get(port_np); @@ -1417,9 +1416,7 @@ static int cpsw_create_ports(struct cpsw_common *cpsw) * accordingly. */ netif_napi_add(ndev, &cpsw->napi_rx, - cpsw->quirk_irq ? - cpsw_rx_poll : cpsw_rx_mq_poll, - NAPI_POLL_WEIGHT); + cpsw->quirk_irq ? cpsw_rx_poll : cpsw_rx_mq_poll); netif_napi_add_tx(ndev, &cpsw->napi_tx, cpsw->quirk_irq ? cpsw_tx_poll : cpsw_tx_mq_poll); diff --git a/drivers/net/ethernet/ti/davinci_emac.c b/drivers/net/ethernet/ti/davinci_emac.c index 2a3e4e842fa5..2eb9d5a32588 100644 --- a/drivers/net/ethernet/ti/davinci_emac.c +++ b/drivers/net/ethernet/ti/davinci_emac.c @@ -374,8 +374,8 @@ static char *emac_rxhost_errcodes[16] = { static void emac_get_drvinfo(struct net_device *ndev, struct ethtool_drvinfo *info) { - strlcpy(info->driver, emac_version_string, sizeof(info->driver)); - strlcpy(info->version, EMAC_MODULE_VERSION, sizeof(info->version)); + strscpy(info->driver, emac_version_string, sizeof(info->driver)); + strscpy(info->version, EMAC_MODULE_VERSION, sizeof(info->version)); } /** @@ -949,7 +949,7 @@ static void emac_tx_handler(void *token, int len, int status) * * Returns success(NETDEV_TX_OK) or error code (typically out of desc's) */ -static int emac_dev_xmit(struct sk_buff *skb, struct net_device *ndev) +static netdev_tx_t emac_dev_xmit(struct sk_buff *skb, struct net_device *ndev) { struct device *emac_dev = &ndev->dev; int ret_code; @@ -1948,7 +1948,7 @@ static int davinci_emac_probe(struct platform_device *pdev) ndev->netdev_ops = &emac_netdev_ops; ndev->ethtool_ops = ðtool_ops; - netif_napi_add(ndev, &priv->napi, emac_poll, NAPI_POLL_WEIGHT); + netif_napi_add(ndev, &priv->napi, emac_poll); pm_runtime_enable(&pdev->dev); rc = pm_runtime_resume_and_get(&pdev->dev); diff --git a/drivers/net/ethernet/ti/davinci_mdio.c b/drivers/net/ethernet/ti/davinci_mdio.c index ea3772618043..946b9753ccfb 100644 --- a/drivers/net/ethernet/ti/davinci_mdio.c +++ b/drivers/net/ethernet/ti/davinci_mdio.c @@ -26,6 +26,8 @@ #include <linux/of_device.h> #include <linux/of_mdio.h> #include <linux/pinctrl/consumer.h> +#include <linux/mdio-bitbang.h> +#include <linux/sys_soc.h> /* * This timeout definition is a worst-case ultra defensive measure against @@ -41,6 +43,7 @@ struct davinci_mdio_of_param { int autosuspend_delay_ms; + bool manual_mode; }; struct davinci_mdio_regs { @@ -49,6 +52,15 @@ struct davinci_mdio_regs { #define CONTROL_IDLE BIT(31) #define CONTROL_ENABLE BIT(30) #define CONTROL_MAX_DIV (0xffff) +#define CONTROL_CLKDIV GENMASK(15, 0) + +#define MDIO_MAN_MDCLK_O BIT(2) +#define MDIO_MAN_OE BIT(1) +#define MDIO_MAN_PIN BIT(0) +#define MDIO_MANUALMODE BIT(31) + +#define MDIO_PIN 0 + u32 alive; u32 link; @@ -59,7 +71,9 @@ struct davinci_mdio_regs { u32 userintmasked; u32 userintmaskset; u32 userintmaskclr; - u32 __reserved_1[20]; + u32 manualif; + u32 poll; + u32 __reserved_1[18]; struct { u32 access; @@ -79,6 +93,7 @@ static const struct mdio_platform_data default_pdata = { struct davinci_mdio_data { struct mdio_platform_data pdata; + struct mdiobb_ctrl bb_ctrl; struct davinci_mdio_regs __iomem *regs; struct clk *clk; struct device *dev; @@ -90,6 +105,7 @@ struct davinci_mdio_data { */ bool skip_scan; u32 clk_div; + bool manual_mode; }; static void davinci_mdio_init_clk(struct davinci_mdio_data *data) @@ -128,9 +144,122 @@ static void davinci_mdio_enable(struct davinci_mdio_data *data) writel(data->clk_div | CONTROL_ENABLE, &data->regs->control); } -static int davinci_mdio_reset(struct mii_bus *bus) +static void davinci_mdio_disable(struct davinci_mdio_data *data) +{ + u32 reg; + + /* Disable MDIO state machine */ + reg = readl(&data->regs->control); + + reg &= ~CONTROL_CLKDIV; + reg |= data->clk_div; + + reg &= ~CONTROL_ENABLE; + writel(reg, &data->regs->control); +} + +static void davinci_mdio_enable_manual_mode(struct davinci_mdio_data *data) +{ + u32 reg; + /* set manual mode */ + reg = readl(&data->regs->poll); + reg |= MDIO_MANUALMODE; + writel(reg, &data->regs->poll); +} + +static void davinci_set_mdc(struct mdiobb_ctrl *ctrl, int level) +{ + struct davinci_mdio_data *data; + u32 reg; + + data = container_of(ctrl, struct davinci_mdio_data, bb_ctrl); + reg = readl(&data->regs->manualif); + + if (level) + reg |= MDIO_MAN_MDCLK_O; + else + reg &= ~MDIO_MAN_MDCLK_O; + + writel(reg, &data->regs->manualif); +} + +static void davinci_set_mdio_dir(struct mdiobb_ctrl *ctrl, int output) +{ + struct davinci_mdio_data *data; + u32 reg; + + data = container_of(ctrl, struct davinci_mdio_data, bb_ctrl); + reg = readl(&data->regs->manualif); + + if (output) + reg |= MDIO_MAN_OE; + else + reg &= ~MDIO_MAN_OE; + + writel(reg, &data->regs->manualif); +} + +static void davinci_set_mdio_data(struct mdiobb_ctrl *ctrl, int value) +{ + struct davinci_mdio_data *data; + u32 reg; + + data = container_of(ctrl, struct davinci_mdio_data, bb_ctrl); + reg = readl(&data->regs->manualif); + + if (value) + reg |= MDIO_MAN_PIN; + else + reg &= ~MDIO_MAN_PIN; + + writel(reg, &data->regs->manualif); +} + +static int davinci_get_mdio_data(struct mdiobb_ctrl *ctrl) +{ + struct davinci_mdio_data *data; + unsigned long reg; + + data = container_of(ctrl, struct davinci_mdio_data, bb_ctrl); + reg = readl(&data->regs->manualif); + return test_bit(MDIO_PIN, ®); +} + +static int davinci_mdiobb_read(struct mii_bus *bus, int phy, int reg) +{ + int ret; + + ret = pm_runtime_resume_and_get(bus->parent); + if (ret < 0) + return ret; + + ret = mdiobb_read(bus, phy, reg); + + pm_runtime_mark_last_busy(bus->parent); + pm_runtime_put_autosuspend(bus->parent); + + return ret; +} + +static int davinci_mdiobb_write(struct mii_bus *bus, int phy, int reg, + u16 val) +{ + int ret; + + ret = pm_runtime_resume_and_get(bus->parent); + if (ret < 0) + return ret; + + ret = mdiobb_write(bus, phy, reg, val); + + pm_runtime_mark_last_busy(bus->parent); + pm_runtime_put_autosuspend(bus->parent); + + return ret; +} + +static int davinci_mdio_common_reset(struct davinci_mdio_data *data) { - struct davinci_mdio_data *data = bus->priv; u32 phy_mask, ver; int ret; @@ -138,6 +267,11 @@ static int davinci_mdio_reset(struct mii_bus *bus) if (ret < 0) return ret; + if (data->manual_mode) { + davinci_mdio_disable(data); + davinci_mdio_enable_manual_mode(data); + } + /* wait for scan logic to settle */ msleep(PHY_MAX_ADDR * data->access_time); @@ -171,6 +305,23 @@ done: return 0; } +static int davinci_mdio_reset(struct mii_bus *bus) +{ + struct davinci_mdio_data *data = bus->priv; + + return davinci_mdio_common_reset(data); +} + +static int davinci_mdiobb_reset(struct mii_bus *bus) +{ + struct mdiobb_ctrl *ctrl = bus->priv; + struct davinci_mdio_data *data; + + data = container_of(ctrl, struct davinci_mdio_data, bb_ctrl); + + return davinci_mdio_common_reset(data); +} + /* wait until hardware is ready for another user access */ static inline int wait_for_user_access(struct davinci_mdio_data *data) { @@ -318,6 +469,28 @@ static int davinci_mdio_probe_dt(struct mdio_platform_data *data, return 0; } +struct k3_mdio_soc_data { + bool manual_mode; +}; + +static const struct k3_mdio_soc_data am65_mdio_soc_data = { + .manual_mode = true, +}; + +static const struct soc_device_attribute k3_mdio_socinfo[] = { + { .family = "AM62X", .revision = "SR1.0", .data = &am65_mdio_soc_data }, + { .family = "AM64X", .revision = "SR1.0", .data = &am65_mdio_soc_data }, + { .family = "AM64X", .revision = "SR2.0", .data = &am65_mdio_soc_data }, + { .family = "AM65X", .revision = "SR1.0", .data = &am65_mdio_soc_data }, + { .family = "AM65X", .revision = "SR2.0", .data = &am65_mdio_soc_data }, + { .family = "J7200", .revision = "SR1.0", .data = &am65_mdio_soc_data }, + { .family = "J7200", .revision = "SR2.0", .data = &am65_mdio_soc_data }, + { .family = "J721E", .revision = "SR1.0", .data = &am65_mdio_soc_data }, + { .family = "J721E", .revision = "SR2.0", .data = &am65_mdio_soc_data }, + { .family = "J721S2", .revision = "SR1.0", .data = &am65_mdio_soc_data}, + { /* sentinel */ }, +}; + #if IS_ENABLED(CONFIG_OF) static const struct davinci_mdio_of_param of_cpsw_mdio_data = { .autosuspend_delay_ms = 100, @@ -331,6 +504,14 @@ static const struct of_device_id davinci_mdio_of_mtable[] = { MODULE_DEVICE_TABLE(of, davinci_mdio_of_mtable); #endif +static const struct mdiobb_ops davinci_mdiobb_ops = { + .owner = THIS_MODULE, + .set_mdc = davinci_set_mdc, + .set_mdio_dir = davinci_set_mdio_dir, + .set_mdio_data = davinci_set_mdio_data, + .get_mdio_data = davinci_get_mdio_data, +}; + static int davinci_mdio_probe(struct platform_device *pdev) { struct mdio_platform_data *pdata = dev_get_platdata(&pdev->dev); @@ -345,7 +526,26 @@ static int davinci_mdio_probe(struct platform_device *pdev) if (!data) return -ENOMEM; - data->bus = devm_mdiobus_alloc(dev); + data->manual_mode = false; + data->bb_ctrl.ops = &davinci_mdiobb_ops; + + if (IS_ENABLED(CONFIG_OF) && dev->of_node) { + const struct soc_device_attribute *soc_match_data; + + soc_match_data = soc_device_match(k3_mdio_socinfo); + if (soc_match_data && soc_match_data->data) { + const struct k3_mdio_soc_data *socdata = + soc_match_data->data; + + data->manual_mode = socdata->manual_mode; + } + } + + if (data->manual_mode) + data->bus = alloc_mdio_bitbang(&data->bb_ctrl); + else + data->bus = devm_mdiobus_alloc(dev); + if (!data->bus) { dev_err(dev, "failed to alloc mii bus\n"); return -ENOMEM; @@ -371,11 +571,20 @@ static int davinci_mdio_probe(struct platform_device *pdev) } data->bus->name = dev_name(dev); - data->bus->read = davinci_mdio_read; - data->bus->write = davinci_mdio_write; - data->bus->reset = davinci_mdio_reset; + + if (data->manual_mode) { + data->bus->read = davinci_mdiobb_read; + data->bus->write = davinci_mdiobb_write; + data->bus->reset = davinci_mdiobb_reset; + + dev_info(dev, "Configuring MDIO in manual mode\n"); + } else { + data->bus->read = davinci_mdio_read; + data->bus->write = davinci_mdio_write; + data->bus->reset = davinci_mdio_reset; + data->bus->priv = data; + } data->bus->parent = dev; - data->bus->priv = data; data->clk = devm_clk_get(dev, "fck"); if (IS_ERR(data->clk)) { @@ -433,9 +642,13 @@ static int davinci_mdio_remove(struct platform_device *pdev) { struct davinci_mdio_data *data = platform_get_drvdata(pdev); - if (data->bus) + if (data->bus) { mdiobus_unregister(data->bus); + if (data->manual_mode) + free_mdio_bitbang(data->bus); + } + pm_runtime_dont_use_autosuspend(&pdev->dev); pm_runtime_disable(&pdev->dev); @@ -452,7 +665,9 @@ static int davinci_mdio_runtime_suspend(struct device *dev) ctrl = readl(&data->regs->control); ctrl &= ~CONTROL_ENABLE; writel(ctrl, &data->regs->control); - wait_for_idle(data); + + if (!data->manual_mode) + wait_for_idle(data); return 0; } @@ -461,7 +676,12 @@ static int davinci_mdio_runtime_resume(struct device *dev) { struct davinci_mdio_data *data = dev_get_drvdata(dev); - davinci_mdio_enable(data); + if (data->manual_mode) { + davinci_mdio_disable(data); + davinci_mdio_enable_manual_mode(data); + } else { + davinci_mdio_enable(data); + } return 0; } #endif diff --git a/drivers/net/ethernet/ti/netcp_core.c b/drivers/net/ethernet/ti/netcp_core.c index b15d44261e76..aba70bef4894 100644 --- a/drivers/net/ethernet/ti/netcp_core.c +++ b/drivers/net/ethernet/ti/netcp_core.c @@ -2095,7 +2095,7 @@ static int netcp_create_interface(struct netcp_device *netcp_device, } /* NAPI register */ - netif_napi_add(ndev, &netcp->rx_napi, netcp_rx_poll, NAPI_POLL_WEIGHT); + netif_napi_add(ndev, &netcp->rx_napi, netcp_rx_poll); netif_napi_add_tx(ndev, &netcp->tx_napi, netcp_tx_poll); /* Register the network device */ diff --git a/drivers/net/ethernet/ti/tlan.c b/drivers/net/ethernet/ti/tlan.c index 741c42c6a417..b3da76efa8f5 100644 --- a/drivers/net/ethernet/ti/tlan.c +++ b/drivers/net/ethernet/ti/tlan.c @@ -762,12 +762,12 @@ static void tlan_get_drvinfo(struct net_device *dev, { struct tlan_priv *priv = netdev_priv(dev); - strlcpy(info->driver, KBUILD_MODNAME, sizeof(info->driver)); + strscpy(info->driver, KBUILD_MODNAME, sizeof(info->driver)); if (priv->pci_dev) - strlcpy(info->bus_info, pci_name(priv->pci_dev), + strscpy(info->bus_info, pci_name(priv->pci_dev), sizeof(info->bus_info)); else - strlcpy(info->bus_info, "EISA", sizeof(info->bus_info)); + strscpy(info->bus_info, "EISA", sizeof(info->bus_info)); } static int tlan_get_eeprom_len(struct net_device *dev) diff --git a/drivers/net/ethernet/toshiba/ps3_gelic_net.c b/drivers/net/ethernet/toshiba/ps3_gelic_net.c index 3dbfb1b20649..cf8de8a7a8a1 100644 --- a/drivers/net/ethernet/toshiba/ps3_gelic_net.c +++ b/drivers/net/ethernet/toshiba/ps3_gelic_net.c @@ -1187,8 +1187,8 @@ int gelic_net_open(struct net_device *netdev) void gelic_net_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *info) { - strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); - strlcpy(info->version, DRV_VERSION, sizeof(info->version)); + strscpy(info->driver, DRV_NAME, sizeof(info->driver)); + strscpy(info->version, DRV_VERSION, sizeof(info->version)); } static int gelic_ether_get_link_ksettings(struct net_device *netdev, @@ -1441,7 +1441,7 @@ static void gelic_ether_setup_netdev_ops(struct net_device *netdev, { netdev->watchdog_timeo = GELIC_NET_WATCHDOG_TIMEOUT; /* NAPI */ - netif_napi_add(netdev, napi, gelic_net_poll, NAPI_POLL_WEIGHT); + netif_napi_add(netdev, napi, gelic_net_poll); netdev->ethtool_ops = &gelic_ether_ethtool_ops; netdev->netdev_ops = &gelic_netdevice_ops; } diff --git a/drivers/net/ethernet/toshiba/spider_net.c b/drivers/net/ethernet/toshiba/spider_net.c index bc4914c758ad..50d7eacfec58 100644 --- a/drivers/net/ethernet/toshiba/spider_net.c +++ b/drivers/net/ethernet/toshiba/spider_net.c @@ -2270,8 +2270,7 @@ spider_net_setup_netdev(struct spider_net_card *card) card->aneg_count = 0; timer_setup(&card->aneg_timer, spider_net_link_phy, 0); - netif_napi_add(netdev, &card->napi, - spider_net_poll, NAPI_POLL_WEIGHT); + netif_napi_add(netdev, &card->napi, spider_net_poll); spider_net_setup_netdev_ops(netdev); diff --git a/drivers/net/ethernet/toshiba/spider_net_ethtool.c b/drivers/net/ethernet/toshiba/spider_net_ethtool.c index 93110dba0bfa..fef9fd127b5e 100644 --- a/drivers/net/ethernet/toshiba/spider_net_ethtool.c +++ b/drivers/net/ethernet/toshiba/spider_net_ethtool.c @@ -63,12 +63,12 @@ spider_net_ethtool_get_drvinfo(struct net_device *netdev, card = netdev_priv(netdev); /* clear and fill out info */ - strlcpy(drvinfo->driver, spider_net_driver_name, + strscpy(drvinfo->driver, spider_net_driver_name, sizeof(drvinfo->driver)); - strlcpy(drvinfo->version, VERSION, sizeof(drvinfo->version)); - strlcpy(drvinfo->fw_version, "no information", + strscpy(drvinfo->version, VERSION, sizeof(drvinfo->version)); + strscpy(drvinfo->fw_version, "no information", sizeof(drvinfo->fw_version)); - strlcpy(drvinfo->bus_info, pci_name(card->pdev), + strscpy(drvinfo->bus_info, pci_name(card->pdev), sizeof(drvinfo->bus_info)); } diff --git a/drivers/net/ethernet/toshiba/tc35815.c b/drivers/net/ethernet/toshiba/tc35815.c index 47aab9c132c8..b50be67b398b 100644 --- a/drivers/net/ethernet/toshiba/tc35815.c +++ b/drivers/net/ethernet/toshiba/tc35815.c @@ -1956,9 +1956,9 @@ static void tc35815_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo * { struct tc35815_local *lp = netdev_priv(dev); - strlcpy(info->driver, MODNAME, sizeof(info->driver)); - strlcpy(info->version, DRV_VERSION, sizeof(info->version)); - strlcpy(info->bus_info, pci_name(lp->pci_dev), sizeof(info->bus_info)); + strscpy(info->driver, MODNAME, sizeof(info->driver)); + strscpy(info->version, DRV_VERSION, sizeof(info->version)); + strscpy(info->bus_info, pci_name(lp->pci_dev), sizeof(info->bus_info)); } static u32 tc35815_get_msglevel(struct net_device *dev) diff --git a/drivers/net/ethernet/tundra/tsi108_eth.c b/drivers/net/ethernet/tundra/tsi108_eth.c index 5251fc324221..2cd2afc3fff0 100644 --- a/drivers/net/ethernet/tundra/tsi108_eth.c +++ b/drivers/net/ethernet/tundra/tsi108_eth.c @@ -59,9 +59,6 @@ /* Check the phy status every half a second. */ #define CHECK_PHY_INTERVAL (HZ/2) -static int tsi108_init_one(struct platform_device *pdev); -static int tsi108_ether_remove(struct platform_device *pdev); - struct tsi108_prv_data { void __iomem *regs; /* Base of normal regs */ void __iomem *phyregs; /* Base of register bank used for PHY access */ @@ -144,16 +141,6 @@ struct tsi108_prv_data { struct platform_device *pdev; }; -/* Structure for a device driver */ - -static struct platform_driver tsi_eth_driver = { - .probe = tsi108_init_one, - .remove = tsi108_ether_remove, - .driver = { - .name = "tsi-ethernet", - }, -}; - static void tsi108_timed_checker(struct timer_list *t); #ifdef DEBUG @@ -1598,7 +1585,7 @@ tsi108_init_one(struct platform_device *pdev) data->phy_type = einfo->phy_type; data->irq_num = einfo->irq_num; data->id = pdev->id; - netif_napi_add(dev, &data->napi, tsi108_poll, 64); + netif_napi_add(dev, &data->napi, tsi108_poll); dev->netdev_ops = &tsi108_netdev_ops; dev->ethtool_ops = &tsi108_ethtool_ops; @@ -1683,6 +1670,16 @@ static int tsi108_ether_remove(struct platform_device *pdev) return 0; } + +/* Structure for a device driver */ + +static struct platform_driver tsi_eth_driver = { + .probe = tsi108_init_one, + .remove = tsi108_ether_remove, + .driver = { + .name = "tsi-ethernet", + }, +}; module_platform_driver(tsi_eth_driver); MODULE_AUTHOR("Tundra Semiconductor Corporation"); diff --git a/drivers/net/ethernet/vertexcom/mse102x.c b/drivers/net/ethernet/vertexcom/mse102x.c index eb39a45de012..aeed2a093e34 100644 --- a/drivers/net/ethernet/vertexcom/mse102x.c +++ b/drivers/net/ethernet/vertexcom/mse102x.c @@ -750,6 +750,13 @@ static const struct of_device_id mse102x_match_table[] = { }; MODULE_DEVICE_TABLE(of, mse102x_match_table); +static const struct spi_device_id mse102x_ids[] = { + { "mse1021" }, + { "mse1022" }, + { } +}; +MODULE_DEVICE_TABLE(spi, mse102x_ids); + static struct spi_driver mse102x_driver = { .driver = { .name = DRV_NAME, @@ -758,10 +765,11 @@ static struct spi_driver mse102x_driver = { }, .probe = mse102x_probe_spi, .remove = mse102x_remove_spi, + .id_table = mse102x_ids, }; module_spi_driver(mse102x_driver); MODULE_DESCRIPTION("MSE102x Network driver"); -MODULE_AUTHOR("Stefan Wahren <stefan.wahren@in-tech.com>"); +MODULE_AUTHOR("Stefan Wahren <stefan.wahren@chargebyte.com>"); MODULE_LICENSE("GPL"); MODULE_ALIAS("spi:" DRV_NAME); diff --git a/drivers/net/ethernet/via/via-rhine.c b/drivers/net/ethernet/via/via-rhine.c index 509c5e9b29df..0fb15a17b547 100644 --- a/drivers/net/ethernet/via/via-rhine.c +++ b/drivers/net/ethernet/via/via-rhine.c @@ -965,7 +965,7 @@ static int rhine_init_one_common(struct device *hwdev, u32 quirks, dev->ethtool_ops = &netdev_ethtool_ops; dev->watchdog_timeo = TX_TIMEOUT; - netif_napi_add(dev, &rp->napi, rhine_napipoll, 64); + netif_napi_add(dev, &rp->napi, rhine_napipoll); if (rp->quirks & rqRhineI) dev->features |= NETIF_F_SG|NETIF_F_HW_CSUM; @@ -2281,8 +2281,8 @@ static void netdev_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *i { struct device *hwdev = dev->dev.parent; - strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); - strlcpy(info->bus_info, dev_name(hwdev), sizeof(info->bus_info)); + strscpy(info->driver, DRV_NAME, sizeof(info->driver)); + strscpy(info->bus_info, dev_name(hwdev), sizeof(info->bus_info)); } static int netdev_get_link_ksettings(struct net_device *dev, diff --git a/drivers/net/ethernet/via/via-velocity.c b/drivers/net/ethernet/via/via-velocity.c index ff0c102cb578..a502812ac418 100644 --- a/drivers/net/ethernet/via/via-velocity.c +++ b/drivers/net/ethernet/via/via-velocity.c @@ -2846,7 +2846,7 @@ static int velocity_probe(struct device *dev, int irq, netdev->netdev_ops = &velocity_netdev_ops; netdev->ethtool_ops = &velocity_ethtool_ops; - netif_napi_add(netdev, &vptr->napi, velocity_poll, NAPI_POLL_WEIGHT); + netif_napi_add(netdev, &vptr->napi, velocity_poll); netdev->hw_features = NETIF_F_IP_CSUM | NETIF_F_SG | NETIF_F_HW_VLAN_CTAG_TX; @@ -3419,13 +3419,13 @@ static void velocity_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo { struct velocity_info *vptr = netdev_priv(dev); - strlcpy(info->driver, VELOCITY_NAME, sizeof(info->driver)); - strlcpy(info->version, VELOCITY_VERSION, sizeof(info->version)); + strscpy(info->driver, VELOCITY_NAME, sizeof(info->driver)); + strscpy(info->version, VELOCITY_VERSION, sizeof(info->version)); if (vptr->pdev) - strlcpy(info->bus_info, pci_name(vptr->pdev), + strscpy(info->bus_info, pci_name(vptr->pdev), sizeof(info->bus_info)); else - strlcpy(info->bus_info, "platform", sizeof(info->bus_info)); + strscpy(info->bus_info, "platform", sizeof(info->bus_info)); } static void velocity_ethtool_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol) diff --git a/drivers/net/ethernet/wangxun/Kconfig b/drivers/net/ethernet/wangxun/Kconfig index b4a4fa0a58f8..f5d43d8c9629 100644 --- a/drivers/net/ethernet/wangxun/Kconfig +++ b/drivers/net/ethernet/wangxun/Kconfig @@ -16,6 +16,19 @@ config NET_VENDOR_WANGXUN if NET_VENDOR_WANGXUN +config NGBE + tristate "Wangxun(R) GbE PCI Express adapters support" + depends on PCI + help + This driver supports Wangxun(R) GbE PCI Express family of + adapters. + + More specific information on configuring the driver is in + <file:Documentation/networking/device_drivers/ethernet/wangxun/ngbe.rst>. + + To compile this driver as a module, choose M here. The module + will be called ngbe. + config TXGBE tristate "Wangxun(R) 10GbE PCI Express adapters support" depends on PCI diff --git a/drivers/net/ethernet/wangxun/Makefile b/drivers/net/ethernet/wangxun/Makefile index c34db1bead25..ac3fb06b233c 100644 --- a/drivers/net/ethernet/wangxun/Makefile +++ b/drivers/net/ethernet/wangxun/Makefile @@ -4,3 +4,4 @@ # obj-$(CONFIG_TXGBE) += txgbe/ +obj-$(CONFIG_NGBE) += ngbe/ diff --git a/drivers/net/ethernet/wangxun/ngbe/Makefile b/drivers/net/ethernet/wangxun/ngbe/Makefile new file mode 100644 index 000000000000..0baf75907496 --- /dev/null +++ b/drivers/net/ethernet/wangxun/ngbe/Makefile @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: GPL-2.0 +# Copyright (c) 2019 - 2022 Beijing WangXun Technology Co., Ltd. +# +# Makefile for the Wangxun(R) GbE PCI Express ethernet driver +# + +obj-$(CONFIG_NGBE) += ngbe.o + +ngbe-objs := ngbe_main.o diff --git a/drivers/net/ethernet/wangxun/ngbe/ngbe.h b/drivers/net/ethernet/wangxun/ngbe/ngbe.h new file mode 100644 index 000000000000..f5fa6e5238cc --- /dev/null +++ b/drivers/net/ethernet/wangxun/ngbe/ngbe.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (c) 2019 - 2022 Beijing WangXun Technology Co., Ltd. */ + +#ifndef _NGBE_H_ +#define _NGBE_H_ + +#include "ngbe_type.h" + +#define NGBE_MAX_FDIR_INDICES 7 + +#define NGBE_MAX_RX_QUEUES (NGBE_MAX_FDIR_INDICES + 1) +#define NGBE_MAX_TX_QUEUES (NGBE_MAX_FDIR_INDICES + 1) + +/* board specific private data structure */ +struct ngbe_adapter { + u8 __iomem *io_addr; /* Mainly for iounmap use */ + /* OS defined structs */ + struct net_device *netdev; + struct pci_dev *pdev; +}; + +extern char ngbe_driver_name[]; + +#endif /* _NGBE_H_ */ diff --git a/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c b/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c new file mode 100644 index 000000000000..7674cb6e5700 --- /dev/null +++ b/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c @@ -0,0 +1,170 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2019 - 2022 Beijing WangXun Technology Co., Ltd. */ + +#include <linux/types.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/netdevice.h> +#include <linux/string.h> +#include <linux/aer.h> +#include <linux/etherdevice.h> + +#include "ngbe.h" +char ngbe_driver_name[] = "ngbe"; + +/* ngbe_pci_tbl - PCI Device ID Table + * + * { Vendor ID, Device ID, SubVendor ID, SubDevice ID, + * Class, Class Mask, private data (not used) } + */ +static const struct pci_device_id ngbe_pci_tbl[] = { + { PCI_VDEVICE(WANGXUN, NGBE_DEV_ID_EM_WX1860AL_W), 0}, + { PCI_VDEVICE(WANGXUN, NGBE_DEV_ID_EM_WX1860A2), 0}, + { PCI_VDEVICE(WANGXUN, NGBE_DEV_ID_EM_WX1860A2S), 0}, + { PCI_VDEVICE(WANGXUN, NGBE_DEV_ID_EM_WX1860A4), 0}, + { PCI_VDEVICE(WANGXUN, NGBE_DEV_ID_EM_WX1860A4S), 0}, + { PCI_VDEVICE(WANGXUN, NGBE_DEV_ID_EM_WX1860AL2), 0}, + { PCI_VDEVICE(WANGXUN, NGBE_DEV_ID_EM_WX1860AL2S), 0}, + { PCI_VDEVICE(WANGXUN, NGBE_DEV_ID_EM_WX1860AL4), 0}, + { PCI_VDEVICE(WANGXUN, NGBE_DEV_ID_EM_WX1860AL4S), 0}, + { PCI_VDEVICE(WANGXUN, NGBE_DEV_ID_EM_WX1860LC), 0}, + { PCI_VDEVICE(WANGXUN, NGBE_DEV_ID_EM_WX1860A1), 0}, + { PCI_VDEVICE(WANGXUN, NGBE_DEV_ID_EM_WX1860A1L), 0}, + /* required last entry */ + { .device = 0 } +}; + +static void ngbe_dev_shutdown(struct pci_dev *pdev, bool *enable_wake) +{ + struct ngbe_adapter *adapter = pci_get_drvdata(pdev); + struct net_device *netdev = adapter->netdev; + + netif_device_detach(netdev); + + pci_disable_device(pdev); +} + +static void ngbe_shutdown(struct pci_dev *pdev) +{ + bool wake; + + ngbe_dev_shutdown(pdev, &wake); + + if (system_state == SYSTEM_POWER_OFF) { + pci_wake_from_d3(pdev, wake); + pci_set_power_state(pdev, PCI_D3hot); + } +} + +/** + * ngbe_probe - Device Initialization Routine + * @pdev: PCI device information struct + * @ent: entry in ngbe_pci_tbl + * + * Returns 0 on success, negative on failure + * + * ngbe_probe initializes an adapter identified by a pci_dev structure. + * The OS initialization, configuring of the adapter private structure, + * and a hardware reset occur. + **/ +static int ngbe_probe(struct pci_dev *pdev, + const struct pci_device_id __always_unused *ent) +{ + struct ngbe_adapter *adapter = NULL; + struct net_device *netdev; + int err; + + err = pci_enable_device_mem(pdev); + if (err) + return err; + + err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)); + if (err) { + dev_err(&pdev->dev, + "No usable DMA configuration, aborting\n"); + goto err_pci_disable_dev; + } + + err = pci_request_selected_regions(pdev, + pci_select_bars(pdev, IORESOURCE_MEM), + ngbe_driver_name); + if (err) { + dev_err(&pdev->dev, + "pci_request_selected_regions failed %d\n", err); + goto err_pci_disable_dev; + } + + pci_enable_pcie_error_reporting(pdev); + pci_set_master(pdev); + + netdev = devm_alloc_etherdev_mqs(&pdev->dev, + sizeof(struct ngbe_adapter), + NGBE_MAX_TX_QUEUES, + NGBE_MAX_RX_QUEUES); + if (!netdev) { + err = -ENOMEM; + goto err_pci_release_regions; + } + + SET_NETDEV_DEV(netdev, &pdev->dev); + + adapter = netdev_priv(netdev); + adapter->netdev = netdev; + adapter->pdev = pdev; + + adapter->io_addr = devm_ioremap(&pdev->dev, + pci_resource_start(pdev, 0), + pci_resource_len(pdev, 0)); + if (!adapter->io_addr) { + err = -EIO; + goto err_pci_release_regions; + } + + netdev->features |= NETIF_F_HIGHDMA; + + pci_set_drvdata(pdev, adapter); + + return 0; + +err_pci_release_regions: + pci_disable_pcie_error_reporting(pdev); + pci_release_selected_regions(pdev, + pci_select_bars(pdev, IORESOURCE_MEM)); +err_pci_disable_dev: + pci_disable_device(pdev); + return err; +} + +/** + * ngbe_remove - Device Removal Routine + * @pdev: PCI device information struct + * + * ngbe_remove is called by the PCI subsystem to alert the driver + * that it should release a PCI device. The could be caused by a + * Hot-Plug event, or because the driver is going to be removed from + * memory. + **/ +static void ngbe_remove(struct pci_dev *pdev) +{ + pci_release_selected_regions(pdev, + pci_select_bars(pdev, IORESOURCE_MEM)); + + pci_disable_pcie_error_reporting(pdev); + + pci_disable_device(pdev); +} + +static struct pci_driver ngbe_driver = { + .name = ngbe_driver_name, + .id_table = ngbe_pci_tbl, + .probe = ngbe_probe, + .remove = ngbe_remove, + .shutdown = ngbe_shutdown, +}; + +module_pci_driver(ngbe_driver); + +MODULE_DEVICE_TABLE(pci, ngbe_pci_tbl); +MODULE_AUTHOR("Beijing WangXun Technology Co., Ltd, <software@net-swift.com>"); +MODULE_DESCRIPTION("WangXun(R) Gigabit PCI Express Network Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/ethernet/wangxun/ngbe/ngbe_type.h b/drivers/net/ethernet/wangxun/ngbe/ngbe_type.h new file mode 100644 index 000000000000..26e776c3539a --- /dev/null +++ b/drivers/net/ethernet/wangxun/ngbe/ngbe_type.h @@ -0,0 +1,50 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (c) 2019 - 2022 Beijing WangXun Technology Co., Ltd. */ + +#ifndef _NGBE_TYPE_H_ +#define _NGBE_TYPE_H_ + +#include <linux/types.h> +#include <linux/netdevice.h> + +/************ NGBE_register.h ************/ +/* Vendor ID */ +#ifndef PCI_VENDOR_ID_WANGXUN +#define PCI_VENDOR_ID_WANGXUN 0x8088 +#endif + +/* Device IDs */ +#define NGBE_DEV_ID_EM_WX1860AL_W 0x0100 +#define NGBE_DEV_ID_EM_WX1860A2 0x0101 +#define NGBE_DEV_ID_EM_WX1860A2S 0x0102 +#define NGBE_DEV_ID_EM_WX1860A4 0x0103 +#define NGBE_DEV_ID_EM_WX1860A4S 0x0104 +#define NGBE_DEV_ID_EM_WX1860AL2 0x0105 +#define NGBE_DEV_ID_EM_WX1860AL2S 0x0106 +#define NGBE_DEV_ID_EM_WX1860AL4 0x0107 +#define NGBE_DEV_ID_EM_WX1860AL4S 0x0108 +#define NGBE_DEV_ID_EM_WX1860LC 0x0109 +#define NGBE_DEV_ID_EM_WX1860A1 0x010a +#define NGBE_DEV_ID_EM_WX1860A1L 0x010b + +/* Subsystem ID */ +#define NGBE_SUBID_M88E1512_SFP 0x0003 +#define NGBE_SUBID_OCP_CARD 0x0040 +#define NGBE_SUBID_LY_M88E1512_SFP 0x0050 +#define NGBE_SUBID_M88E1512_RJ45 0x0051 +#define NGBE_SUBID_M88E1512_MIX 0x0052 +#define NGBE_SUBID_YT8521S_SFP 0x0060 +#define NGBE_SUBID_INTERNAL_YT8521S_SFP 0x0061 +#define NGBE_SUBID_YT8521S_SFP_GPIO 0x0062 +#define NGBE_SUBID_INTERNAL_YT8521S_SFP_GPIO 0x0064 +#define NGBE_SUBID_LY_YT8521S_SFP 0x0070 +#define NGBE_SUBID_RGMII_FPGA 0x0080 + +#define NGBE_OEM_MASK 0x00FF + +#define NGBE_NCSI_SUP 0x8000 +#define NGBE_NCSI_MASK 0x8000 +#define NGBE_WOL_SUP 0x4000 +#define NGBE_WOL_MASK 0x4000 + +#endif /* _NGBE_TYPE_H_ */ diff --git a/drivers/net/ethernet/wiznet/w5100.c b/drivers/net/ethernet/wiznet/w5100.c index acd78120e53c..634946e87e5f 100644 --- a/drivers/net/ethernet/wiznet/w5100.c +++ b/drivers/net/ethernet/wiznet/w5100.c @@ -719,9 +719,9 @@ static void w5100_hw_close(struct w5100_priv *priv) static void w5100_get_drvinfo(struct net_device *ndev, struct ethtool_drvinfo *info) { - strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); - strlcpy(info->version, DRV_VERSION, sizeof(info->version)); - strlcpy(info->bus_info, dev_name(ndev->dev.parent), + strscpy(info->driver, DRV_NAME, sizeof(info->driver)); + strscpy(info->version, DRV_VERSION, sizeof(info->version)); + strscpy(info->bus_info, dev_name(ndev->dev.parent), sizeof(info->bus_info)); } diff --git a/drivers/net/ethernet/wiznet/w5300.c b/drivers/net/ethernet/wiznet/w5300.c index 773f8c77909a..b0958fe8111e 100644 --- a/drivers/net/ethernet/wiznet/w5300.c +++ b/drivers/net/ethernet/wiznet/w5300.c @@ -282,9 +282,9 @@ static void w5300_hw_close(struct w5300_priv *priv) static void w5300_get_drvinfo(struct net_device *ndev, struct ethtool_drvinfo *info) { - strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); - strlcpy(info->version, DRV_VERSION, sizeof(info->version)); - strlcpy(info->bus_info, dev_name(ndev->dev.parent), + strscpy(info->driver, DRV_NAME, sizeof(info->driver)); + strscpy(info->version, DRV_VERSION, sizeof(info->version)); + strscpy(info->bus_info, dev_name(ndev->dev.parent), sizeof(info->bus_info)); } diff --git a/drivers/net/ethernet/xilinx/ll_temac.h b/drivers/net/ethernet/xilinx/ll_temac.h index c6395c406418..6668d1b760d8 100644 --- a/drivers/net/ethernet/xilinx/ll_temac.h +++ b/drivers/net/ethernet/xilinx/ll_temac.h @@ -21,36 +21,45 @@ /* Configuration options */ /* Accept all incoming packets. - * This option defaults to disabled (cleared) */ + * This option defaults to disabled (cleared) + */ #define XTE_OPTION_PROMISC (1 << 0) /* Jumbo frame support for Tx & Rx. - * This option defaults to disabled (cleared) */ + * This option defaults to disabled (cleared) + */ #define XTE_OPTION_JUMBO (1 << 1) /* VLAN Rx & Tx frame support. - * This option defaults to disabled (cleared) */ + * This option defaults to disabled (cleared) + */ #define XTE_OPTION_VLAN (1 << 2) /* Enable recognition of flow control frames on Rx - * This option defaults to enabled (set) */ + * This option defaults to enabled (set) + */ #define XTE_OPTION_FLOW_CONTROL (1 << 4) /* Strip FCS and PAD from incoming frames. * Note: PAD from VLAN frames is not stripped. - * This option defaults to disabled (set) */ + * This option defaults to disabled (set) + */ #define XTE_OPTION_FCS_STRIP (1 << 5) /* Generate FCS field and add PAD automatically for outgoing frames. - * This option defaults to enabled (set) */ + * This option defaults to enabled (set) + */ #define XTE_OPTION_FCS_INSERT (1 << 6) /* Enable Length/Type error checking for incoming frames. When this option is -set, the MAC will filter frames that have a mismatched type/length field -and if XTE_OPTION_REPORT_RXERR is set, the user is notified when these -types of frames are encountered. When this option is cleared, the MAC will -allow these types of frames to be received. -This option defaults to enabled (set) */ + * set, the MAC will filter frames that have a mismatched type/length field + * and if XTE_OPTION_REPORT_RXERR is set, the user is notified when these + * types of frames are encountered. When this option is cleared, the MAC will + * allow these types of frames to be received. + * This option defaults to enabled (set) + */ #define XTE_OPTION_LENTYPE_ERR (1 << 7) /* Enable the transmitter. - * This option defaults to enabled (set) */ + * This option defaults to enabled (set) + */ #define XTE_OPTION_TXEN (1 << 11) /* Enable the receiver -* This option defaults to enabled (set) */ + * This option defaults to enabled (set) + */ #define XTE_OPTION_RXEN (1 << 12) /* Default options set when device is initialized or reset */ @@ -68,18 +77,18 @@ This option defaults to enabled (set) */ #define TX_TAILDESC_PTR 0x04 /* rw */ #define TX_CHNL_CTRL 0x05 /* rw */ /* - 0:7 24:31 IRQTimeout - 8:15 16:23 IRQCount - 16:20 11:15 Reserved - 21 10 0 - 22 9 UseIntOnEnd - 23 8 LdIRQCnt - 24 7 IRQEn - 25:28 3:6 Reserved - 29 2 IrqErrEn - 30 1 IrqDlyEn - 31 0 IrqCoalEn -*/ + * 0:7 24:31 IRQTimeout + * 8:15 16:23 IRQCount + * 16:20 11:15 Reserved + * 21 10 0 + * 22 9 UseIntOnEnd + * 23 8 LdIRQCnt + * 24 7 IRQEn + * 25:28 3:6 Reserved + * 29 2 IrqErrEn + * 30 1 IrqDlyEn + * 31 0 IrqCoalEn + */ #define CHNL_CTRL_IRQ_IOE (1 << 9) #define CHNL_CTRL_IRQ_EN (1 << 7) #define CHNL_CTRL_IRQ_ERR_EN (1 << 2) @@ -87,35 +96,35 @@ This option defaults to enabled (set) */ #define CHNL_CTRL_IRQ_COAL_EN (1 << 0) #define TX_IRQ_REG 0x06 /* rw */ /* - 0:7 24:31 DltTmrValue - 8:15 16:23 ClscCntrValue - 16:17 14:15 Reserved - 18:21 10:13 ClscCnt - 22:23 8:9 DlyCnt - 24:28 3::7 Reserved - 29 2 ErrIrq - 30 1 DlyIrq - 31 0 CoalIrq + * 0:7 24:31 DltTmrValue + * 8:15 16:23 ClscCntrValue + * 16:17 14:15 Reserved + * 18:21 10:13 ClscCnt + * 22:23 8:9 DlyCnt + * 24:28 3::7 Reserved + * 29 2 ErrIrq + * 30 1 DlyIrq + * 31 0 CoalIrq */ #define TX_CHNL_STS 0x07 /* r */ /* - 0:9 22:31 Reserved - 10 21 TailPErr - 11 20 CmpErr - 12 19 AddrErr - 13 18 NxtPErr - 14 17 CurPErr - 15 16 BsyWr - 16:23 8:15 Reserved - 24 7 Error - 25 6 IOE - 26 5 SOE - 27 4 Cmplt - 28 3 SOP - 29 2 EOP - 30 1 EngBusy - 31 0 Reserved -*/ + * 0:9 22:31 Reserved + * 10 21 TailPErr + * 11 20 CmpErr + * 12 19 AddrErr + * 13 18 NxtPErr + * 14 17 CurPErr + * 15 16 BsyWr + * 16:23 8:15 Reserved + * 24 7 Error + * 25 6 IOE + * 26 5 SOE + * 27 4 Cmplt + * 28 3 SOP + * 29 2 EOP + * 30 1 EngBusy + * 31 0 Reserved + */ #define RX_NXTDESC_PTR 0x08 /* r */ #define RX_CURBUF_ADDR 0x09 /* r */ @@ -124,17 +133,17 @@ This option defaults to enabled (set) */ #define RX_TAILDESC_PTR 0x0c /* rw */ #define RX_CHNL_CTRL 0x0d /* rw */ /* - 0:7 24:31 IRQTimeout - 8:15 16:23 IRQCount - 16:20 11:15 Reserved - 21 10 0 - 22 9 UseIntOnEnd - 23 8 LdIRQCnt - 24 7 IRQEn - 25:28 3:6 Reserved - 29 2 IrqErrEn - 30 1 IrqDlyEn - 31 0 IrqCoalEn + * 0:7 24:31 IRQTimeout + * 8:15 16:23 IRQCount + * 16:20 11:15 Reserved + * 21 10 0 + * 22 9 UseIntOnEnd + * 23 8 LdIRQCnt + * 24 7 IRQEn + * 25:28 3:6 Reserved + * 29 2 IrqErrEn + * 30 1 IrqDlyEn + * 31 0 IrqCoalEn */ #define RX_IRQ_REG 0x0e /* rw */ #define IRQ_COAL (1 << 0) @@ -142,13 +151,13 @@ This option defaults to enabled (set) */ #define IRQ_ERR (1 << 2) #define IRQ_DMAERR (1 << 7) /* this is not documented ??? */ /* - 0:7 24:31 DltTmrValue - 8:15 16:23 ClscCntrValue - 16:17 14:15 Reserved - 18:21 10:13 ClscCnt - 22:23 8:9 DlyCnt - 24:28 3::7 Reserved -*/ + * 0:7 24:31 DltTmrValue + * 8:15 16:23 ClscCntrValue + * 16:17 14:15 Reserved + * 18:21 10:13 ClscCnt + * 22:23 8:9 DlyCnt + * 24:28 3::7 Reserved + */ #define RX_CHNL_STS 0x0f /* r */ #define CHNL_STS_ENGBUSY (1 << 1) #define CHNL_STS_EOP (1 << 2) @@ -165,23 +174,23 @@ This option defaults to enabled (set) */ #define CHNL_STS_CMPERR (1 << 20) #define CHNL_STS_TAILERR (1 << 21) /* - 0:9 22:31 Reserved - 10 21 TailPErr - 11 20 CmpErr - 12 19 AddrErr - 13 18 NxtPErr - 14 17 CurPErr - 15 16 BsyWr - 16:23 8:15 Reserved - 24 7 Error - 25 6 IOE - 26 5 SOE - 27 4 Cmplt - 28 3 SOP - 29 2 EOP - 30 1 EngBusy - 31 0 Reserved -*/ + * 0:9 22:31 Reserved + * 10 21 TailPErr + * 11 20 CmpErr + * 12 19 AddrErr + * 13 18 NxtPErr + * 14 17 CurPErr + * 15 16 BsyWr + * 16:23 8:15 Reserved + * 24 7 Error + * 25 6 IOE + * 26 5 SOE + * 27 4 Cmplt + * 28 3 SOP + * 29 2 EOP + * 30 1 EngBusy + * 31 0 Reserved + */ #define DMA_CONTROL_REG 0x10 /* rw */ #define DMA_CONTROL_RST (1 << 0) diff --git a/drivers/net/ethernet/xilinx/ll_temac_main.c b/drivers/net/ethernet/xilinx/ll_temac_main.c index 3f6b9dfca095..1066420d6a83 100644 --- a/drivers/net/ethernet/xilinx/ll_temac_main.c +++ b/drivers/net/ethernet/xilinx/ll_temac_main.c @@ -117,8 +117,8 @@ int temac_indirect_busywait(struct temac_local *lp) spin_until_cond(hard_acs_rdy_or_timeout(lp, timeout)); if (WARN_ON(!hard_acs_rdy(lp))) return -ETIMEDOUT; - else - return 0; + + return 0; } /* @@ -261,7 +261,7 @@ static void temac_dma_dcr_out(struct temac_local *lp, int reg, u32 value) * I/O functions */ static int temac_dcr_setup(struct temac_local *lp, struct platform_device *op, - struct device_node *np) + struct device_node *np) { unsigned int dcrs; @@ -286,7 +286,7 @@ static int temac_dcr_setup(struct temac_local *lp, struct platform_device *op, * such as with MicroBlaze and x86 */ static int temac_dcr_setup(struct temac_local *lp, struct platform_device *op, - struct device_node *np) + struct device_node *np) { return -1; } @@ -307,11 +307,9 @@ static void temac_dma_bd_release(struct net_device *ndev) for (i = 0; i < lp->rx_bd_num; i++) { if (!lp->rx_skb[i]) break; - else { - dma_unmap_single(ndev->dev.parent, lp->rx_bd_v[i].phys, - XTE_MAX_JUMBO_FRAME_SIZE, DMA_FROM_DEVICE); - dev_kfree_skb(lp->rx_skb[i]); - } + dma_unmap_single(ndev->dev.parent, lp->rx_bd_v[i].phys, + XTE_MAX_JUMBO_FRAME_SIZE, DMA_FROM_DEVICE); + dev_kfree_skb(lp->rx_skb[i]); } if (lp->rx_bd_v) dma_free_coherent(ndev->dev.parent, @@ -430,7 +428,8 @@ static void temac_do_set_mac_address(struct net_device *ndev) (ndev->dev_addr[2] << 16) | (ndev->dev_addr[3] << 24)); /* There are reserved bits in EUAW1 - * so don't affect them Set MAC bits [47:32] in EUAW1 */ + * so don't affect them Set MAC bits [47:32] in EUAW1 + */ temac_indirect_out32_locked(lp, XTE_UAW1_OFFSET, (ndev->dev_addr[4] & 0x000000ff) | (ndev->dev_addr[5] << 8)); @@ -530,66 +529,66 @@ static struct temac_option { { .opt = XTE_OPTION_JUMBO, .reg = XTE_RXC1_OFFSET, - .m_or =XTE_RXC1_RXJMBO_MASK, + .m_or = XTE_RXC1_RXJMBO_MASK, }, /* Turn on VLAN packet support for both Rx and Tx */ { .opt = XTE_OPTION_VLAN, .reg = XTE_TXC_OFFSET, - .m_or =XTE_TXC_TXVLAN_MASK, + .m_or = XTE_TXC_TXVLAN_MASK, }, { .opt = XTE_OPTION_VLAN, .reg = XTE_RXC1_OFFSET, - .m_or =XTE_RXC1_RXVLAN_MASK, + .m_or = XTE_RXC1_RXVLAN_MASK, }, /* Turn on FCS stripping on receive packets */ { .opt = XTE_OPTION_FCS_STRIP, .reg = XTE_RXC1_OFFSET, - .m_or =XTE_RXC1_RXFCS_MASK, + .m_or = XTE_RXC1_RXFCS_MASK, }, /* Turn on FCS insertion on transmit packets */ { .opt = XTE_OPTION_FCS_INSERT, .reg = XTE_TXC_OFFSET, - .m_or =XTE_TXC_TXFCS_MASK, + .m_or = XTE_TXC_TXFCS_MASK, }, /* Turn on length/type field checking on receive packets */ { .opt = XTE_OPTION_LENTYPE_ERR, .reg = XTE_RXC1_OFFSET, - .m_or =XTE_RXC1_RXLT_MASK, + .m_or = XTE_RXC1_RXLT_MASK, }, /* Turn on flow control */ { .opt = XTE_OPTION_FLOW_CONTROL, .reg = XTE_FCC_OFFSET, - .m_or =XTE_FCC_RXFLO_MASK, + .m_or = XTE_FCC_RXFLO_MASK, }, /* Turn on flow control */ { .opt = XTE_OPTION_FLOW_CONTROL, .reg = XTE_FCC_OFFSET, - .m_or =XTE_FCC_TXFLO_MASK, + .m_or = XTE_FCC_TXFLO_MASK, }, /* Turn on promiscuous frame filtering (all frames are received ) */ { .opt = XTE_OPTION_PROMISC, .reg = XTE_AFM_OFFSET, - .m_or =XTE_AFM_EPPRM_MASK, + .m_or = XTE_AFM_EPPRM_MASK, }, /* Enable transmitter if not already enabled */ { .opt = XTE_OPTION_TXEN, .reg = XTE_TXC_OFFSET, - .m_or =XTE_TXC_TXEN_MASK, + .m_or = XTE_TXC_TXEN_MASK, }, /* Enable receiver? */ { .opt = XTE_OPTION_RXEN, .reg = XTE_RXC1_OFFSET, - .m_or =XTE_RXC1_RXEN_MASK, + .m_or = XTE_RXC1_RXEN_MASK, }, {} }; @@ -641,7 +640,7 @@ static void temac_device_reset(struct net_device *ndev) udelay(1); if (--timeout == 0) { dev_err(&ndev->dev, - "temac_device_reset RX reset timeout!!\n"); + "%s RX reset timeout!!\n", __func__); break; } } @@ -653,7 +652,7 @@ static void temac_device_reset(struct net_device *ndev) udelay(1); if (--timeout == 0) { dev_err(&ndev->dev, - "temac_device_reset TX reset timeout!!\n"); + "%s TX reset timeout!!\n", __func__); break; } } @@ -672,7 +671,7 @@ static void temac_device_reset(struct net_device *ndev) udelay(1); if (--timeout == 0) { dev_err(&ndev->dev, - "temac_device_reset DMA reset timeout!!\n"); + "%s DMA reset timeout!!\n", __func__); break; } } @@ -680,7 +679,7 @@ static void temac_device_reset(struct net_device *ndev) if (temac_dma_bd_init(ndev)) { dev_err(&ndev->dev, - "temac_device_reset descriptor allocation failed\n"); + "%s descriptor allocation failed\n", __func__); } spin_lock_irqsave(lp->indirect_lock, flags); @@ -691,7 +690,8 @@ static void temac_device_reset(struct net_device *ndev) spin_unlock_irqrestore(lp->indirect_lock, flags); /* Sync default options with HW - * but leave receiver and transmitter disabled. */ + * but leave receiver and transmitter disabled. + */ temac_setoptions(ndev, lp->options & ~(XTE_OPTION_TXEN | XTE_OPTION_RXEN)); @@ -723,9 +723,15 @@ static void temac_adjust_link(struct net_device *ndev) mii_speed &= ~XTE_EMCFG_LINKSPD_MASK; switch (phy->speed) { - case SPEED_1000: mii_speed |= XTE_EMCFG_LINKSPD_1000; break; - case SPEED_100: mii_speed |= XTE_EMCFG_LINKSPD_100; break; - case SPEED_10: mii_speed |= XTE_EMCFG_LINKSPD_10; break; + case SPEED_1000: + mii_speed |= XTE_EMCFG_LINKSPD_1000; + break; + case SPEED_100: + mii_speed |= XTE_EMCFG_LINKSPD_100; + break; + case SPEED_10: + mii_speed |= XTE_EMCFG_LINKSPD_10; + break; } /* Write new speed setting out to TEMAC */ @@ -1007,7 +1013,6 @@ static void ll_temac_recv(struct net_device *ndev) if (((lp->temac_features & TEMAC_FEATURE_RX_CSUM) != 0) && (skb->protocol == htons(ETH_P_IP)) && (skb->len > 64)) { - /* Convert from device endianness (be32) to cpu * endianness, and if necessary swap the bytes * (back) for proper IP checksum byte order @@ -1563,16 +1568,12 @@ static int temac_probe(struct platform_device *pdev) } /* Error handle returned DMA RX and TX interrupts */ - if (lp->rx_irq < 0) { - if (lp->rx_irq != -EPROBE_DEFER) - dev_err(&pdev->dev, "could not get DMA RX irq\n"); - return lp->rx_irq; - } - if (lp->tx_irq < 0) { - if (lp->tx_irq != -EPROBE_DEFER) - dev_err(&pdev->dev, "could not get DMA TX irq\n"); - return lp->tx_irq; - } + if (lp->rx_irq < 0) + return dev_err_probe(&pdev->dev, lp->rx_irq, + "could not get DMA RX irq\n"); + if (lp->tx_irq < 0) + return dev_err_probe(&pdev->dev, lp->tx_irq, + "could not get DMA TX irq\n"); if (temac_np) { /* Retrieve the MAC address */ diff --git a/drivers/net/ethernet/xilinx/ll_temac_mdio.c b/drivers/net/ethernet/xilinx/ll_temac_mdio.c index 6fd2dea4e60f..2371c072b53f 100644 --- a/drivers/net/ethernet/xilinx/ll_temac_mdio.c +++ b/drivers/net/ethernet/xilinx/ll_temac_mdio.c @@ -29,7 +29,8 @@ static int temac_mdio_read(struct mii_bus *bus, int phy_id, int reg) /* Write the PHY address to the MIIM Access Initiator register. * When the transfer completes, the PHY register value will appear - * in the LSW0 register */ + * in the LSW0 register + */ spin_lock_irqsave(lp->indirect_lock, flags); temac_iow(lp, XTE_LSW0_OFFSET, (phy_id << 5) | reg); rc = temac_indirect_in32_locked(lp, XTE_MIIMAI_OFFSET); @@ -88,7 +89,8 @@ int temac_mdio_setup(struct temac_local *lp, struct platform_device *pdev) } /* Enable the MDIO bus by asserting the enable bit and writing - * in the clock config */ + * in the clock config + */ temac_indirect_out32(lp, XTE_MC_OFFSET, 1 << 6 | clk_div); bus = devm_mdiobus_alloc(&pdev->dev); diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet.h b/drivers/net/ethernet/xilinx/xilinx_axienet.h index f2e2261b4b7d..6370c447ac5c 100644 --- a/drivers/net/ethernet/xilinx/xilinx_axienet.h +++ b/drivers/net/ethernet/xilinx/xilinx_axienet.h @@ -402,6 +402,9 @@ struct axidma_bd { * @rx_bd_num: Size of RX buffer descriptor ring * @rx_bd_ci: Stores the index of the Rx buffer descriptor in the ring being * accessed currently. + * @rx_packets: RX packet count for statistics + * @rx_bytes: RX byte count for statistics + * @rx_stat_sync: Synchronization object for RX stats * @napi_tx: NAPI TX control structure * @tx_dma_cr: Nominal content of TX DMA control register * @tx_bd_v: Virtual address of the TX buffer descriptor ring @@ -411,6 +414,9 @@ struct axidma_bd { * complete. Only updated at runtime by TX NAPI poll. * @tx_bd_tail: Stores the index of the next Tx buffer descriptor in the ring * to be populated. + * @tx_packets: TX packet count for statistics + * @tx_bytes: TX byte count for statistics + * @tx_stat_sync: Synchronization object for TX stats * @dma_err_task: Work structure to process Axi DMA errors * @tx_irq: Axidma TX IRQ number * @rx_irq: Axidma RX IRQ number @@ -458,6 +464,9 @@ struct axienet_local { dma_addr_t rx_bd_p; u32 rx_bd_num; u32 rx_bd_ci; + u64_stats_t rx_packets; + u64_stats_t rx_bytes; + struct u64_stats_sync rx_stat_sync; struct napi_struct napi_tx; u32 tx_dma_cr; @@ -466,6 +475,9 @@ struct axienet_local { u32 tx_bd_num; u32 tx_bd_ci; u32 tx_bd_tail; + u64_stats_t tx_packets; + u64_stats_t tx_bytes; + struct u64_stats_sync tx_stat_sync; struct work_struct dma_err_task; @@ -591,7 +603,7 @@ static inline void axienet_dma_out_addr(struct axienet_local *lp, off_t reg, #else /* CONFIG_64BIT */ static inline void axienet_dma_out_addr(struct axienet_local *lp, off_t reg, - dma_addr_t addr) + dma_addr_t addr) { axienet_dma_out32(lp, reg, lower_32_bits(addr)); } diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c index 1760930ec0c4..d1d772580da9 100644 --- a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c +++ b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c @@ -597,7 +597,7 @@ static int axienet_device_reset(struct net_device *ndev) lp->options &= (~XAE_OPTION_JUMBO); if ((ndev->mtu > XAE_MTU) && - (ndev->mtu <= XAE_JUMBO_MTU)) { + (ndev->mtu <= XAE_JUMBO_MTU)) { lp->max_frm_size = ndev->mtu + VLAN_ETH_HLEN + XAE_TRL_SIZE; @@ -645,7 +645,7 @@ static int axienet_device_reset(struct net_device *ndev) * @nr_bds: Max number of descriptors to clean up * @force: Whether to clean descriptors even if not complete * @sizep: Pointer to a u32 filled with the total sum of all bytes - * in all cleaned-up descriptors. Ignored if NULL. + * in all cleaned-up descriptors. Ignored if NULL. * @budget: NAPI budget (use 0 when not called from NAPI poll) * * Would either be called after a successful transmit operation, or after @@ -752,8 +752,10 @@ static int axienet_tx_poll(struct napi_struct *napi, int budget) if (lp->tx_bd_ci >= lp->tx_bd_num) lp->tx_bd_ci %= lp->tx_bd_num; - ndev->stats.tx_packets += packets; - ndev->stats.tx_bytes += size; + u64_stats_update_begin(&lp->tx_stat_sync); + u64_stats_add(&lp->tx_packets, packets); + u64_stats_add(&lp->tx_bytes, size); + u64_stats_update_end(&lp->tx_stat_sync); /* Matches barrier in axienet_start_xmit */ smp_mb(); @@ -984,8 +986,10 @@ static int axienet_rx_poll(struct napi_struct *napi, int budget) cur_p = &lp->rx_bd_v[lp->rx_bd_ci]; } - lp->ndev->stats.rx_packets += packets; - lp->ndev->stats.rx_bytes += size; + u64_stats_update_begin(&lp->rx_stat_sync); + u64_stats_add(&lp->rx_packets, packets); + u64_stats_add(&lp->rx_bytes, size); + u64_stats_update_end(&lp->rx_stat_sync); if (tail_p) axienet_dma_out_addr(lp, XAXIDMA_RX_TDESC_OFFSET, tail_p); @@ -1292,10 +1296,32 @@ static int axienet_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) return phylink_mii_ioctl(lp->phylink, rq, cmd); } +static void +axienet_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats) +{ + struct axienet_local *lp = netdev_priv(dev); + unsigned int start; + + netdev_stats_to_stats64(stats, &dev->stats); + + do { + start = u64_stats_fetch_begin_irq(&lp->rx_stat_sync); + stats->rx_packets = u64_stats_read(&lp->rx_packets); + stats->rx_bytes = u64_stats_read(&lp->rx_bytes); + } while (u64_stats_fetch_retry_irq(&lp->rx_stat_sync, start)); + + do { + start = u64_stats_fetch_begin_irq(&lp->tx_stat_sync); + stats->tx_packets = u64_stats_read(&lp->tx_packets); + stats->tx_bytes = u64_stats_read(&lp->tx_bytes); + } while (u64_stats_fetch_retry_irq(&lp->tx_stat_sync, start)); +} + static const struct net_device_ops axienet_netdev_ops = { .ndo_open = axienet_open, .ndo_stop = axienet_stop, .ndo_start_xmit = axienet_start_xmit, + .ndo_get_stats64 = axienet_get_stats64, .ndo_change_mtu = axienet_change_mtu, .ndo_set_mac_address = netdev_set_mac_address, .ndo_validate_addr = eth_validate_addr, @@ -1317,8 +1343,8 @@ static const struct net_device_ops axienet_netdev_ops = { static void axienet_ethtools_get_drvinfo(struct net_device *ndev, struct ethtool_drvinfo *ed) { - strlcpy(ed->driver, DRIVER_NAME, sizeof(ed->driver)); - strlcpy(ed->version, DRIVER_VERSION, sizeof(ed->version)); + strscpy(ed->driver, DRIVER_NAME, sizeof(ed->driver)); + strscpy(ed->version, DRIVER_VERSION, sizeof(ed->version)); } /** @@ -1349,7 +1375,7 @@ static int axienet_ethtools_get_regs_len(struct net_device *ndev) static void axienet_ethtools_get_regs(struct net_device *ndev, struct ethtool_regs *regs, void *ret) { - u32 *data = (u32 *) ret; + u32 *data = (u32 *)ret; size_t len = sizeof(u32) * AXIENET_REGS_N; struct axienet_local *lp = netdev_priv(ndev); @@ -1850,8 +1876,11 @@ static int axienet_probe(struct platform_device *pdev) lp->rx_bd_num = RX_BD_NUM_DEFAULT; lp->tx_bd_num = TX_BD_NUM_DEFAULT; - netif_napi_add(ndev, &lp->napi_rx, axienet_rx_poll, NAPI_POLL_WEIGHT); - netif_napi_add(ndev, &lp->napi_tx, axienet_tx_poll, NAPI_POLL_WEIGHT); + u64_stats_init(&lp->rx_stat_sync); + u64_stats_init(&lp->tx_stat_sync); + + netif_napi_add(ndev, &lp->napi_rx, axienet_rx_poll); + netif_napi_add(ndev, &lp->napi_tx, axienet_tx_poll); lp->axi_clk = devm_clk_get_optional(&pdev->dev, "s_axi_lite_clk"); if (!lp->axi_clk) { diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet_mdio.c b/drivers/net/ethernet/xilinx/xilinx_axienet_mdio.c index 2772a79cd3ed..0b3b6935c558 100644 --- a/drivers/net/ethernet/xilinx/xilinx_axienet_mdio.c +++ b/drivers/net/ethernet/xilinx/xilinx_axienet_mdio.c @@ -126,7 +126,7 @@ static int axienet_mdio_write(struct mii_bus *bus, int phy_id, int reg, return ret; } - axienet_iow(lp, XAE_MDIO_MWD_OFFSET, (u32) val); + axienet_iow(lp, XAE_MDIO_MWD_OFFSET, (u32)val); axienet_iow(lp, XAE_MDIO_MCR_OFFSET, (((phy_id << XAE_MDIO_MCR_PHYAD_SHIFT) & XAE_MDIO_MCR_PHYAD_MASK) | diff --git a/drivers/net/ethernet/xilinx/xilinx_emaclite.c b/drivers/net/ethernet/xilinx/xilinx_emaclite.c index 016a9c4f2c6c..05848ff15fb5 100644 --- a/drivers/net/ethernet/xilinx/xilinx_emaclite.c +++ b/drivers/net/ethernet/xilinx/xilinx_emaclite.c @@ -1060,7 +1060,7 @@ static bool get_bool(struct platform_device *ofdev, const char *s) static void xemaclite_ethtools_get_drvinfo(struct net_device *ndev, struct ethtool_drvinfo *ed) { - strlcpy(ed->driver, DRIVER_NAME, sizeof(ed->driver)); + strscpy(ed->driver, DRIVER_NAME, sizeof(ed->driver)); } static const struct ethtool_ops xemaclite_ethtool_ops = { diff --git a/drivers/net/ethernet/xircom/xirc2ps_cs.c b/drivers/net/ethernet/xircom/xirc2ps_cs.c index f9587e55b842..894e92ef415b 100644 --- a/drivers/net/ethernet/xircom/xirc2ps_cs.c +++ b/drivers/net/ethernet/xircom/xirc2ps_cs.c @@ -1402,7 +1402,7 @@ do_open(struct net_device *dev) static void netdev_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { - strlcpy(info->driver, "xirc2ps_cs", sizeof(info->driver)); + strscpy(info->driver, "xirc2ps_cs", sizeof(info->driver)); snprintf(info->bus_info, sizeof(info->bus_info), "PCMCIA 0x%lx", dev->base_addr); } diff --git a/drivers/net/ethernet/xscale/ixp4xx_eth.c b/drivers/net/ethernet/xscale/ixp4xx_eth.c index 3591b9edc9a1..3b0c5f177447 100644 --- a/drivers/net/ethernet/xscale/ixp4xx_eth.c +++ b/drivers/net/ethernet/xscale/ixp4xx_eth.c @@ -841,7 +841,7 @@ static void eth_txdone_irq(void *unused) } } -static int eth_xmit(struct sk_buff *skb, struct net_device *dev) +static netdev_tx_t eth_xmit(struct sk_buff *skb, struct net_device *dev) { struct port *port = netdev_priv(dev); unsigned int txreadyq = port->plat->txreadyq; @@ -999,11 +999,11 @@ static void ixp4xx_get_drvinfo(struct net_device *dev, { struct port *port = netdev_priv(dev); - strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); + strscpy(info->driver, DRV_NAME, sizeof(info->driver)); snprintf(info->fw_version, sizeof(info->fw_version), "%u:%u:%u:%u", port->firmware[0], port->firmware[1], port->firmware[2], port->firmware[3]); - strlcpy(info->bus_info, "internal", sizeof(info->bus_info)); + strscpy(info->bus_info, "internal", sizeof(info->bus_info)); } static int ixp4xx_get_ts_info(struct net_device *dev, |