From 465b210fdc653086fca34914c3a633efafb83e71 Mon Sep 17 00:00:00 2001 From: Hangbin Liu Date: Thu, 20 Feb 2025 08:53:26 +0000 Subject: [PATCH 01/16] selftests: fib_nexthops: do not mark skipped tests as failed The current test marks all unexpected return values as failed and sets ret to 1. If a test is skipped, the entire test also returns 1, incorrectly indicating failure. To fix this, add a skipped variable and set ret to 4 if it was previously 0. Otherwise, keep ret set to 1. Signed-off-by: Hangbin Liu Link: https://patch.msgid.link/20250220085326.1512814-1-liuhangbin@gmail.com Signed-off-by: Jakub Kicinski --- tools/testing/selftests/net/fib_nexthops.sh | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tools/testing/selftests/net/fib_nexthops.sh b/tools/testing/selftests/net/fib_nexthops.sh index bea1282e0281..b39f748c2572 100755 --- a/tools/testing/selftests/net/fib_nexthops.sh +++ b/tools/testing/selftests/net/fib_nexthops.sh @@ -76,11 +76,13 @@ log_test() printf "TEST: %-60s [ OK ]\n" "${msg}" nsuccess=$((nsuccess+1)) else - ret=1 - nfail=$((nfail+1)) if [[ $rc -eq $ksft_skip ]]; then + [[ $ret -eq 0 ]] && ret=$ksft_skip + nskip=$((nskip+1)) printf "TEST: %-60s [SKIP]\n" "${msg}" else + ret=1 + nfail=$((nfail+1)) printf "TEST: %-60s [FAIL]\n" "${msg}" fi @@ -2528,6 +2530,7 @@ done if [ "$TESTS" != "none" ]; then printf "\nTests passed: %3d\n" ${nsuccess} printf "Tests failed: %3d\n" ${nfail} + printf "Tests skipped: %2d\n" ${nskip} fi exit $ret -- 2.51.0 From 3e401818c81bb9f8438d1c2a803471f441981329 Mon Sep 17 00:00:00 2001 From: "Russell King (Oracle)" Date: Thu, 20 Feb 2025 12:47:49 +0000 Subject: [PATCH 02/16] net: stmmac: print stmmac_init_dma_engine() errors using netdev_err() stmmac_init_dma_engine() uses dev_err() which leads to errors being reported as e.g: dwc-eth-dwmac 2490000.ethernet: Failed to reset the dma dwc-eth-dwmac 2490000.ethernet eth0: stmmac_hw_setup: DMA engine initialization failed stmmac_init_dma_engine() is only called from stmmac_hw_setup() which itself uses netdev_err(), and we will have a net_device setup. So, change the dev_err() to netdev_err() to give consistent error messages. Signed-off-by: Russell King (Oracle) Link: https://patch.msgid.link/E1tl5y1-004UgG-8X@rmk-PC.armlinux.org.uk Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index edf4b1d651a6..424fa2fe31c6 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -3047,7 +3047,7 @@ static int stmmac_init_dma_engine(struct stmmac_priv *priv) int ret = 0; if (!priv->plat->dma_cfg || !priv->plat->dma_cfg->pbl) { - dev_err(priv->device, "Invalid DMA configuration\n"); + netdev_err(priv->dev, "Invalid DMA configuration\n"); return -EINVAL; } @@ -3056,7 +3056,7 @@ static int stmmac_init_dma_engine(struct stmmac_priv *priv) ret = stmmac_reset(priv, priv->ioaddr); if (ret) { - dev_err(priv->device, "Failed to reset the dma\n"); + netdev_err(priv->dev, "Failed to reset the dma\n"); return ret; } -- 2.51.0 From e6a532185daae9302bf2f358c8675733093b906e Mon Sep 17 00:00:00 2001 From: Sean Anderson Date: Thu, 20 Feb 2025 11:42:57 -0500 Subject: [PATCH 03/16] net: cadence: macb: Implement BQL Implement byte queue limits to allow queuing disciplines to account for packets enqueued in the ring buffer but not yet transmitted. There are a separate set of transmit functions for AT91 that I haven't touched since I don't have hardware to test on. Signed-off-by: Sean Anderson Link: https://patch.msgid.link/20250220164257.96859-1-sean.anderson@linux.dev Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/cadence/macb_main.c | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/cadence/macb_main.c b/drivers/net/ethernet/cadence/macb_main.c index 2112a9701e05..5345f3e1a795 100644 --- a/drivers/net/ethernet/cadence/macb_main.c +++ b/drivers/net/ethernet/cadence/macb_main.c @@ -1079,15 +1079,18 @@ static void macb_tx_error_task(struct work_struct *work) tx_error_task); bool halt_timeout = false; struct macb *bp = queue->bp; + u32 queue_index; + u32 packets = 0; + u32 bytes = 0; struct macb_tx_skb *tx_skb; struct macb_dma_desc *desc; struct sk_buff *skb; unsigned int tail; unsigned long flags; + queue_index = queue - bp->queues; netdev_vdbg(bp->dev, "macb_tx_error_task: q = %u, t = %u, h = %u\n", - (unsigned int)(queue - bp->queues), - queue->tx_tail, queue->tx_head); + queue_index, queue->tx_tail, queue->tx_head); /* Prevent the queue NAPI TX poll from running, as it calls * macb_tx_complete(), which in turn may call netif_wake_subqueue(). @@ -1140,8 +1143,10 @@ static void macb_tx_error_task(struct work_struct *work) skb->data); bp->dev->stats.tx_packets++; queue->stats.tx_packets++; + packets++; bp->dev->stats.tx_bytes += skb->len; queue->stats.tx_bytes += skb->len; + bytes += skb->len; } } else { /* "Buffers exhausted mid-frame" errors may only happen @@ -1158,6 +1163,9 @@ static void macb_tx_error_task(struct work_struct *work) macb_tx_unmap(bp, tx_skb, 0); } + netdev_tx_completed_queue(netdev_get_tx_queue(bp->dev, queue_index), + packets, bytes); + /* Set end of TX queue */ desc = macb_tx_desc(queue, 0); macb_set_addr(bp, desc, 0); @@ -1228,6 +1236,7 @@ static int macb_tx_complete(struct macb_queue *queue, int budget) unsigned int tail; unsigned int head; int packets = 0; + u32 bytes = 0; spin_lock(&queue->tx_ptr_lock); head = queue->tx_head; @@ -1269,6 +1278,7 @@ static int macb_tx_complete(struct macb_queue *queue, int budget) bp->dev->stats.tx_bytes += skb->len; queue->stats.tx_bytes += skb->len; packets++; + bytes += skb->len; } /* Now we can safely release resources */ @@ -1283,6 +1293,9 @@ static int macb_tx_complete(struct macb_queue *queue, int budget) } } + netdev_tx_completed_queue(netdev_get_tx_queue(bp->dev, queue_index), + packets, bytes); + queue->tx_tail = tail; if (__netif_subqueue_stopped(bp->dev, queue_index) && CIRC_CNT(queue->tx_head, queue->tx_tail, @@ -2384,6 +2397,8 @@ static netdev_tx_t macb_start_xmit(struct sk_buff *skb, struct net_device *dev) /* Make newly initialized descriptor visible to hardware */ wmb(); skb_tx_timestamp(skb); + netdev_tx_sent_queue(netdev_get_tx_queue(bp->dev, queue_index), + skb->len); spin_lock_irq(&bp->lock); macb_writel(bp, NCR, macb_readl(bp, NCR) | MACB_BIT(TSTART)); @@ -3019,6 +3034,7 @@ static int macb_close(struct net_device *dev) for (q = 0, queue = bp->queues; q < bp->num_queues; ++q, ++queue) { napi_disable(&queue->napi_rx); napi_disable(&queue->napi_tx); + netdev_tx_reset_queue(netdev_get_tx_queue(dev, q)); } phylink_stop(bp->phylink); -- 2.51.0 From dcc35baae732b9079b2c6595cfd86da02b34a4e6 Mon Sep 17 00:00:00 2001 From: Jeremy Kerr Date: Fri, 21 Feb 2025 08:56:57 +0800 Subject: [PATCH 04/16] usb: Add base USB MCTP definitions Upcoming changes will add a USB host (and later gadget) driver for the MCTP-over-USB protocol. Add a header that provides common definitions for protocol support: the packet header format and a few framing definitions. Add a define for the MCTP class code, as per https://usb.org/defined-class-codes. Signed-off-by: Jeremy Kerr Acked-by: Greg Kroah-Hartman Link: https://patch.msgid.link/20250221-dev-mctp-usb-v3-1-3353030fe9cc@codeconstruct.com.au Signed-off-by: Jakub Kicinski --- MAINTAINERS | 1 + include/linux/usb/mctp-usb.h | 30 ++++++++++++++++++++++++++++++ include/uapi/linux/usb/ch9.h | 1 + 3 files changed, 32 insertions(+) create mode 100644 include/linux/usb/mctp-usb.h diff --git a/MAINTAINERS b/MAINTAINERS index b0cd54818bb3..8019d5a97546 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -13896,6 +13896,7 @@ L: netdev@vger.kernel.org S: Maintained F: Documentation/networking/mctp.rst F: drivers/net/mctp/ +F: include/linux/usb/mctp-usb.h F: include/net/mctp.h F: include/net/mctpdevice.h F: include/net/netns/mctp.h diff --git a/include/linux/usb/mctp-usb.h b/include/linux/usb/mctp-usb.h new file mode 100644 index 000000000000..a2f6f1e04efb --- /dev/null +++ b/include/linux/usb/mctp-usb.h @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * mctp-usb.h - MCTP USB transport binding: common definitions, + * based on DMTF0283 specification: + * https://www.dmtf.org/sites/default/files/standards/documents/DSP0283_1.0.1.pdf + * + * These are protocol-level definitions, that may be shared between host + * and gadget drivers. + * + * Copyright (C) 2024-2025 Code Construct Pty Ltd + */ + +#ifndef __LINUX_USB_MCTP_USB_H +#define __LINUX_USB_MCTP_USB_H + +#include + +struct mctp_usb_hdr { + __be16 id; + u8 rsvd; + u8 len; +} __packed; + +#define MCTP_USB_XFER_SIZE 512 +#define MCTP_USB_BTU 68 +#define MCTP_USB_MTU_MIN MCTP_USB_BTU +#define MCTP_USB_MTU_MAX (U8_MAX - sizeof(struct mctp_usb_hdr)) +#define MCTP_USB_DMTF_ID 0x1ab4 + +#endif /* __LINUX_USB_MCTP_USB_H */ diff --git a/include/uapi/linux/usb/ch9.h b/include/uapi/linux/usb/ch9.h index 91f0f7e214a5..052290652046 100644 --- a/include/uapi/linux/usb/ch9.h +++ b/include/uapi/linux/usb/ch9.h @@ -330,6 +330,7 @@ struct usb_device_descriptor { #define USB_CLASS_AUDIO_VIDEO 0x10 #define USB_CLASS_BILLBOARD 0x11 #define USB_CLASS_USB_TYPE_C_BRIDGE 0x12 +#define USB_CLASS_MCTP 0x14 #define USB_CLASS_MISC 0xef #define USB_CLASS_APP_SPEC 0xfe #define USB_SUBCLASS_DFU 0x01 -- 2.51.0 From 0791c0327a6e4e7691d6fc5ad334c215de04dcc9 Mon Sep 17 00:00:00 2001 From: Jeremy Kerr Date: Fri, 21 Feb 2025 08:56:58 +0800 Subject: [PATCH 05/16] net: mctp: Add MCTP USB transport driver Add an implementation for DMTF DSP0283, which defines a MCTP-over-USB transport. As per that spec, we're restricted to full speed mode, requiring 512-byte transfers. Each MCTP-over-USB interface is a peer-to-peer link to a single MCTP endpoint, so no physical addressing is required (of course, that MCTP endpoint may then bridge to further MCTP endpoints). Consequently, interfaces will report with no lladdr data: # mctp link dev lo index 1 address 00:00:00:00:00:00 net 1 mtu 65536 up dev mctpusb0 index 6 address none net 1 mtu 68 up This is a simple initial implementation, with single rx & tx urbs, and no multi-packet tx transfers - although we do accept multi-packet rx from the device. Includes suggested fixes from Santosh Puranik . Signed-off-by: Jeremy Kerr Cc: Santosh Puranik Link: https://patch.msgid.link/20250221-dev-mctp-usb-v3-2-3353030fe9cc@codeconstruct.com.au Signed-off-by: Jakub Kicinski --- drivers/net/mctp/Kconfig | 10 + drivers/net/mctp/Makefile | 1 + drivers/net/mctp/mctp-usb.c | 385 ++++++++++++++++++++++++++++++++++++ 3 files changed, 396 insertions(+) create mode 100644 drivers/net/mctp/mctp-usb.c diff --git a/drivers/net/mctp/Kconfig b/drivers/net/mctp/Kconfig index 15860d6ac39f..cf325ab0b1ef 100644 --- a/drivers/net/mctp/Kconfig +++ b/drivers/net/mctp/Kconfig @@ -47,6 +47,16 @@ config MCTP_TRANSPORT_I3C A MCTP protocol network device is created for each I3C bus having a "mctp-controller" devicetree property. +config MCTP_TRANSPORT_USB + tristate "MCTP USB transport" + depends on USB + help + Provides a driver to access MCTP devices over USB transport, + defined by DMTF specification DSP0283. + + MCTP-over-USB interfaces are peer-to-peer, so each interface + represents a physical connection to one remote MCTP endpoint. + endmenu endif diff --git a/drivers/net/mctp/Makefile b/drivers/net/mctp/Makefile index e1cb99ced54a..c36006849a1e 100644 --- a/drivers/net/mctp/Makefile +++ b/drivers/net/mctp/Makefile @@ -1,3 +1,4 @@ obj-$(CONFIG_MCTP_SERIAL) += mctp-serial.o obj-$(CONFIG_MCTP_TRANSPORT_I2C) += mctp-i2c.o obj-$(CONFIG_MCTP_TRANSPORT_I3C) += mctp-i3c.o +obj-$(CONFIG_MCTP_TRANSPORT_USB) += mctp-usb.o diff --git a/drivers/net/mctp/mctp-usb.c b/drivers/net/mctp/mctp-usb.c new file mode 100644 index 000000000000..e8d4b01c3f34 --- /dev/null +++ b/drivers/net/mctp/mctp-usb.c @@ -0,0 +1,385 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * mctp-usb.c - MCTP-over-USB (DMTF DSP0283) transport binding driver. + * + * DSP0283 is available at: + * https://www.dmtf.org/sites/default/files/standards/documents/DSP0283_1.0.1.pdf + * + * Copyright (C) 2024-2025 Code Construct Pty Ltd + */ + +#include +#include +#include +#include + +#include +#include +#include + +#include + +struct mctp_usb { + struct usb_device *usbdev; + struct usb_interface *intf; + bool stopped; + + struct net_device *netdev; + + u8 ep_in; + u8 ep_out; + + struct urb *tx_urb; + struct urb *rx_urb; + + struct delayed_work rx_retry_work; +}; + +static void mctp_usb_out_complete(struct urb *urb) +{ + struct sk_buff *skb = urb->context; + struct net_device *netdev = skb->dev; + int status; + + status = urb->status; + + switch (status) { + case -ENOENT: + case -ECONNRESET: + case -ESHUTDOWN: + case -EPROTO: + dev_dstats_tx_dropped(netdev); + break; + case 0: + dev_dstats_tx_add(netdev, skb->len); + netif_wake_queue(netdev); + consume_skb(skb); + return; + default: + netdev_dbg(netdev, "unexpected tx urb status: %d\n", status); + dev_dstats_tx_dropped(netdev); + } + + kfree_skb(skb); +} + +static netdev_tx_t mctp_usb_start_xmit(struct sk_buff *skb, + struct net_device *dev) +{ + struct mctp_usb *mctp_usb = netdev_priv(dev); + struct mctp_usb_hdr *hdr; + unsigned int plen; + struct urb *urb; + int rc; + + plen = skb->len; + + if (plen + sizeof(*hdr) > MCTP_USB_XFER_SIZE) + goto err_drop; + + rc = skb_cow_head(skb, sizeof(*hdr)); + if (rc) + goto err_drop; + + hdr = skb_push(skb, sizeof(*hdr)); + if (!hdr) + goto err_drop; + + hdr->id = cpu_to_be16(MCTP_USB_DMTF_ID); + hdr->rsvd = 0; + hdr->len = plen + sizeof(*hdr); + + urb = mctp_usb->tx_urb; + + usb_fill_bulk_urb(urb, mctp_usb->usbdev, + usb_sndbulkpipe(mctp_usb->usbdev, mctp_usb->ep_out), + skb->data, skb->len, + mctp_usb_out_complete, skb); + + rc = usb_submit_urb(urb, GFP_ATOMIC); + if (rc) + goto err_drop; + else + netif_stop_queue(dev); + + return NETDEV_TX_OK; + +err_drop: + dev_dstats_tx_dropped(dev); + kfree_skb(skb); + return NETDEV_TX_OK; +} + +static void mctp_usb_in_complete(struct urb *urb); + +/* If we fail to queue an in urb atomically (either due to skb allocation or + * urb submission), we will schedule a rx queue in nonatomic context + * after a delay, specified in jiffies + */ +static const unsigned long RX_RETRY_DELAY = HZ / 4; + +static int mctp_usb_rx_queue(struct mctp_usb *mctp_usb, gfp_t gfp) +{ + struct sk_buff *skb; + int rc; + + skb = __netdev_alloc_skb(mctp_usb->netdev, MCTP_USB_XFER_SIZE, gfp); + if (!skb) { + rc = -ENOMEM; + goto err_retry; + } + + usb_fill_bulk_urb(mctp_usb->rx_urb, mctp_usb->usbdev, + usb_rcvbulkpipe(mctp_usb->usbdev, mctp_usb->ep_in), + skb->data, MCTP_USB_XFER_SIZE, + mctp_usb_in_complete, skb); + + rc = usb_submit_urb(mctp_usb->rx_urb, gfp); + if (rc) { + netdev_dbg(mctp_usb->netdev, "rx urb submit failure: %d\n", rc); + kfree_skb(skb); + if (rc == -ENOMEM) + goto err_retry; + } + + return rc; + +err_retry: + schedule_delayed_work(&mctp_usb->rx_retry_work, RX_RETRY_DELAY); + return rc; +} + +static void mctp_usb_in_complete(struct urb *urb) +{ + struct sk_buff *skb = urb->context; + struct net_device *netdev = skb->dev; + struct mctp_usb *mctp_usb = netdev_priv(netdev); + struct mctp_skb_cb *cb; + unsigned int len; + int status; + + status = urb->status; + + switch (status) { + case -ENOENT: + case -ECONNRESET: + case -ESHUTDOWN: + case -EPROTO: + kfree_skb(skb); + return; + case 0: + break; + default: + netdev_dbg(netdev, "unexpected rx urb status: %d\n", status); + kfree_skb(skb); + return; + } + + len = urb->actual_length; + __skb_put(skb, len); + + while (skb) { + struct sk_buff *skb2 = NULL; + struct mctp_usb_hdr *hdr; + u8 pkt_len; /* length of MCTP packet, no USB header */ + + hdr = skb_pull_data(skb, sizeof(*hdr)); + if (!hdr) + break; + + if (be16_to_cpu(hdr->id) != MCTP_USB_DMTF_ID) { + netdev_dbg(netdev, "rx: invalid id %04x\n", + be16_to_cpu(hdr->id)); + break; + } + + if (hdr->len < + sizeof(struct mctp_hdr) + sizeof(struct mctp_usb_hdr)) { + netdev_dbg(netdev, "rx: short packet (hdr) %d\n", + hdr->len); + break; + } + + /* we know we have at least sizeof(struct mctp_usb_hdr) here */ + pkt_len = hdr->len - sizeof(struct mctp_usb_hdr); + if (pkt_len > skb->len) { + netdev_dbg(netdev, + "rx: short packet (xfer) %d, actual %d\n", + hdr->len, skb->len); + break; + } + + if (pkt_len < skb->len) { + /* more packets may follow - clone to a new + * skb to use on the next iteration + */ + skb2 = skb_clone(skb, GFP_ATOMIC); + if (skb2) { + if (!skb_pull(skb2, pkt_len)) { + kfree_skb(skb2); + skb2 = NULL; + } + } + skb_trim(skb, pkt_len); + } + + dev_dstats_rx_add(netdev, skb->len); + + skb->protocol = htons(ETH_P_MCTP); + skb_reset_network_header(skb); + cb = __mctp_cb(skb); + cb->halen = 0; + netif_rx(skb); + + skb = skb2; + } + + if (skb) + kfree_skb(skb); + + mctp_usb_rx_queue(mctp_usb, GFP_ATOMIC); +} + +static void mctp_usb_rx_retry_work(struct work_struct *work) +{ + struct mctp_usb *mctp_usb = container_of(work, struct mctp_usb, + rx_retry_work.work); + + if (READ_ONCE(mctp_usb->stopped)) + return; + + mctp_usb_rx_queue(mctp_usb, GFP_KERNEL); +} + +static int mctp_usb_open(struct net_device *dev) +{ + struct mctp_usb *mctp_usb = netdev_priv(dev); + + WRITE_ONCE(mctp_usb->stopped, false); + + return mctp_usb_rx_queue(mctp_usb, GFP_KERNEL); +} + +static int mctp_usb_stop(struct net_device *dev) +{ + struct mctp_usb *mctp_usb = netdev_priv(dev); + + netif_stop_queue(dev); + + /* prevent RX submission retry */ + WRITE_ONCE(mctp_usb->stopped, true); + + usb_kill_urb(mctp_usb->rx_urb); + usb_kill_urb(mctp_usb->tx_urb); + + cancel_delayed_work_sync(&mctp_usb->rx_retry_work); + + return 0; +} + +static const struct net_device_ops mctp_usb_netdev_ops = { + .ndo_start_xmit = mctp_usb_start_xmit, + .ndo_open = mctp_usb_open, + .ndo_stop = mctp_usb_stop, +}; + +static void mctp_usb_netdev_setup(struct net_device *dev) +{ + dev->type = ARPHRD_MCTP; + + dev->mtu = MCTP_USB_MTU_MIN; + dev->min_mtu = MCTP_USB_MTU_MIN; + dev->max_mtu = MCTP_USB_MTU_MAX; + + dev->hard_header_len = sizeof(struct mctp_usb_hdr); + dev->tx_queue_len = DEFAULT_TX_QUEUE_LEN; + dev->flags = IFF_NOARP; + dev->netdev_ops = &mctp_usb_netdev_ops; + dev->pcpu_stat_type = NETDEV_PCPU_STAT_DSTATS; +} + +static int mctp_usb_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usb_endpoint_descriptor *ep_in, *ep_out; + struct usb_host_interface *iface_desc; + struct net_device *netdev; + struct mctp_usb *dev; + int rc; + + /* only one alternate */ + iface_desc = intf->cur_altsetting; + + rc = usb_find_common_endpoints(iface_desc, &ep_in, &ep_out, NULL, NULL); + if (rc) { + dev_err(&intf->dev, "invalid endpoints on device?\n"); + return rc; + } + + netdev = alloc_netdev(sizeof(*dev), "mctpusb%d", NET_NAME_ENUM, + mctp_usb_netdev_setup); + if (!netdev) + return -ENOMEM; + + SET_NETDEV_DEV(netdev, &intf->dev); + dev = netdev_priv(netdev); + dev->netdev = netdev; + dev->usbdev = usb_get_dev(interface_to_usbdev(intf)); + dev->intf = intf; + usb_set_intfdata(intf, dev); + + dev->ep_in = ep_in->bEndpointAddress; + dev->ep_out = ep_out->bEndpointAddress; + + dev->tx_urb = usb_alloc_urb(0, GFP_KERNEL); + dev->rx_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!dev->tx_urb || !dev->rx_urb) { + rc = -ENOMEM; + goto err_free_urbs; + } + + INIT_DELAYED_WORK(&dev->rx_retry_work, mctp_usb_rx_retry_work); + + rc = mctp_register_netdev(netdev, NULL, MCTP_PHYS_BINDING_USB); + if (rc) + goto err_free_urbs; + + return 0; + +err_free_urbs: + usb_free_urb(dev->tx_urb); + usb_free_urb(dev->rx_urb); + free_netdev(netdev); + return rc; +} + +static void mctp_usb_disconnect(struct usb_interface *intf) +{ + struct mctp_usb *dev = usb_get_intfdata(intf); + + mctp_unregister_netdev(dev->netdev); + usb_free_urb(dev->tx_urb); + usb_free_urb(dev->rx_urb); + usb_put_dev(dev->usbdev); + free_netdev(dev->netdev); +} + +static const struct usb_device_id mctp_usb_devices[] = { + { USB_INTERFACE_INFO(USB_CLASS_MCTP, 0x0, 0x1) }, + { 0 }, +}; + +MODULE_DEVICE_TABLE(usb, mctp_usb_devices); + +static struct usb_driver mctp_usb_driver = { + .name = "mctp-usb", + .id_table = mctp_usb_devices, + .probe = mctp_usb_probe, + .disconnect = mctp_usb_disconnect, +}; + +module_usb_driver(mctp_usb_driver) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Jeremy Kerr "); +MODULE_DESCRIPTION("MCTP USB transport"); -- 2.51.0 From e13b6da7045f997e1a5a5efd61d40e63c4fc20e8 Mon Sep 17 00:00:00 2001 From: Jason Wang Date: Tue, 18 Feb 2025 10:39:08 +0800 Subject: [PATCH 06/16] virtio-net: tweak for better TX performance in NAPI mode There are several issues existed in start_xmit(): - Transmitted packets need to be freed before sending a packet, this introduces delay and increases the average packets transmit time. This also increase the time that spent in holding the TX lock. - Notification is enabled after free_old_xmit_skbs() which will introduce unnecessary interrupts if TX notification happens on the same CPU that is doing the transmission now (actually, virtio-net driver are optimized for this case). So this patch tries to avoid those issues by not cleaning transmitted packets in start_xmit() when TX NAPI is enabled and disable notifications even more aggressively. Notification will be since the beginning of the start_xmit(). But we can't enable delayed notification after TX is stopped as we will lose the notifications. Instead, the delayed notification needs is enabled after the virtqueue is kicked for best performance. Performance numbers: 1) single queue 2 vcpus guest with pktgen_sample03_burst_single_flow.sh (burst 256) + testpmd (rxonly) on the host: - When pinning TX IRQ to pktgen VCPU: split virtqueue PPS were increased 55% from 6.89 Mpps to 10.7 Mpps and 32% TX interrupts were eliminated. Packed virtqueue PPS were increased 50% from 7.09 Mpps to 10.7 Mpps, 99% TX interrupts were eliminated. - When pinning TX IRQ to VCPU other than pktgen: split virtqueue PPS were increased 96% from 5.29 Mpps to 10.4 Mpps and 45% TX interrupts were eliminated; Packed virtqueue PPS were increased 78% from 6.12 Mpps to 10.9 Mpps and 99% TX interrupts were eliminated. 2) single queue 1 vcpu guest + vhost-net/TAP on the host: single session netperf from guest to host shows 82% improvement from 31Gb/s to 58Gb/s, %stddev were reduced from 34.5% to 1.9% and 88% of TX interrupts were eliminated. Signed-off-by: Jason Wang Acked-by: Michael S. Tsirkin Signed-off-by: David S. Miller --- drivers/net/virtio_net.c | 45 ++++++++++++++++++++++++++++------------ 1 file changed, 32 insertions(+), 13 deletions(-) diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index 7646ddd9bef7..ac26a6201c44 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -1088,11 +1088,10 @@ static bool is_xdp_raw_buffer_queue(struct virtnet_info *vi, int q) return false; } -static void check_sq_full_and_disable(struct virtnet_info *vi, - struct net_device *dev, - struct send_queue *sq) +static bool tx_may_stop(struct virtnet_info *vi, + struct net_device *dev, + struct send_queue *sq) { - bool use_napi = sq->napi.weight; int qnum; qnum = sq - vi->sq; @@ -1114,6 +1113,25 @@ static void check_sq_full_and_disable(struct virtnet_info *vi, u64_stats_update_begin(&sq->stats.syncp); u64_stats_inc(&sq->stats.stop); u64_stats_update_end(&sq->stats.syncp); + + return true; + } + + return false; +} + +static void check_sq_full_and_disable(struct virtnet_info *vi, + struct net_device *dev, + struct send_queue *sq) +{ + bool use_napi = sq->napi.weight; + int qnum; + + qnum = sq - vi->sq; + + if (tx_may_stop(vi, dev, sq)) { + struct netdev_queue *txq = netdev_get_tx_queue(dev, qnum); + if (use_napi) { if (unlikely(!virtqueue_enable_cb_delayed(sq->vq))) virtqueue_napi_schedule(&sq->napi, sq->vq); @@ -3253,15 +3271,10 @@ static netdev_tx_t start_xmit(struct sk_buff *skb, struct net_device *dev) bool use_napi = sq->napi.weight; bool kick; - /* Free up any pending old buffers before queueing new ones. */ - do { - if (use_napi) - virtqueue_disable_cb(sq->vq); - + if (!use_napi) free_old_xmit(sq, txq, false); - - } while (use_napi && !xmit_more && - unlikely(!virtqueue_enable_cb_delayed(sq->vq))); + else + virtqueue_disable_cb(sq->vq); /* timestamp packet in software */ skb_tx_timestamp(skb); @@ -3287,7 +3300,10 @@ static netdev_tx_t start_xmit(struct sk_buff *skb, struct net_device *dev) nf_reset_ct(skb); } - check_sq_full_and_disable(vi, dev, sq); + if (use_napi) + tx_may_stop(vi, dev, sq); + else + check_sq_full_and_disable(vi, dev,sq); kick = use_napi ? __netdev_tx_sent_queue(txq, skb->len, xmit_more) : !xmit_more || netif_xmit_stopped(txq); @@ -3299,6 +3315,9 @@ static netdev_tx_t start_xmit(struct sk_buff *skb, struct net_device *dev) } } + if (use_napi && kick && unlikely(!virtqueue_enable_cb_delayed(sq->vq))) + virtqueue_napi_schedule(&sq->napi, sq->vq); + return NETDEV_TX_OK; } -- 2.51.0 From 98f9928843331fc4e91b25d9969a9458c3f9c552 Mon Sep 17 00:00:00 2001 From: "Russell King (Oracle)" Date: Fri, 21 Feb 2025 11:38:20 +0000 Subject: [PATCH 07/16] net: stmmac: qcom-ethqos: use rgmii_clock() to set the link clock The link clock operates at twice the RGMII clock rate. Therefore, we can use the rgmii_clock() helper to set this clock rate. Signed-off-by: Russell King (Oracle) Reviewed-by: Vinod Koul Reviewed-by: Andrew Lunn Link: https://patch.msgid.link/E1tlRMK-004Vsx-Ss@rmk-PC.armlinux.org.uk Signed-off-by: Jakub Kicinski --- .../stmicro/stmmac/dwmac-qcom-ethqos.c | 23 ++++--------------- 1 file changed, 5 insertions(+), 18 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c index 192f270197c8..eafe637540b6 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c @@ -169,30 +169,17 @@ static void rgmii_dump(void *priv) rgmii_readl(ethqos, EMAC_SYSTEM_LOW_POWER_DEBUG)); } -/* Clock rates */ -#define RGMII_1000_NOM_CLK_FREQ (250 * 1000 * 1000UL) -#define RGMII_ID_MODE_100_LOW_SVS_CLK_FREQ (50 * 1000 * 1000UL) -#define RGMII_ID_MODE_10_LOW_SVS_CLK_FREQ (5 * 1000 * 1000UL) - static void ethqos_update_link_clk(struct qcom_ethqos *ethqos, int speed) { + long rate; + if (!phy_interface_mode_is_rgmii(ethqos->phy_mode)) return; - switch (speed) { - case SPEED_1000: - ethqos->link_clk_rate = RGMII_1000_NOM_CLK_FREQ; - break; - - case SPEED_100: - ethqos->link_clk_rate = RGMII_ID_MODE_100_LOW_SVS_CLK_FREQ; - break; - - case SPEED_10: - ethqos->link_clk_rate = RGMII_ID_MODE_10_LOW_SVS_CLK_FREQ; - break; - } + rate = rgmii_clock(speed); + if (rate > 0) + ethqos->link_clk_rate = rate * 2; clk_set_rate(ethqos->link_clk, ethqos->link_clk_rate); } -- 2.51.0 From 75bc3dab4e49b4daccb27ad6ce8ce2fcd253fc1b Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Fri, 21 Feb 2025 05:12:23 +0000 Subject: [PATCH 08/16] net-sysfs: restore behavior for not running devices modprobe dummy dumdummies=1 Old behavior : $ cat /sys/class/net/dummy0/carrier cat: /sys/class/net/dummy0/carrier: Invalid argument After blamed commit, an empty string is reported. $ cat /sys/class/net/dummy0/carrier $ In this commit, I restore the old behavior for carrier, speed and duplex attributes. Fixes: 79c61899b5ee ("net-sysfs: remove rtnl_trylock from device attributes") Signed-off-by: Eric Dumazet Reported-by: Marco Leogrande Reviewed-by: Antoine Tenart Link: https://patch.msgid.link/20250221051223.576726-1-edumazet@google.com Signed-off-by: Jakub Kicinski --- net/core/net-sysfs.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/net/core/net-sysfs.c b/net/core/net-sysfs.c index 3fe2c521e574..f61c1d829811 100644 --- a/net/core/net-sysfs.c +++ b/net/core/net-sysfs.c @@ -313,12 +313,13 @@ static ssize_t carrier_show(struct device *dev, struct device_attribute *attr, char *buf) { struct net_device *netdev = to_net_dev(dev); - int ret = -EINVAL; + int ret; ret = sysfs_rtnl_lock(&dev->kobj, &attr->attr, netdev); if (ret) return ret; + ret = -EINVAL; if (netif_running(netdev)) { /* Synchronize carrier state with link watch, * see also rtnl_getlink(). @@ -349,6 +350,7 @@ static ssize_t speed_show(struct device *dev, if (ret) return ret; + ret = -EINVAL; if (netif_running(netdev)) { struct ethtool_link_ksettings cmd; @@ -376,6 +378,7 @@ static ssize_t duplex_show(struct device *dev, if (ret) return ret; + ret = -EINVAL; if (netif_running(netdev)) { struct ethtool_link_ksettings cmd; -- 2.51.0 From 352bc4513ec3907db71cb5674fb93a76fc341ca9 Mon Sep 17 00:00:00 2001 From: Kunihiko Hayashi Date: Fri, 21 Feb 2025 14:18:18 +0900 Subject: [PATCH 09/16] net: stmmac: Correct usage of maximum queue number macros The maximum numbers of each Rx and Tx queues are defined by MTL_MAX_RX_QUEUES and MTL_MAX_TX_QUEUES respectively. There are some places where Rx and Tx are used in reverse. There is no issue when the Tx and Rx macros have the same value, but should correct usage of macros for maximum queue number to keep consistency and prevent unexpected mistakes. Reviewed-by: Russell King (Oracle) Reviewed-by: Huacai Chen Signed-off-by: Kunihiko Hayashi Link: https://patch.msgid.link/20250221051818.4163678-1-hayashi.kunihiko@socionext.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/stmicro/stmmac/common.h | 4 ++-- drivers/net/ethernet/stmicro/stmmac/stmmac.h | 7 +++---- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/common.h b/drivers/net/ethernet/stmicro/stmmac/common.h index 55053528e498..412b07e77945 100644 --- a/drivers/net/ethernet/stmicro/stmmac/common.h +++ b/drivers/net/ethernet/stmicro/stmmac/common.h @@ -101,8 +101,8 @@ struct stmmac_rxq_stats { /* Updates on each CPU protected by not allowing nested irqs. */ struct stmmac_pcpu_stats { struct u64_stats_sync syncp; - u64_stats_t rx_normal_irq_n[MTL_MAX_TX_QUEUES]; - u64_stats_t tx_normal_irq_n[MTL_MAX_RX_QUEUES]; + u64_stats_t rx_normal_irq_n[MTL_MAX_RX_QUEUES]; + u64_stats_t tx_normal_irq_n[MTL_MAX_TX_QUEUES]; }; /* Extra statistic and debug information exposed by ethtool */ diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac.h b/drivers/net/ethernet/stmicro/stmmac/stmmac.h index b403029b1aaa..b32187284607 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac.h +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac.h @@ -259,7 +259,7 @@ struct stmmac_priv { /* Frequently used values are kept adjacent for cache effect */ u32 tx_coal_frames[MTL_MAX_TX_QUEUES]; u32 tx_coal_timer[MTL_MAX_TX_QUEUES]; - u32 rx_coal_frames[MTL_MAX_TX_QUEUES]; + u32 rx_coal_frames[MTL_MAX_RX_QUEUES]; int hwts_tx_en; bool tx_path_in_lpi_mode; @@ -267,8 +267,7 @@ struct stmmac_priv { int sph; int sph_cap; u32 sarc_type; - - u32 rx_riwt[MTL_MAX_TX_QUEUES]; + u32 rx_riwt[MTL_MAX_RX_QUEUES]; int hwts_rx_en; void __iomem *ioaddr; @@ -344,7 +343,7 @@ struct stmmac_priv { char int_name_sfty[IFNAMSIZ + 10]; char int_name_sfty_ce[IFNAMSIZ + 10]; char int_name_sfty_ue[IFNAMSIZ + 10]; - char int_name_rx_irq[MTL_MAX_TX_QUEUES][IFNAMSIZ + 14]; + char int_name_rx_irq[MTL_MAX_RX_QUEUES][IFNAMSIZ + 14]; char int_name_tx_irq[MTL_MAX_TX_QUEUES][IFNAMSIZ + 18]; #ifdef CONFIG_DEBUG_FS -- 2.51.0 From 89ac4a59ca6d98091e8317bc5a342f38669e19e2 Mon Sep 17 00:00:00 2001 From: Nicolas Dichtel Date: Fri, 21 Feb 2025 12:07:28 +0100 Subject: [PATCH 10/16] skbuff: kill skb_flow_get_ports() Since commit a815bde56b15 ("net, bonding: Refactor bond_xmit_hash for use with xdp_buff"), this function is not used anymore. Signed-off-by: Nicolas Dichtel Reviewed-by: Simon Horman Link: https://patch.msgid.link/20250221110941.2041629-2-nicolas.dichtel@6wind.com Signed-off-by: Jakub Kicinski --- include/linux/skbuff.h | 6 ------ 1 file changed, 6 deletions(-) diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 0b4f1889500d..21644e9215e4 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -1536,12 +1536,6 @@ u32 __skb_get_poff(const struct sk_buff *skb, const void *data, __be32 __skb_flow_get_ports(const struct sk_buff *skb, int thoff, u8 ip_proto, const void *data, int hlen_proto); -static inline __be32 skb_flow_get_ports(const struct sk_buff *skb, - int thoff, u8 ip_proto) -{ - return __skb_flow_get_ports(skb, thoff, ip_proto, NULL, 0); -} - void skb_flow_dissector_init(struct flow_dissector *flow_dissector, const struct flow_dissector_key *key, unsigned int key_count); -- 2.51.0 From c52fd4f083cc634c57fc98fce36860e63f6bce2b Mon Sep 17 00:00:00 2001 From: Nicolas Dichtel Date: Fri, 21 Feb 2025 12:07:29 +0100 Subject: [PATCH 11/16] net: remove '__' from __skb_flow_get_ports() Only one version of skb_flow_get_ports() exists after the previous commit, so let's remove the useless '__'. Suggested-by: Simon Horman Signed-off-by: Nicolas Dichtel Link: https://patch.msgid.link/20250221110941.2041629-3-nicolas.dichtel@6wind.com Signed-off-by: Jakub Kicinski --- drivers/net/bonding/bond_main.c | 2 +- include/linux/skbuff.h | 4 ++-- net/core/flow_dissector.c | 10 +++++----- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index f6d0628a36d9..7d716e90a84c 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -4214,7 +4214,7 @@ static bool bond_flow_ip(struct sk_buff *skb, struct flow_keys *fk, const void * } if (l34 && *ip_proto >= 0) - fk->ports.ports = __skb_flow_get_ports(skb, *nhoff, *ip_proto, data, hlen); + fk->ports.ports = skb_flow_get_ports(skb, *nhoff, *ip_proto, data, hlen); return true; } diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 21644e9215e4..f2bb8473d99a 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -1533,8 +1533,8 @@ void __skb_get_hash_net(const struct net *net, struct sk_buff *skb); u32 skb_get_poff(const struct sk_buff *skb); u32 __skb_get_poff(const struct sk_buff *skb, const void *data, const struct flow_keys_basic *keys, int hlen); -__be32 __skb_flow_get_ports(const struct sk_buff *skb, int thoff, u8 ip_proto, - const void *data, int hlen_proto); +__be32 skb_flow_get_ports(const struct sk_buff *skb, int thoff, u8 ip_proto, + const void *data, int hlen_proto); void skb_flow_dissector_init(struct flow_dissector *flow_dissector, const struct flow_dissector_key *key, diff --git a/net/core/flow_dissector.c b/net/core/flow_dissector.c index 9cd8de6bebb5..1b61bb25ba0e 100644 --- a/net/core/flow_dissector.c +++ b/net/core/flow_dissector.c @@ -106,7 +106,7 @@ int flow_dissector_bpf_prog_attach_check(struct net *net, #endif /* CONFIG_BPF_SYSCALL */ /** - * __skb_flow_get_ports - extract the upper layer ports and return them + * skb_flow_get_ports - extract the upper layer ports and return them * @skb: sk_buff to extract the ports from * @thoff: transport header offset * @ip_proto: protocol for which to get port offset @@ -116,8 +116,8 @@ int flow_dissector_bpf_prog_attach_check(struct net *net, * The function will try to retrieve the ports at offset thoff + poff where poff * is the protocol port offset returned from proto_ports_offset */ -__be32 __skb_flow_get_ports(const struct sk_buff *skb, int thoff, u8 ip_proto, - const void *data, int hlen) +__be32 skb_flow_get_ports(const struct sk_buff *skb, int thoff, u8 ip_proto, + const void *data, int hlen) { int poff = proto_ports_offset(ip_proto); @@ -137,7 +137,7 @@ __be32 __skb_flow_get_ports(const struct sk_buff *skb, int thoff, u8 ip_proto, return 0; } -EXPORT_SYMBOL(__skb_flow_get_ports); +EXPORT_SYMBOL(skb_flow_get_ports); static bool icmp_has_id(u8 type) { @@ -870,7 +870,7 @@ __skb_flow_dissect_ports(const struct sk_buff *skb, if (!key_ports && !key_ports_range) return; - ports = __skb_flow_get_ports(skb, nhoff, ip_proto, data, hlen); + ports = skb_flow_get_ports(skb, nhoff, ip_proto, data, hlen); if (key_ports) key_ports->ports = ports; -- 2.51.0 From 171fd7cb153c4614d4132fbb135ef1c0855e330a Mon Sep 17 00:00:00 2001 From: "Russell King (Oracle)" Date: Fri, 21 Feb 2025 14:15:12 +0000 Subject: [PATCH 12/16] net: stmmac: thead: use rgmii_clock() for RGMII clock rate Switch to using rgmii_clock() to get the RGMII TXC rate, and calculate the divisor from the parent clock rate and the rate indicated by rgmii_clock(). Signed-off-by: Russell King (Oracle) Reviewed-by: Andrew Lunn Tested-by: Drew Fustini Link: https://patch.msgid.link/E1tlTo8-004W3a-CO@rmk-PC.armlinux.org.uk Signed-off-by: Jakub Kicinski --- .../net/ethernet/stmicro/stmmac/dwmac-thead.c | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-thead.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-thead.c index ddb1d8aba321..f16fa341aadb 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-thead.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-thead.c @@ -109,6 +109,7 @@ static void thead_dwmac_fix_speed(void *priv, int speed, unsigned int mode) struct plat_stmmacenet_data *plat; struct thead_dwmac *dwmac = priv; unsigned long rate; + long tx_rate; u32 div, reg; plat = dwmac->plat; @@ -131,21 +132,14 @@ static void thead_dwmac_fix_speed(void *priv, int speed, unsigned int mode) writel(0, dwmac->apb_base + GMAC_PLLCLK_DIV); - switch (speed) { - case SPEED_1000: - div = rate / GMAC_GMII_RGMII_RATE; - break; - case SPEED_100: - div = rate / GMAC_MII_RATE; - break; - case SPEED_10: - div = rate * 10 / GMAC_MII_RATE; - break; - default: + tx_rate = rgmii_clock(speed); + if (tx_rate < 0) { dev_err(dwmac->dev, "invalid speed %d\n", speed); return; } + div = rate / tx_rate; + reg = FIELD_PREP(GMAC_PLLCLK_DIV_EN, 1) | FIELD_PREP(GMAC_PLLCLK_DIV_NUM, div); writel(reg, dwmac->apb_base + GMAC_PLLCLK_DIV); -- 2.51.0 From 8bfff0481d917ef92bfad607839acb7edb5e0b0d Mon Sep 17 00:00:00 2001 From: "Russell King (Oracle)" Date: Fri, 21 Feb 2025 14:15:17 +0000 Subject: [PATCH 13/16] net: stmmac: thead: ensure divisor gives proper rate thead was checking that the stmmac_clk rate was a multiple of the RGMII rates for 1G and 100M, but didn't check for 10M. Rather than use this with hard-coded speeds, check that the calculated divisor gives the required rate by multplying the transmit clock rate back up to the stmmac clock rate and checking that it agrees. Signed-off-by: Russell King (Oracle) Reviewed-by: Andrew Lunn Tested-by: Drew Fustini Link: https://patch.msgid.link/E1tlToD-004W3g-HB@rmk-PC.armlinux.org.uk Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/stmicro/stmmac/dwmac-thead.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-thead.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-thead.c index f16fa341aadb..f9f2bd65959f 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-thead.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-thead.c @@ -45,9 +45,6 @@ #define TXCLK_DIR_OUTPUT FIELD_PREP(TXCLK_DIR_MASK, 0) #define TXCLK_DIR_INPUT FIELD_PREP(TXCLK_DIR_MASK, 1) -#define GMAC_GMII_RGMII_RATE 125000000 -#define GMAC_MII_RATE 25000000 - struct thead_dwmac { struct plat_stmmacenet_data *plat; void __iomem *apb_base; @@ -124,11 +121,6 @@ static void thead_dwmac_fix_speed(void *priv, int speed, unsigned int mode) case PHY_INTERFACE_MODE_RGMII_RXID: case PHY_INTERFACE_MODE_RGMII_TXID: rate = clk_get_rate(plat->stmmac_clk); - if (!rate || rate % GMAC_GMII_RGMII_RATE != 0 || - rate % GMAC_MII_RATE != 0) { - dev_err(dwmac->dev, "invalid gmac rate %ld\n", rate); - return; - } writel(0, dwmac->apb_base + GMAC_PLLCLK_DIV); @@ -139,6 +131,10 @@ static void thead_dwmac_fix_speed(void *priv, int speed, unsigned int mode) } div = rate / tx_rate; + if (rate != tx_rate * div) { + dev_err(dwmac->dev, "invalid gmac rate %lu\n", rate); + return; + } reg = FIELD_PREP(GMAC_PLLCLK_DIV_EN, 1) | FIELD_PREP(GMAC_PLLCLK_DIV_NUM, div); -- 2.51.0 From 7183877d6853801258b7a8d3b51b415982e5097e Mon Sep 17 00:00:00 2001 From: Breno Leitao Date: Fri, 21 Feb 2025 09:51:27 -0800 Subject: [PATCH 14/16] net: Remove shadow variable in netdev_run_todo() Fix a shadow variable warning in net/core/dev.c when compiled with CONFIG_LOCKDEP enabled. The warning occurs because 'dev' is redeclared inside the while loop, shadowing the outer scope declaration. net/core/dev.c:11211:22: warning: declaration shadows a local variable [-Wshadow] struct net_device *dev = list_first_entry(&unlink_list, net/core/dev.c:11202:21: note: previous declaration is here struct net_device *dev, *tmp; Remove the redundant declaration since the variable is already defined in the outer scope and will be overwritten in the subsequent list_for_each_entry_safe() loop anyway. Signed-off-by: Breno Leitao Reviewed-by: Eric Dumazet Link: https://patch.msgid.link/20250221-netcons_fix_shadow-v1-1-dee20c8658dd@debian.org Signed-off-by: Jakub Kicinski --- net/core/dev.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/net/core/dev.c b/net/core/dev.c index 8c7ee7ada6a3..3f525278a871 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -11209,9 +11209,8 @@ void netdev_run_todo(void) list_replace_init(&net_unlink_list, &unlink_list); while (!list_empty(&unlink_list)) { - struct net_device *dev = list_first_entry(&unlink_list, - struct net_device, - unlink_list); + dev = list_first_entry(&unlink_list, struct net_device, + unlink_list); list_del_init(&dev->unlink_list); dev->nested_level = dev->lower_level - 1; } -- 2.51.0 From 6538c8ca8ee182a8b8233e0e57b5bffd8318d60c Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Mon, 24 Feb 2025 00:36:11 +0100 Subject: [PATCH 15/16] net: ethernet: renesas: rcar_gen4_ptp: Remove bool conversion MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Remove the unnecessary bool conversion and simplify the code. Signed-off-by: Thorsten Blum Reviewed-by: Geert Uytterhoeven Reviewed-by: Niklas Söderlund Link: https://patch.msgid.link/20250223233613.100518-2-thorsten.blum@linux.dev Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/renesas/rcar_gen4_ptp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/renesas/rcar_gen4_ptp.c b/drivers/net/ethernet/renesas/rcar_gen4_ptp.c index 72e7fcc56693..4c3e8cc5046f 100644 --- a/drivers/net/ethernet/renesas/rcar_gen4_ptp.c +++ b/drivers/net/ethernet/renesas/rcar_gen4_ptp.c @@ -29,8 +29,8 @@ static const struct rcar_gen4_ptp_reg_offset gen4_offs = { static int rcar_gen4_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm) { struct rcar_gen4_ptp_private *ptp_priv = ptp_to_priv(ptp); - bool neg_adj = scaled_ppm < 0 ? true : false; s64 addend = ptp_priv->default_addend; + bool neg_adj = scaled_ppm < 0; s64 diff; if (neg_adj) -- 2.51.0 From 2e5af6b2ae85328051f2ecb1b7fe64cdd835c8e9 Mon Sep 17 00:00:00 2001 From: Jiawen Wu Date: Fri, 21 Feb 2025 14:57:17 +0800 Subject: [PATCH 16/16] net: txgbe: Add basic support for new AML devices There is a new 40/25/10 Gigabit Ethernet device. To support basic functions, PHYLINK is temporarily skipped as it is intended to implement these configurations in the firmware. And the associated link IRQ is also skipped. And Implement the new SW-FW interaction interface, which use 64 Byte message buffer. Signed-off-by: Jiawen Wu Link: https://patch.msgid.link/20250221065718.197544-1-jiawenwu@trustnetic.com Signed-off-by: Jakub Kicinski --- .../net/ethernet/wangxun/libwx/wx_ethtool.c | 44 +++- drivers/net/ethernet/wangxun/libwx/wx_hw.c | 213 +++++++++++++++--- drivers/net/ethernet/wangxun/libwx/wx_lib.c | 25 +- drivers/net/ethernet/wangxun/libwx/wx_type.h | 29 ++- drivers/net/ethernet/wangxun/txgbe/txgbe_hw.c | 6 + .../net/ethernet/wangxun/txgbe/txgbe_irq.c | 7 + .../net/ethernet/wangxun/txgbe/txgbe_main.c | 43 +++- .../net/ethernet/wangxun/txgbe/txgbe_phy.c | 6 + .../net/ethernet/wangxun/txgbe/txgbe_type.h | 14 ++ 9 files changed, 333 insertions(+), 54 deletions(-) diff --git a/drivers/net/ethernet/wangxun/libwx/wx_ethtool.c b/drivers/net/ethernet/wangxun/libwx/wx_ethtool.c index 28f982fbc64c..6d3b57233a39 100644 --- a/drivers/net/ethernet/wangxun/libwx/wx_ethtool.c +++ b/drivers/net/ethernet/wangxun/libwx/wx_ethtool.c @@ -219,6 +219,9 @@ int wx_nway_reset(struct net_device *netdev) { struct wx *wx = netdev_priv(netdev); + if (wx->mac.type == wx_mac_aml) + return -EOPNOTSUPP; + return phylink_ethtool_nway_reset(wx->phylink); } EXPORT_SYMBOL(wx_nway_reset); @@ -228,6 +231,9 @@ int wx_get_link_ksettings(struct net_device *netdev, { struct wx *wx = netdev_priv(netdev); + if (wx->mac.type == wx_mac_aml) + return -EOPNOTSUPP; + return phylink_ethtool_ksettings_get(wx->phylink, cmd); } EXPORT_SYMBOL(wx_get_link_ksettings); @@ -237,6 +243,9 @@ int wx_set_link_ksettings(struct net_device *netdev, { struct wx *wx = netdev_priv(netdev); + if (wx->mac.type == wx_mac_aml) + return -EOPNOTSUPP; + return phylink_ethtool_ksettings_set(wx->phylink, cmd); } EXPORT_SYMBOL(wx_set_link_ksettings); @@ -246,6 +255,9 @@ void wx_get_pauseparam(struct net_device *netdev, { struct wx *wx = netdev_priv(netdev); + if (wx->mac.type == wx_mac_aml) + return; + phylink_ethtool_get_pauseparam(wx->phylink, pause); } EXPORT_SYMBOL(wx_get_pauseparam); @@ -255,6 +267,9 @@ int wx_set_pauseparam(struct net_device *netdev, { struct wx *wx = netdev_priv(netdev); + if (wx->mac.type == wx_mac_aml) + return -EOPNOTSUPP; + return phylink_ethtool_set_pauseparam(wx->phylink, pause); } EXPORT_SYMBOL(wx_set_pauseparam); @@ -325,10 +340,17 @@ int wx_set_coalesce(struct net_device *netdev, if (ec->tx_max_coalesced_frames_irq) wx->tx_work_limit = ec->tx_max_coalesced_frames_irq; - if (wx->mac.type == wx_mac_sp) + switch (wx->mac.type) { + case wx_mac_sp: max_eitr = WX_SP_MAX_EITR; - else + break; + case wx_mac_aml: + max_eitr = WX_AML_MAX_EITR; + break; + default: max_eitr = WX_EM_MAX_EITR; + break; + } if ((ec->rx_coalesce_usecs > (max_eitr >> 2)) || (ec->tx_coalesce_usecs > (max_eitr >> 2))) @@ -350,10 +372,15 @@ int wx_set_coalesce(struct net_device *netdev, wx->tx_itr_setting = ec->tx_coalesce_usecs; if (wx->tx_itr_setting == 1) { - if (wx->mac.type == wx_mac_sp) + switch (wx->mac.type) { + case wx_mac_sp: + case wx_mac_aml: tx_itr_param = WX_12K_ITR; - else + break; + default: tx_itr_param = WX_20K_ITR; + break; + } } else { tx_itr_param = wx->tx_itr_setting; } @@ -386,10 +413,15 @@ static unsigned int wx_max_channels(struct wx *wx) max_combined = 1; } else { /* support up to max allowed queues with RSS */ - if (wx->mac.type == wx_mac_sp) + switch (wx->mac.type) { + case wx_mac_sp: + case wx_mac_aml: max_combined = 63; - else + break; + default: max_combined = 8; + break; + } } return max_combined; diff --git a/drivers/net/ethernet/wangxun/libwx/wx_hw.c b/drivers/net/ethernet/wangxun/libwx/wx_hw.c index 907d13ade404..b5f35b187077 100644 --- a/drivers/net/ethernet/wangxun/libwx/wx_hw.c +++ b/drivers/net/ethernet/wangxun/libwx/wx_hw.c @@ -112,10 +112,15 @@ static void wx_intr_disable(struct wx *wx, u64 qmask) if (mask) wr32(wx, WX_PX_IMS(0), mask); - if (wx->mac.type == wx_mac_sp) { + switch (wx->mac.type) { + case wx_mac_sp: + case wx_mac_aml: mask = (qmask >> 32); if (mask) wr32(wx, WX_PX_IMS(1), mask); + break; + default: + break; } } @@ -126,10 +131,16 @@ void wx_intr_enable(struct wx *wx, u64 qmask) mask = (qmask & U32_MAX); if (mask) wr32(wx, WX_PX_IMC(0), mask); - if (wx->mac.type == wx_mac_sp) { + + switch (wx->mac.type) { + case wx_mac_sp: + case wx_mac_aml: mask = (qmask >> 32); if (mask) wr32(wx, WX_PX_IMC(1), mask); + break; + default: + break; } } EXPORT_SYMBOL(wx_intr_enable); @@ -278,22 +289,8 @@ static int wx_acquire_sw_sync(struct wx *wx, u32 mask) return ret; } -/** - * wx_host_interface_command - Issue command to manageability block - * @wx: pointer to the HW structure - * @buffer: contains the command to write and where the return status will - * be placed - * @length: length of buffer, must be multiple of 4 bytes - * @timeout: time in ms to wait for command completion - * @return_data: read and return data from the buffer (true) or not (false) - * Needed because FW structures are big endian and decoding of - * these fields can be 8 bit or 16 bit based on command. Decoding - * is not easily understood without making a table of commands. - * So we will leave this up to the caller to read back the data - * in these cases. - **/ -int wx_host_interface_command(struct wx *wx, u32 *buffer, - u32 length, u32 timeout, bool return_data) +static int wx_host_interface_command_s(struct wx *wx, u32 *buffer, + u32 length, u32 timeout, bool return_data) { u32 hdr_size = sizeof(struct wx_hic_hdr); u32 hicr, i, bi, buf[64] = {}; @@ -301,22 +298,10 @@ int wx_host_interface_command(struct wx *wx, u32 *buffer, u32 dword_len; u16 buf_len; - if (length == 0 || length > WX_HI_MAX_BLOCK_BYTE_LENGTH) { - wx_err(wx, "Buffer length failure buffersize=%d.\n", length); - return -EINVAL; - } - status = wx_acquire_sw_sync(wx, WX_MNG_SWFW_SYNC_SW_MB); if (status != 0) return status; - /* Calculate length in DWORDs. We must be DWORD aligned */ - if ((length % (sizeof(u32))) != 0) { - wx_err(wx, "Buffer length failure, not aligned to dword"); - status = -EINVAL; - goto rel_out; - } - dword_len = length >> 2; /* The device driver writes the relevant command block @@ -391,6 +376,139 @@ rel_out: wx_release_sw_sync(wx, WX_MNG_SWFW_SYNC_SW_MB); return status; } + +static bool wx_poll_fw_reply(struct wx *wx, u32 *buffer, u8 send_cmd) +{ + u32 dword_len = sizeof(struct wx_hic_hdr) >> 2; + struct wx_hic_hdr *recv_hdr; + u32 i; + + /* read hdr */ + for (i = 0; i < dword_len; i++) { + buffer[i] = rd32a(wx, WX_FW2SW_MBOX, i); + le32_to_cpus(&buffer[i]); + } + + /* check hdr */ + recv_hdr = (struct wx_hic_hdr *)buffer; + if (recv_hdr->cmd == send_cmd && + recv_hdr->index == wx->swfw_index) + return true; + + return false; +} + +static int wx_host_interface_command_r(struct wx *wx, u32 *buffer, + u32 length, u32 timeout, bool return_data) +{ + struct wx_hic_hdr *hdr = (struct wx_hic_hdr *)buffer; + u32 hdr_size = sizeof(struct wx_hic_hdr); + bool busy, reply; + u32 dword_len; + u16 buf_len; + int err = 0; + u8 send_cmd; + u32 i; + + /* wait to get lock */ + might_sleep(); + err = read_poll_timeout(test_and_set_bit, busy, !busy, 1000, timeout * 1000, + false, WX_STATE_SWFW_BUSY, wx->state); + if (err) + return err; + + /* index to unique seq id for each mbox message */ + hdr->index = wx->swfw_index; + send_cmd = hdr->cmd; + + dword_len = length >> 2; + /* write data to SW-FW mbox array */ + for (i = 0; i < dword_len; i++) { + wr32a(wx, WX_SW2FW_MBOX, i, (__force u32)cpu_to_le32(buffer[i])); + /* write flush */ + rd32a(wx, WX_SW2FW_MBOX, i); + } + + /* generate interrupt to notify FW */ + wr32m(wx, WX_SW2FW_MBOX_CMD, WX_SW2FW_MBOX_CMD_VLD, 0); + wr32m(wx, WX_SW2FW_MBOX_CMD, WX_SW2FW_MBOX_CMD_VLD, WX_SW2FW_MBOX_CMD_VLD); + + /* polling reply from FW */ + err = read_poll_timeout(wx_poll_fw_reply, reply, reply, 1000, 50000, + true, wx, buffer, send_cmd); + if (err) { + wx_err(wx, "Polling from FW messages timeout, cmd: 0x%x, index: %d\n", + send_cmd, wx->swfw_index); + goto rel_out; + } + + /* expect no reply from FW then return */ + if (!return_data) + goto rel_out; + + /* If there is any thing in data position pull it in */ + buf_len = hdr->buf_len; + if (buf_len == 0) + goto rel_out; + + if (length < buf_len + hdr_size) { + wx_err(wx, "Buffer not large enough for reply message.\n"); + err = -EFAULT; + goto rel_out; + } + + /* Calculate length in DWORDs, add 3 for odd lengths */ + dword_len = (buf_len + 3) >> 2; + for (i = hdr_size >> 2; i <= dword_len; i++) { + buffer[i] = rd32a(wx, WX_FW2SW_MBOX, i); + le32_to_cpus(&buffer[i]); + } + +rel_out: + /* index++, index replace wx_hic_hdr.checksum */ + if (wx->swfw_index == WX_HIC_HDR_INDEX_MAX) + wx->swfw_index = 0; + else + wx->swfw_index++; + + clear_bit(WX_STATE_SWFW_BUSY, wx->state); + return err; +} + +/** + * wx_host_interface_command - Issue command to manageability block + * @wx: pointer to the HW structure + * @buffer: contains the command to write and where the return status will + * be placed + * @length: length of buffer, must be multiple of 4 bytes + * @timeout: time in ms to wait for command completion + * @return_data: read and return data from the buffer (true) or not (false) + * Needed because FW structures are big endian and decoding of + * these fields can be 8 bit or 16 bit based on command. Decoding + * is not easily understood without making a table of commands. + * So we will leave this up to the caller to read back the data + * in these cases. + **/ +int wx_host_interface_command(struct wx *wx, u32 *buffer, + u32 length, u32 timeout, bool return_data) +{ + if (length == 0 || length > WX_HI_MAX_BLOCK_BYTE_LENGTH) { + wx_err(wx, "Buffer length failure buffersize=%d.\n", length); + return -EINVAL; + } + + /* Calculate length in DWORDs. We must be DWORD aligned */ + if ((length % (sizeof(u32))) != 0) { + wx_err(wx, "Buffer length failure, not aligned to dword"); + return -EINVAL; + } + + if (test_bit(WX_FLAG_SWFW_RING, wx->flags)) + return wx_host_interface_command_r(wx, buffer, length, + timeout, return_data); + + return wx_host_interface_command_s(wx, buffer, length, timeout, return_data); +} EXPORT_SYMBOL(wx_host_interface_command); int wx_set_pps(struct wx *wx, bool enable, u64 nsec, u64 cycles) @@ -442,7 +560,10 @@ static int wx_read_ee_hostif_data(struct wx *wx, u16 offset, u16 *data) if (status != 0) return status; - *data = (u16)rd32a(wx, WX_MNG_MBOX, FW_NVM_DATA_OFFSET); + if (!test_bit(WX_FLAG_SWFW_RING, wx->flags)) + *data = (u16)rd32a(wx, WX_MNG_MBOX, FW_NVM_DATA_OFFSET); + else + *data = (u16)rd32a(wx, WX_FW2SW_MBOX, FW_NVM_DATA_OFFSET); return status; } @@ -486,6 +607,7 @@ int wx_read_ee_hostif_buffer(struct wx *wx, u16 words_to_read; u32 value = 0; int status; + u32 mbox; u32 i; /* Take semaphore for the entire operation. */ @@ -518,8 +640,12 @@ int wx_read_ee_hostif_buffer(struct wx *wx, goto out; } + if (!test_bit(WX_FLAG_SWFW_RING, wx->flags)) + mbox = WX_MNG_MBOX; + else + mbox = WX_FW2SW_MBOX; for (i = 0; i < words_to_read; i++) { - u32 reg = WX_MNG_MBOX + (FW_NVM_DATA_OFFSET << 2) + 2 * i; + u32 reg = mbox + (FW_NVM_DATA_OFFSET << 2) + 2 * i; value = rd32(wx, reg); data[current_word] = (u16)(value & 0xffff); @@ -569,12 +695,17 @@ void wx_init_eeprom_params(struct wx *wx) } } - if (wx->mac.type == wx_mac_sp) { + switch (wx->mac.type) { + case wx_mac_sp: + case wx_mac_aml: if (wx_read_ee_hostif(wx, WX_SW_REGION_PTR, &data)) { wx_err(wx, "NVM Read Error\n"); return; } data = data >> 1; + break; + default: + break; } eeprom->sw_region_offset = data; @@ -635,8 +766,15 @@ static int wx_set_rar(struct wx *wx, u32 index, u8 *addr, u64 pools, /* setup VMDq pool mapping */ wr32(wx, WX_PSR_MAC_SWC_VM_L, pools & 0xFFFFFFFF); - if (wx->mac.type == wx_mac_sp) + + switch (wx->mac.type) { + case wx_mac_sp: + case wx_mac_aml: wr32(wx, WX_PSR_MAC_SWC_VM_H, pools >> 32); + break; + default: + break; + } /* HW expects these in little endian so we reverse the byte * order from network order (big endian) to little endian @@ -774,9 +912,14 @@ void wx_init_rx_addrs(struct wx *wx) wx_set_rar(wx, 0, wx->mac.addr, 0, WX_PSR_MAC_SWC_AD_H_AV); - if (wx->mac.type == wx_mac_sp) { + switch (wx->mac.type) { + case wx_mac_sp: + case wx_mac_aml: /* clear VMDq pool/queue selection for RAR 0 */ wx_clear_vmdq(wx, 0, WX_CLEAR_VMDQ_ALL); + break; + default: + break; } } diff --git a/drivers/net/ethernet/wangxun/libwx/wx_lib.c b/drivers/net/ethernet/wangxun/libwx/wx_lib.c index 42f90802870b..895f3c96a678 100644 --- a/drivers/net/ethernet/wangxun/libwx/wx_lib.c +++ b/drivers/net/ethernet/wangxun/libwx/wx_lib.c @@ -1823,10 +1823,16 @@ static int wx_alloc_q_vector(struct wx *wx, /* initialize pointer to rings */ ring = q_vector->ring; - if (wx->mac.type == wx_mac_sp) + switch (wx->mac.type) { + case wx_mac_sp: + case wx_mac_aml: default_itr = WX_12K_ITR; - else + break; + default: default_itr = WX_7K_ITR; + break; + } + /* initialize ITR */ if (txr_count && !rxr_count) /* tx only vector */ @@ -2182,10 +2188,17 @@ void wx_write_eitr(struct wx_q_vector *q_vector) int v_idx = q_vector->v_idx; u32 itr_reg; - if (wx->mac.type == wx_mac_sp) + switch (wx->mac.type) { + case wx_mac_sp: itr_reg = q_vector->itr & WX_SP_MAX_EITR; - else + break; + case wx_mac_aml: + itr_reg = (q_vector->itr >> 3) & WX_AML_MAX_EITR; + break; + default: itr_reg = q_vector->itr & WX_EM_MAX_EITR; + break; + } itr_reg |= WX_PX_ITR_CNT_WDIS; @@ -2761,7 +2774,7 @@ int wx_set_features(struct net_device *netdev, netdev_features_t features) netdev->features = features; - if (wx->mac.type == wx_mac_sp && changed & NETIF_F_HW_VLAN_CTAG_RX) + if (changed & NETIF_F_HW_VLAN_CTAG_RX && wx->do_reset) wx->do_reset(netdev); else if (changed & (NETIF_F_HW_VLAN_CTAG_RX | NETIF_F_HW_VLAN_CTAG_FILTER)) wx_set_rx_mode(netdev); @@ -2793,7 +2806,7 @@ int wx_set_features(struct net_device *netdev, netdev_features_t features) break; } - if (need_reset) + if (need_reset && wx->do_reset) wx->do_reset(netdev); return 0; diff --git a/drivers/net/ethernet/wangxun/libwx/wx_type.h b/drivers/net/ethernet/wangxun/libwx/wx_type.h index db446e690dc7..f79746ac6aca 100644 --- a/drivers/net/ethernet/wangxun/libwx/wx_type.h +++ b/drivers/net/ethernet/wangxun/libwx/wx_type.h @@ -309,6 +309,10 @@ #define WX_MNG_MBOX_CTL_FWRDY BIT(2) #define WX_MNG_BMC2OS_CNT 0x1E090 #define WX_MNG_OS2BMC_CNT 0x1E094 +#define WX_SW2FW_MBOX_CMD 0x1E0A0 +#define WX_SW2FW_MBOX_CMD_VLD BIT(31) +#define WX_SW2FW_MBOX 0x1E200 +#define WX_FW2SW_MBOX 0x1E300 /************************************* ETH MAC *****************************/ #define WX_MAC_TX_CFG 0x11000 @@ -372,6 +376,7 @@ enum WX_MSCA_CMD_value { #define WX_12K_ITR 336 #define WX_20K_ITR 200 #define WX_SP_MAX_EITR 0x00000FF8U +#define WX_AML_MAX_EITR 0x00000FFFU #define WX_EM_MAX_EITR 0x00007FFCU /* transmit DMA Registers */ @@ -415,6 +420,7 @@ enum WX_MSCA_CMD_value { /****************** Manageablility Host Interface defines ********************/ #define WX_HI_MAX_BLOCK_BYTE_LENGTH 256 /* Num of bytes in range */ #define WX_HI_COMMAND_TIMEOUT 1000 /* Process HI command limit */ +#define WX_HIC_HDR_INDEX_MAX 255 #define FW_READ_SHADOW_RAM_CMD 0x31 #define FW_READ_SHADOW_RAM_LEN 0x6 @@ -711,21 +717,30 @@ struct wx_hic_hdr { u8 cmd_resv; u8 ret_status; } cmd_or_resp; - u8 checksum; + union { + u8 checksum; + u8 index; + }; }; struct wx_hic_hdr2_req { u8 cmd; u8 buf_lenh; u8 buf_lenl; - u8 checksum; + union { + u8 checksum; + u8 index; + }; }; struct wx_hic_hdr2_rsp { u8 cmd; u8 buf_lenl; u8 buf_lenh_status; /* 7-5: high bits of buf_len, 4-0: status */ - u8 checksum; + union { + u8 checksum; + u8 index; + }; }; union wx_hic_hdr2 { @@ -773,7 +788,8 @@ struct wx_thermal_sensor_data { enum wx_mac_type { wx_mac_unknown = 0, wx_mac_sp, - wx_mac_em + wx_mac_em, + wx_mac_aml, }; enum sp_media_type { @@ -1085,12 +1101,14 @@ struct wx_hw_stats { enum wx_state { WX_STATE_RESETTING, + WX_STATE_SWFW_BUSY, WX_STATE_PTP_RUNNING, WX_STATE_PTP_TX_IN_PROGRESS, - WX_STATE_NBITS, /* must be last */ + WX_STATE_NBITS /* must be last */ }; enum wx_pf_flags { + WX_FLAG_SWFW_RING, WX_FLAG_FDIR_CAPABLE, WX_FLAG_FDIR_HASH, WX_FLAG_FDIR_PERFECT, @@ -1130,6 +1148,7 @@ struct wx { char eeprom_id[32]; char *driver_name; enum wx_reset_type reset_type; + u8 swfw_index; /* PHY stuff */ unsigned int link; diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_hw.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_hw.c index cd1372da92a9..4b9921b7bb11 100644 --- a/drivers/net/ethernet/wangxun/txgbe/txgbe_hw.c +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_hw.c @@ -197,6 +197,12 @@ int txgbe_reset_hw(struct wx *wx) txgbe_reset_misc(wx); + if (wx->mac.type != wx_mac_sp) { + wr32(wx, TXGBE_PX_PF_BME, 0x1); + wr32m(wx, TXGBE_RDM_RSC_CTL, TXGBE_RDM_RSC_CTL_FREE_CTL, + TXGBE_RDM_RSC_CTL_FREE_CTL); + } + wx_clear_hw_cntrs(wx); /* Store the permanent mac address */ diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_irq.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_irq.c index 0ee73a265545..8658a51ee810 100644 --- a/drivers/net/ethernet/wangxun/txgbe/txgbe_irq.c +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_irq.c @@ -166,6 +166,9 @@ static void txgbe_del_irq_domain(struct txgbe *txgbe) void txgbe_free_misc_irq(struct txgbe *txgbe) { + if (txgbe->wx->mac.type == wx_mac_aml) + return; + free_irq(txgbe->link_irq, txgbe); free_irq(txgbe->misc.irq, txgbe); txgbe_del_irq_domain(txgbe); @@ -177,6 +180,9 @@ int txgbe_setup_misc_irq(struct txgbe *txgbe) struct wx *wx = txgbe->wx; int hwirq, err; + if (wx->mac.type == wx_mac_aml) + goto skip_sp_irq; + txgbe->misc.nirqs = 1; txgbe->misc.domain = irq_domain_add_simple(NULL, txgbe->misc.nirqs, 0, &txgbe_misc_irq_domain_ops, txgbe); @@ -206,6 +212,7 @@ int txgbe_setup_misc_irq(struct txgbe *txgbe) if (err) goto free_msic_irq; +skip_sp_irq: wx->misc_irq_domain = true; return 0; diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c index 734450af9a43..ce83811a45e2 100644 --- a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c @@ -35,6 +35,12 @@ char txgbe_driver_name[] = "txgbe"; static const struct pci_device_id txgbe_pci_tbl[] = { { PCI_VDEVICE(WANGXUN, TXGBE_DEV_ID_SP1000), 0}, { PCI_VDEVICE(WANGXUN, TXGBE_DEV_ID_WX1820), 0}, + { PCI_VDEVICE(WANGXUN, TXGBE_DEV_ID_AML5010), 0}, + { PCI_VDEVICE(WANGXUN, TXGBE_DEV_ID_AML5110), 0}, + { PCI_VDEVICE(WANGXUN, TXGBE_DEV_ID_AML5025), 0}, + { PCI_VDEVICE(WANGXUN, TXGBE_DEV_ID_AML5125), 0}, + { PCI_VDEVICE(WANGXUN, TXGBE_DEV_ID_AML5040), 0}, + { PCI_VDEVICE(WANGXUN, TXGBE_DEV_ID_AML5140), 0}, /* required last entry */ { .device = 0 } }; @@ -90,7 +96,18 @@ static void txgbe_up_complete(struct wx *wx) smp_mb__before_atomic(); wx_napi_enable_all(wx); - phylink_start(wx->phylink); + if (wx->mac.type == wx_mac_aml) { + u32 reg; + + reg = rd32(wx, TXGBE_AML_MAC_TX_CFG); + reg &= ~TXGBE_AML_MAC_TX_CFG_SPEED_MASK; + reg |= TXGBE_AML_MAC_TX_CFG_SPEED_25G; + wr32(wx, WX_MAC_TX_CFG, reg); + txgbe_enable_sec_tx_path(wx); + netif_carrier_on(wx->netdev); + } else { + phylink_start(wx->phylink); + } /* clear any pending interrupts, may auto mask */ rd32(wx, WX_PX_IC(0)); @@ -171,7 +188,10 @@ void txgbe_down(struct wx *wx) { txgbe_disable_device(wx); txgbe_reset(wx); - phylink_stop(wx->phylink); + if (wx->mac.type == wx_mac_aml) + netif_carrier_off(wx->netdev); + else + phylink_stop(wx->phylink); wx_clean_all_tx_rings(wx); wx_clean_all_rx_rings(wx); @@ -197,6 +217,14 @@ static void txgbe_init_type_code(struct wx *wx) case TXGBE_DEV_ID_WX1820: wx->mac.type = wx_mac_sp; break; + case TXGBE_DEV_ID_AML5010: + case TXGBE_DEV_ID_AML5110: + case TXGBE_DEV_ID_AML5025: + case TXGBE_DEV_ID_AML5125: + case TXGBE_DEV_ID_AML5040: + case TXGBE_DEV_ID_AML5140: + wx->mac.type = wx_mac_aml; + break; default: wx->mac.type = wx_mac_unknown; break; @@ -284,6 +312,17 @@ static int txgbe_sw_init(struct wx *wx) wx->do_reset = txgbe_do_reset; + switch (wx->mac.type) { + case wx_mac_sp: + break; + case wx_mac_aml: + set_bit(WX_FLAG_SWFW_RING, wx->flags); + wx->swfw_index = 0; + break; + default: + break; + } + return 0; } diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c index 7e17d727c2ba..85f022ceef4f 100644 --- a/drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c @@ -567,6 +567,9 @@ int txgbe_init_phy(struct txgbe *txgbe) struct wx *wx = txgbe->wx; int ret; + if (wx->mac.type == wx_mac_aml) + return 0; + if (txgbe->wx->media_type == sp_media_copper) return txgbe_ext_phy_init(txgbe); @@ -631,6 +634,9 @@ err_unregister_swnode: void txgbe_remove_phy(struct txgbe *txgbe) { + if (txgbe->wx->mac.type == wx_mac_aml) + return; + if (txgbe->wx->media_type == sp_media_copper) { phylink_disconnect_phy(txgbe->wx->phylink); phylink_destroy(txgbe->wx->phylink); diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h b/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h index 629a13e96b85..9c1c26234cad 100644 --- a/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h @@ -10,6 +10,12 @@ /* Device IDs */ #define TXGBE_DEV_ID_SP1000 0x1001 #define TXGBE_DEV_ID_WX1820 0x2001 +#define TXGBE_DEV_ID_AML5010 0x5010 +#define TXGBE_DEV_ID_AML5110 0x5110 +#define TXGBE_DEV_ID_AML5025 0x5025 +#define TXGBE_DEV_ID_AML5125 0x5125 +#define TXGBE_DEV_ID_AML5040 0x5040 +#define TXGBE_DEV_ID_AML5140 0x5140 /* Subsystem IDs */ /* SFP */ @@ -137,6 +143,14 @@ #define TXGBE_RDB_FDIR_FLEX_CFG_MSK BIT(2) #define TXGBE_RDB_FDIR_FLEX_CFG_OFST(v) FIELD_PREP(GENMASK(7, 3), v) +/*************************** Amber Lite Registers ****************************/ +#define TXGBE_PX_PF_BME 0x4B8 +#define TXGBE_AML_MAC_TX_CFG 0x11000 +#define TXGBE_AML_MAC_TX_CFG_SPEED_MASK GENMASK(30, 27) +#define TXGBE_AML_MAC_TX_CFG_SPEED_25G BIT(28) +#define TXGBE_RDM_RSC_CTL 0x1200C +#define TXGBE_RDM_RSC_CTL_FREE_CTL BIT(7) + /* Checksum and EEPROM pointers */ #define TXGBE_EEPROM_LAST_WORD 0x800 #define TXGBE_EEPROM_CHECKSUM 0x2F -- 2.51.0