From nobody Mon Jun 8 09:49:26 2026 Received: from smtpbgeu1.qq.com (smtpbgeu1.qq.com [52.59.177.22]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 148543FFAA8 for ; Thu, 4 Jun 2026 11:29:51 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=52.59.177.22 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780572595; cv=none; b=HcYiL0Xtu9/C61rL7Om+/nC9XF4miyqstvZSHqcBdKTmAF3vi9cTawhPLlA5d+uHAyLGxDkdl8pYwr7FxhPE4xn8Bbar3ysqb6ofwVpMjHE5hCHSIPja12l9XAxyOPNUiAldEyMoC1ZedrdiCLyDOypmUrKop0WPGdVh68foALw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780572595; c=relaxed/simple; bh=m9HsIlDLKznM2T1txxr6ZGwq65pmwKDWEBn0SuHBVNw=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version:Content-Type; b=FQRPphsAqIpRHOsm2alPFnC7BAbiYTS/D5OmkCZaO4qYBsoTw4v3GGwxMW3wejPLTgZBS0E4PqKEaTgfYFmnznoai5ctBWhn35rW91Rmie05h/OPQ1dlnDif0F2b0V29vQfiqgMlGdLNiy2nbrdiaFTAzI+BSPkUWPEXBBmwXpk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=mucse.com; spf=pass smtp.mailfrom=mucse.com; arc=none smtp.client-ip=52.59.177.22 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=mucse.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=mucse.com X-QQ-mid: zesmtpgz8t1780572482t242461f7 X-QQ-Originating-IP: FHgeB4crH0OrKMLNQ6W4ysaMKgSdo2j5Rnc5RbAqCzg= Received: from localhost.localdomain ( [203.174.112.180]) by bizesmtp.qq.com (ESMTP) with id ; Thu, 04 Jun 2026 19:28:00 +0800 (CST) X-QQ-SSF: 0000000000000000000000000000000 X-QQ-GoodBg: 0 X-BIZMAIL-ID: 7388711927534182022 EX-QQ-RecipientCnt: 13 From: Dong Yibo To: andrew+netdev@lunn.ch, davem@davemloft.net, edumazet@google.com, kuba@kernel.org, pabeni@redhat.com, danishanwar@ti.com, vadim.fedorenko@linux.dev, horms@kernel.org, u.kleine-koenig@baylibre.com Cc: linux-kernel@vger.kernel.org, netdev@vger.kernel.org, dong100@mucse.com, yaojun@mucse.com Subject: [PATCH net-next v6 1/4] net: rnpgbe: Add interrupt handling Date: Thu, 4 Jun 2026 19:27:47 +0800 Message-Id: <20260604112750.769215-2-dong100@mucse.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20260604112750.769215-1-dong100@mucse.com> References: <20260604112750.769215-1-dong100@mucse.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-QQ-SENDSIZE: 520 Feedback-ID: zesmtpgz:mucse.com:qybglogicsvrgz:qybglogicsvrgz3a-1 X-QQ-XMAILINFO: NkHKfw09D6j8VOR43jPERRv01mXS1fmQIO41+zU3HgIIR6TImcLeYTN9 Rv1vtYlikjk8ENb4ZZLqIksDvF3CavzXOmLnEJ36tlEZb5QUPT7pPC7gJImuEKvJIoyvLY8 xc1XjgUzJVjmlMOgqRASp9CzcvdvxNvXCmJlSt9V8Q1di+Z1qPcjQ8jaAG4dABAZtRV5fmy M/E21DsKqEseldfDv0WUmtgJN8m4ijof81T9kwF7WZ0/JA7p7jQ4YjsVo9debm2VgvY9qd/ RS+Jj3fRLVi5PqqDmAYJ3AmEO/++hea5YzyO/KJRy26+9LXRAcE4/lY3zyxVVnG93UbmjVw TDK4oLZPurwZDrzk6vPJi2RGw14fPk567X00MA+5ieeUqMTfzc/rHSRADSDOl5+ztioId9c s2nX5qniL4kn9s+3loyDuq9jLm4/yP+kGLjwh6xXyo+4glC4LQhCPGBS/iO9YV/ezlQMpSv RoYZU+ulMgU1WUuyPHvs2p11Uriv6UP6r9XludOWskg0PpNK1Wh19aCRvBPbpVXs81C964G RkFWSO4HIM/VMtmb5Ssrygqu/Hb69BLI24s1ZJQsKpVU1ypmjKEp8oYaJZZBKM5oxGAwDpF 1S1vSq89uroYAvdFSUgVtnhUEeMcSuwwlh7nrkO8NWz8NRdcTG+kKGyyHJq1fRZMAjM5+zG U/sRphM2O7S4RnCsqSoRDT3VteCt9MdO2F7ztw5hGna2V2cPFuxLPEW4j8KCoj5TQ3aS5vV gvV7FOfAr/gFuYyTuHQX//uibWMSWWmWrTyC5U9Rj0ey+oWJRDLQcSHT0zgQAUPLRU4WTP8 +ADF0l2O2LCeh5Bxnnm7WnZ4u5HGuQYKpJBWnYZmKhReNEze5xqShGsDxuvGEmHRgZB41Ac fagx0/Nv9776rBEtEF44/3ZxqG12h5xWCjwW5c9AUQUFWv9UoMhVXt/xh4cxx+wwI3xv5mZ J/sPD4RKZ16m+f3eHHQko0BkyNYGgf8IV165g7dbzfw4/WtJUXmv7QkrDMQGZmvh2JZPIbg M+exh0iBmsdNdxlX2My77xX01D2HLLtNe1yHcoxa7kxJaPnZPlmMmNiNiW7ec9Xkf0Luoxh dpnEbZ3u/y/ X-QQ-XMRINFO: NS+P29fieYNwqS3WCnRCOn9D1NpZuCnCRA== X-QQ-RECHKSPAM: 0 Add comprehensive interrupt handling for the RNPGBE driver: - Implement msi-x/msi interrupt configuration and management - Create library functions for interrupt registration and cleanup This infrastructure enables proper interrupt handling for the RNPGBE driver. Signed-off-by: Dong Yibo --- drivers/net/ethernet/mucse/rnpgbe/Makefile | 3 +- drivers/net/ethernet/mucse/rnpgbe/rnpgbe.h | 53 ++ .../net/ethernet/mucse/rnpgbe/rnpgbe_chip.c | 4 + drivers/net/ethernet/mucse/rnpgbe/rnpgbe_hw.h | 2 + .../net/ethernet/mucse/rnpgbe/rnpgbe_lib.c | 636 ++++++++++++++++++ .../net/ethernet/mucse/rnpgbe/rnpgbe_lib.h | 34 + .../net/ethernet/mucse/rnpgbe/rnpgbe_main.c | 48 +- .../net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.c | 8 + .../net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.h | 1 + 9 files changed, 786 insertions(+), 3 deletions(-) create mode 100644 drivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c create mode 100644 drivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.h diff --git a/drivers/net/ethernet/mucse/rnpgbe/Makefile b/drivers/net/ether= net/mucse/rnpgbe/Makefile index de8bcb7772ab..17574cad392a 100644 --- a/drivers/net/ethernet/mucse/rnpgbe/Makefile +++ b/drivers/net/ethernet/mucse/rnpgbe/Makefile @@ -8,4 +8,5 @@ obj-$(CONFIG_MGBE) +=3D rnpgbe.o rnpgbe-objs :=3D rnpgbe_main.o\ rnpgbe_chip.o\ rnpgbe_mbx.o\ - rnpgbe_mbx_fw.o + rnpgbe_mbx_fw.o\ + rnpgbe_lib.o diff --git a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe.h b/drivers/net/ether= net/mucse/rnpgbe/rnpgbe.h index 5b024f9f7e17..fa6c7a8507c5 100644 --- a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe.h +++ b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe.h @@ -6,6 +6,10 @@ =20 #include #include +#include +#include + +#include "rnpgbe_hw.h" =20 enum rnpgbe_boards { board_n500, @@ -35,21 +39,68 @@ enum { =20 struct mucse_hw { void __iomem *hw_addr; + void __iomem *ring_msix_base; struct pci_dev *pdev; struct mucse_mbx_info mbx; int port; u8 pfvfnum; }; =20 +struct mucse_ring { + struct mucse_ring *next; + struct mucse_q_vector *q_vector; + void __iomem *ring_addr; + void __iomem *irq_mask; + void __iomem *trig; + u8 queue_index; + /* hw ring idx */ + u8 rnpgbe_queue_idx; +} ____cacheline_internodealigned_in_smp; + +struct mucse_ring_container { + struct mucse_ring *ring; + u16 count; +}; + +struct mucse_q_vector { + struct mucse *mucse; + int v_idx; + struct mucse_ring_container rx, tx; + struct napi_struct napi; + char name[IFNAMSIZ + 18]; + /* for dynamic allocation of rings associated with this q_vector */ + struct mucse_ring ring[] ____cacheline_internodealigned_in_smp; +}; + struct mucse_stats { u64 tx_dropped; }; =20 +#define MAX_Q_VECTORS 8 + +enum mucse_state_t { + __MUCSE_DOWN, +}; + struct mucse { struct net_device *netdev; struct pci_dev *pdev; struct mucse_hw hw; struct mucse_stats stats; +#define M_FLAG_MSI_EN BIT(0) +#define M_FLAG_MSIX_SINGLE_EN BIT(1) +#define M_FLAG_MSIX_EN BIT(2) + u32 flags; + struct mucse_ring *tx_ring[RNPGBE_MAX_QUEUES] + ____cacheline_aligned_in_smp; + struct mucse_ring *rx_ring[RNPGBE_MAX_QUEUES] + ____cacheline_aligned_in_smp; + struct mucse_q_vector *q_vector[MAX_Q_VECTORS]; + int num_tx_queues; + int num_q_vectors; + int num_rx_queues; + char mbx_name[32]; + unsigned long state; }; =20 int rnpgbe_get_permanent_mac(struct mucse_hw *hw, u8 *perm_addr); @@ -68,4 +119,6 @@ int rnpgbe_init_hw(struct mucse_hw *hw, int board_type); =20 #define mucse_hw_wr32(hw, reg, val) \ writel((val), (hw)->hw_addr + (reg)) +#define mucse_hw_rd32(hw, reg) \ + readl((hw)->hw_addr + (reg)) #endif /* _RNPGBE_H */ diff --git a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_chip.c b/drivers/net/= ethernet/mucse/rnpgbe/rnpgbe_chip.c index ebc7b3750157..921cc325a991 100644 --- a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_chip.c +++ b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_chip.c @@ -89,6 +89,8 @@ static void rnpgbe_init_n500(struct mucse_hw *hw) { struct mucse_mbx_info *mbx =3D &hw->mbx; =20 + hw->ring_msix_base =3D hw->hw_addr + MUCSE_N500_RING_MSIX_BASE; + mbx->fwpf_ctrl_base =3D MUCSE_N500_FWPF_CTRL_BASE; mbx->fwpf_shm_base =3D MUCSE_N500_FWPF_SHM_BASE; } @@ -104,6 +106,8 @@ static void rnpgbe_init_n210(struct mucse_hw *hw) { struct mucse_mbx_info *mbx =3D &hw->mbx; =20 + hw->ring_msix_base =3D hw->hw_addr + MUCSE_N210_RING_MSIX_BASE; + mbx->fwpf_ctrl_base =3D MUCSE_N210_FWPF_CTRL_BASE; mbx->fwpf_shm_base =3D MUCSE_N210_FWPF_SHM_BASE; } diff --git a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_hw.h b/drivers/net/et= hernet/mucse/rnpgbe/rnpgbe_hw.h index e77e6bc3d3e3..0dce78e4a91b 100644 --- a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_hw.h +++ b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_hw.h @@ -6,10 +6,12 @@ =20 #define MUCSE_N500_FWPF_CTRL_BASE 0x28b00 #define MUCSE_N500_FWPF_SHM_BASE 0x2d000 +#define MUCSE_N500_RING_MSIX_BASE 0x28700 #define MUCSE_GBE_PFFW_MBX_CTRL_OFFSET 0x5500 #define MUCSE_GBE_FWPF_MBX_MASK_OFFSET 0x5700 #define MUCSE_N210_FWPF_CTRL_BASE 0x29400 #define MUCSE_N210_FWPF_SHM_BASE 0x2d900 +#define MUCSE_N210_RING_MSIX_BASE 0x29000 =20 #define RNPGBE_DMA_AXI_EN 0x0010 =20 diff --git a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c b/drivers/net/e= thernet/mucse/rnpgbe/rnpgbe_lib.c new file mode 100644 index 000000000000..cb241dc339d8 --- /dev/null +++ b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c @@ -0,0 +1,636 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright(c) 2020 - 2025 Mucse Corporation. */ + +#include +#include + +#include "rnpgbe_lib.h" +#include "rnpgbe.h" +#include "rnpgbe_mbx_fw.h" + +/** + * rnpgbe_msix_other - Other irq handler + * @irq: irq num + * @data: private data + * + * @return: IRQ_HANDLED + **/ +static irqreturn_t rnpgbe_msix_other(int irq, void *data) +{ + struct mucse *mucse =3D (struct mucse *)data; + + mucse_fw_irq_handler(&mucse->hw); + + return IRQ_HANDLED; +} + +static void rnpgbe_irq_disable_queues(struct mucse_q_vector *q_vector) +{ + struct mucse_ring *ring; + + /* tx/rx use one register, different bit */ + mucse_for_each_ring(ring, q_vector->tx) { + writel(INT_VALID, ring->trig); + writel((RX_INT_MASK | TX_INT_MASK), ring->irq_mask); + } +} + +/** + * rnpgbe_int_single - Msix-signle/msi irq handler + * @irq: irq num + * @data: private data + * @return: IRQ_HANDLED + **/ +static irqreturn_t rnpgbe_int_single(int irq, void *data) +{ + struct mucse *mucse =3D (struct mucse *)data; + struct mucse_q_vector *q_vector; + + mucse_fw_irq_handler(&mucse->hw); + + if (test_bit(__MUCSE_DOWN, &mucse->state)) + return IRQ_HANDLED; + + q_vector =3D mucse->q_vector[0]; + rnpgbe_irq_disable_queues(q_vector); + if (q_vector->rx.ring || q_vector->tx.ring) + napi_schedule_irqoff(&q_vector->napi); + + return IRQ_HANDLED; +} + +static void rnpgbe_irq_enable_queues(struct mucse_q_vector *q_vector) +{ + struct mucse_ring *ring; + + /* tx/rx use one register, different bit */ + mucse_for_each_ring(ring, q_vector->tx) { + writel(0, ring->irq_mask); + + /* Trigger hw to re-check events lost during the + * masking window. This is not a W1C status clear + * nor a software interrupt trigger =E2=80=94 hw re-issues + * the interrupt only if an event was actually dropped. + */ + writel(INT_VALID | TX_INT_MASK | RX_INT_MASK, ring->trig); + } +} + +/** + * rnpgbe_poll - NAPI Rx polling callback + * @napi: structure for representing this polling device + * @budget: how many packets driver is allowed to clean + * + * @return: work done in this call + * This function is used for legacy and MSI, NAPI mode + **/ +static int rnpgbe_poll(struct napi_struct *napi, int budget) +{ + struct mucse_q_vector *q_vector =3D + container_of(napi, struct mucse_q_vector, napi); + int work_done =3D 0; + + /* Exit if we are called by netpoll */ + if (unlikely(!budget)) + return 0; + + if (likely(napi_complete_done(napi, work_done))) + rnpgbe_irq_enable_queues(q_vector); + + return work_done; +} + +/** + * rnpgbe_request_mbx_irq - Register mbx routine + * @mucse: pointer to private structure + * + * In MSIX mode, register a dedicated handler for vector 0 (mailbox) + * In MSI/MSI-X_SINGLE mode, mailbox is multiplexed through + * data tx/rx handler. + * + * @return: 0 on success, negative on failure + **/ +int rnpgbe_request_mbx_irq(struct mucse *mucse) +{ + struct pci_dev *pdev =3D mucse->pdev; + int err =3D 0; + + snprintf(mucse->mbx_name, sizeof(mucse->mbx_name), + "rnpgbe-mbx:%s", pci_name(pdev)); + + if (mucse->flags & M_FLAG_MSIX_EN) { + err =3D request_irq(pci_irq_vector(pdev, 0), + rnpgbe_msix_other, 0, mucse->mbx_name, + mucse); + } else { + err =3D request_irq(pci_irq_vector(pdev, 0), + rnpgbe_int_single, 0, mucse->mbx_name, + mucse); + } + + return err; +} + +/** + * rnpgbe_free_mbx_irq - Remove mbx routine + * @mucse: pointer to private structure + **/ +void rnpgbe_free_mbx_irq(struct mucse *mucse) +{ + struct pci_dev *pdev =3D mucse->pdev; + + free_irq(pci_irq_vector(pdev, 0), mucse); +} + +/** + * rnpgbe_set_num_queues - Allocate queues for device, feature dependent + * @mucse: pointer to private structure + * + * Determine tx/rx queue nums + **/ +static void rnpgbe_set_num_queues(struct mucse *mucse) +{ + /* start from 1 queue */ + mucse->num_tx_queues =3D 1; + mucse->num_rx_queues =3D 1; +} + +/** + * rnpgbe_set_interrupt_capability - Set MSI-X or MSI if supported + * @mucse: pointer to private structure + * + * Attempt to configure the interrupts using the best available + * capabilities of the hardware. + * + * @return: 0 on success, negative on failure + **/ +static int rnpgbe_set_interrupt_capability(struct mucse *mucse) +{ + int v_budget; + + v_budget =3D min3(mucse->num_tx_queues, mucse->num_rx_queues, + MAX_Q_VECTORS); + v_budget =3D min_t(int, v_budget, num_online_cpus()); + /* add one vector for mbx */ + v_budget +=3D 1; + + /* Hardware limitation: only 1 MSI vector is supported even + * if multiple messages are requested. MSI mode falls back + * to single vector automatically. + */ + v_budget =3D pci_alloc_irq_vectors(mucse->pdev, 1, v_budget, + PCI_IRQ_MSI | PCI_IRQ_MSIX); + if (v_budget < 0) + return v_budget; + + if (mucse->pdev->msix_enabled) { + /* q_vector not include mbx */ + if (v_budget > 1) { + mucse->flags |=3D M_FLAG_MSIX_EN; + mucse->num_q_vectors =3D v_budget - 1; + } else { + mucse->flags |=3D M_FLAG_MSIX_SINGLE_EN; + mucse->num_q_vectors =3D 1; + } + } else { + /* hw only support 1 msi irq */ + mucse->num_q_vectors =3D 1; + mucse->flags |=3D M_FLAG_MSI_EN; + } + + return 0; +} + +/** + * mucse_add_ring - Add ring to ring container + * @ring: ring to be added + * @head: ring container + **/ +static void mucse_add_ring(struct mucse_ring *ring, + struct mucse_ring_container *head) +{ + ring->next =3D head->ring; + head->ring =3D ring; + head->count++; +} + +/** + * rnpgbe_alloc_q_vector - Allocate memory for a single interrupt vector + * @mucse: pointer to private structure + * @eth_queue_idx: queue_index idx for this q_vector + * @v_idx: index of vector used for this q_vector + * @r_idx: total number of rings to allocate + * @r_count: ring count + * @step: ring step + * + * @return: 0 on success. If allocation fails we return -ENOMEM. + **/ +static int rnpgbe_alloc_q_vector(struct mucse *mucse, + int eth_queue_idx, int v_idx, int r_idx, + int r_count, int step) +{ + int rxr_idx =3D r_idx, txr_idx =3D r_idx; + struct mucse_hw *hw =3D &mucse->hw; + struct mucse_q_vector *q_vector; + int txr_count, rxr_count, idx; + struct mucse_ring *ring; + int ring_count; + + txr_count =3D r_count; + rxr_count =3D r_count; + ring_count =3D txr_count + rxr_count; + + q_vector =3D kzalloc_flex(*q_vector, ring, ring_count); + if (!q_vector) + return -ENOMEM; + + netif_napi_add(mucse->netdev, &q_vector->napi, rnpgbe_poll); + /* tie q_vector and mucse together */ + mucse->q_vector[v_idx] =3D q_vector; + q_vector->mucse =3D mucse; + q_vector->v_idx =3D v_idx; + /* if mbx use separate irq, we should add 1 */ + if (mucse->flags & M_FLAG_MSIX_EN) + q_vector->v_idx++; + + ring =3D q_vector->ring; + + for (idx =3D 0; idx < txr_count; idx++) { + mucse_add_ring(ring, &q_vector->tx); + ring->queue_index =3D eth_queue_idx + idx; + ring->rnpgbe_queue_idx =3D txr_idx; + ring->ring_addr =3D hw->hw_addr + RING_OFFSET(txr_idx); + ring->irq_mask =3D ring->ring_addr + RNPGBE_DMA_INT_MASK; + ring->trig =3D ring->ring_addr + RNPGBE_DMA_INT_TRIG; + mucse->tx_ring[ring->queue_index] =3D ring; + txr_idx +=3D step; + ring++; + } + + for (idx =3D 0; idx < rxr_count; idx++) { + mucse_add_ring(ring, &q_vector->rx); + ring->queue_index =3D eth_queue_idx + idx; + ring->rnpgbe_queue_idx =3D rxr_idx; + ring->ring_addr =3D hw->hw_addr + RING_OFFSET(rxr_idx); + ring->irq_mask =3D ring->ring_addr + RNPGBE_DMA_INT_MASK; + ring->trig =3D ring->ring_addr + RNPGBE_DMA_INT_TRIG; + mucse->rx_ring[ring->queue_index] =3D ring; + rxr_idx +=3D step; + ring++; + } + + return 0; +} + +/** + * rnpgbe_free_q_vector - Free memory allocated for specific interrupt vec= tor + * @mucse: pointer to private structure + * @v_idx: index of vector to be freed + * + * This function frees the memory allocated to the q_vector. In addition = if + * NAPI is enabled it will delete any references to the NAPI struct prior + * to freeing the q_vector. + **/ +static void rnpgbe_free_q_vector(struct mucse *mucse, int v_idx) +{ + struct mucse_q_vector *q_vector =3D mucse->q_vector[v_idx]; + struct mucse_ring *ring; + + mucse_for_each_ring(ring, q_vector->tx) + mucse->tx_ring[ring->queue_index] =3D NULL; + mucse_for_each_ring(ring, q_vector->rx) + mucse->rx_ring[ring->queue_index] =3D NULL; + mucse->q_vector[v_idx] =3D NULL; + netif_napi_del(&q_vector->napi); + kfree(q_vector); +} + +/** + * rnpgbe_alloc_q_vectors - Allocate memory for interrupt vectors + * @mucse: pointer to private structure + * + * @return: 0 if success. if allocation fails we return -ENOMEM. + **/ +static int rnpgbe_alloc_q_vectors(struct mucse *mucse) +{ + int err, ring_cnt, v_remaing =3D mucse->num_q_vectors; + int r_remaing =3D min_t(int, mucse->num_tx_queues, + mucse->num_rx_queues); + int q_vector_nums =3D 0; + int eth_queue_idx =3D 0; + int ring_step =3D 1; + int ring_idx =3D 0; + int v_idx =3D 0; + + for (; r_remaing > 0 && v_remaing > 0; v_remaing--) { + ring_cnt =3D DIV_ROUND_UP(r_remaing, v_remaing); + err =3D rnpgbe_alloc_q_vector(mucse, eth_queue_idx, + v_idx, ring_idx, ring_cnt, + ring_step); + if (err) + goto err_free_q_vector; + ring_idx +=3D ring_step * ring_cnt; + eth_queue_idx +=3D ring_cnt; + r_remaing -=3D ring_cnt; + q_vector_nums++; + v_idx++; + } + /* Fix the real used q_vectors_nums */ + mucse->num_q_vectors =3D q_vector_nums; + mucse->num_tx_queues =3D eth_queue_idx; + mucse->num_rx_queues =3D eth_queue_idx; + + return 0; + +err_free_q_vector: + mucse->num_tx_queues =3D 0; + mucse->num_rx_queues =3D 0; + mucse->num_q_vectors =3D 0; + + while (v_idx--) + rnpgbe_free_q_vector(mucse, v_idx); + + return err; +} + +/** + * rnpgbe_reset_interrupt_capability - Reset irq capability setup + * @mucse: pointer to private structure + **/ +static void rnpgbe_reset_interrupt_capability(struct mucse *mucse) +{ + pci_free_irq_vectors(mucse->pdev); + mucse->flags &=3D ~(M_FLAG_MSIX_EN | + M_FLAG_MSIX_SINGLE_EN | + M_FLAG_MSI_EN); +} + +/** + * rnpgbe_init_interrupt_scheme - Determine proper interrupt scheme + * @mucse: pointer to private structure + * + * We determine which interrupt scheme to use based on... + * - Hardware queue count + * - cpu numbers + * - irq mode (msi/legacy force 1) + * + * @return: 0 on success, negative on failure + **/ +int rnpgbe_init_interrupt_scheme(struct mucse *mucse) +{ + int err; + + rnpgbe_set_num_queues(mucse); + + err =3D rnpgbe_set_interrupt_capability(mucse); + if (err) + return err; + + err =3D rnpgbe_alloc_q_vectors(mucse); + if (err) { + rnpgbe_reset_interrupt_capability(mucse); + return err; + } + + return 0; +} + +/** + * rnpgbe_free_q_vectors - Free memory allocated for interrupt vectors + * @mucse: pointer to private structure + * + * This function frees the memory allocated to the q_vectors. In addition= if + * NAPI is enabled it will delete any references to the NAPI struct prior + * to freeing the q_vector. + **/ +static void rnpgbe_free_q_vectors(struct mucse *mucse) +{ + int v_idx =3D mucse->num_q_vectors; + + mucse->num_rx_queues =3D 0; + mucse->num_tx_queues =3D 0; + mucse->num_q_vectors =3D 0; + + while (v_idx--) + rnpgbe_free_q_vector(mucse, v_idx); +} + +/** + * rnpgbe_clear_interrupt_scheme - Clear the current interrupt scheme sett= ings + * @mucse: pointer to private structure + * + * Clear interrupt specific resources and reset the structure + **/ +void rnpgbe_clear_interrupt_scheme(struct mucse *mucse) +{ + mucse->num_tx_queues =3D 0; + mucse->num_rx_queues =3D 0; + rnpgbe_free_q_vectors(mucse); + rnpgbe_reset_interrupt_capability(mucse); +} + +/** + * rnpgbe_msix_clean_rings - Msix irq handler for ring irq + * @irq: irq num + * @data: private data + * + * rnpgbe_msix_clean_rings handle irq from ring, start napi + * @return: IRQ_HANDLED + **/ +static irqreturn_t rnpgbe_msix_clean_rings(int irq, void *data) +{ + struct mucse_q_vector *q_vector =3D (struct mucse_q_vector *)data; + + rnpgbe_irq_disable_queues(q_vector); + if (q_vector->rx.ring || q_vector->tx.ring) + napi_schedule_irqoff(&q_vector->napi); + + return IRQ_HANDLED; +} + +/** + * rnpgbe_request_irq - Initialize interrupts + * @mucse: pointer to private structure + * + * Attempts to configure interrupts using the best available + * capabilities of the hardware and kernel. + * + * @return: 0 on success, negative value on failure + **/ +int rnpgbe_request_irq(struct mucse *mucse) +{ + struct net_device *netdev =3D mucse->netdev; + struct pci_dev *pdev =3D mucse->pdev; + struct mucse_q_vector *q_vector; + int err, i; + + if (mucse->flags & M_FLAG_MSIX_EN) { + for (i =3D 0; i < mucse->num_q_vectors; i++) { + q_vector =3D mucse->q_vector[i]; + + snprintf(q_vector->name, sizeof(q_vector->name), + "%s-%s-%d", netdev->name, "TxRx", i); + + err =3D request_irq(pci_irq_vector(pdev, i + 1), + rnpgbe_msix_clean_rings, 0, + q_vector->name, + q_vector); + if (err) { + dev_err(&pdev->dev, "MSI-X req err %d: %d\n", + i + 1, err); + goto err_free_irqs; + } + } + } + + return 0; +err_free_irqs: + while (i--) { + q_vector =3D mucse->q_vector[i]; + synchronize_irq(pci_irq_vector(pdev, i + 1)); + free_irq(pci_irq_vector(pdev, i + 1), q_vector); + } + + return err; +} + +/** + * rnpgbe_free_irq - Free interrupts + * @mucse: pointer to private structure + * + * Attempts to free interrupts according initialized type. + **/ +void rnpgbe_free_irq(struct mucse *mucse) +{ + struct pci_dev *pdev =3D mucse->pdev; + struct mucse_q_vector *q_vector; + + if (mucse->flags & M_FLAG_MSIX_EN) { + for (int i =3D 0; i < mucse->num_q_vectors; i++) { + q_vector =3D mucse->q_vector[i]; + if (!q_vector) + continue; + + free_irq(pci_irq_vector(pdev, i + 1), q_vector); + } + } +} + +/** + * rnpgbe_set_ring_vector - Set the ring_vector registers, + * mapping interrupt causes to vectors + * @mucse: pointer to private structure + * @queue: queue to map the corresponding interrupt to + * @vector: the vector num to map to the corresponding queue + * + */ +static void rnpgbe_set_ring_vector(struct mucse *mucse, + u8 queue, u8 vector) +{ + struct mucse_hw *hw =3D &mucse->hw; + u32 data; + + data =3D hw->pfvfnum << 24; + data |=3D (vector << 8); + data |=3D vector; + writel(data, hw->ring_msix_base + RING_VECTOR(queue)); +} + +/** + * rnpgbe_configure_msi - Configure MSI hardware + * @mucse: pointer to private structure + * + * rnpgbe_configure_msi sets up the hardware to properly generate MSI + * interrupts. + **/ +static void rnpgbe_configure_msi(struct mucse *mucse) +{ + struct mucse_q_vector *q_vector =3D mucse->q_vector[0]; + struct mucse_ring *ring; + + /* tx/rx use one register, different bit */ + mucse_for_each_ring(ring, q_vector->tx) + rnpgbe_set_ring_vector(mucse, ring->rnpgbe_queue_idx, 0); +} + +/** + * rnpgbe_configure_msix - Configure MSI-X hardware + * @mucse: pointer to private structure + * + * rnpgbe_configure_msix sets up the hardware to properly generate MSI-X + * interrupts. + **/ +static void rnpgbe_configure_msix(struct mucse *mucse) +{ + struct mucse_q_vector *q_vector; + + for (int i =3D 0; i < mucse->num_q_vectors; i++) { + struct mucse_ring *ring; + + q_vector =3D mucse->q_vector[i]; + /* tx/rx use one register, different bit */ + mucse_for_each_ring(ring, q_vector->tx) { + rnpgbe_set_ring_vector(mucse, ring->rnpgbe_queue_idx, + q_vector->v_idx); + } + } +} + +static void rnpgbe_irq_enable(struct mucse *mucse) +{ + for (int i =3D 0; i < mucse->num_q_vectors; i++) + rnpgbe_irq_enable_queues(mucse->q_vector[i]); +} + +/** + * rnpgbe_irq_disable - Mask off interrupt generation on the NIC + * @mucse: board private structure + **/ +void rnpgbe_irq_disable(struct mucse *mucse) +{ + struct pci_dev *pdev =3D mucse->pdev; + + if (mucse->flags & M_FLAG_MSIX_EN) { + for (int i =3D 0; i < mucse->num_q_vectors; i++) { + rnpgbe_irq_disable_queues(mucse->q_vector[i]); + synchronize_irq(pci_irq_vector(pdev, i + 1)); + } + } else { + rnpgbe_irq_disable_queues(mucse->q_vector[0]); + synchronize_irq(pci_irq_vector(pdev, 0)); + } +} + +static void rnpgbe_napi_enable_all(struct mucse *mucse) +{ + for (int i =3D 0; i < mucse->num_q_vectors; i++) + napi_enable(&mucse->q_vector[i]->napi); +} + +static void rnpgbe_napi_disable_all(struct mucse *mucse) +{ + for (int i =3D 0; i < mucse->num_q_vectors; i++) + napi_disable(&mucse->q_vector[i]->napi); +} + +void rnpgbe_down(struct mucse *mucse) +{ + set_bit(__MUCSE_DOWN, &mucse->state); + rnpgbe_napi_disable_all(mucse); + rnpgbe_irq_disable(mucse); +} + +/** + * rnpgbe_up_complete - Final step for port up + * @mucse: pointer to private structure + **/ +void rnpgbe_up_complete(struct mucse *mucse) +{ + if (mucse->flags & (M_FLAG_MSIX_EN | M_FLAG_MSIX_SINGLE_EN)) + rnpgbe_configure_msix(mucse); + else + rnpgbe_configure_msi(mucse); + rnpgbe_napi_enable_all(mucse); + clear_bit(__MUCSE_DOWN, &mucse->state); + rnpgbe_irq_enable(mucse); +} diff --git a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.h b/drivers/net/e= thernet/mucse/rnpgbe/rnpgbe_lib.h new file mode 100644 index 000000000000..d38c08df6b7c --- /dev/null +++ b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright(c) 2020 - 2025 Mucse Corporation. */ + +#ifndef _RNPGBE_LIB_H +#define _RNPGBE_LIB_H + +struct mucse; +struct mucse_hw; + +#define RING_OFFSET(n) (0x1000 + 0x100 * (n)) +#define RNPGBE_DMA_INT_MASK 0x24 +#define TX_INT_MASK BIT(1) +#define RX_INT_MASK BIT(0) +#define INT_VALID (BIT(16) | BIT(17)) +#define RNPGBE_DMA_INT_TRIG 0x2c /* lost-interrupt recovery trigger = */ +/* | 31:24 | .... | 15:8 | 7:0 | */ +/* | pfvfnum | | tx vector | rx vector | */ +#define RING_VECTOR(n) (0x04 * (n)) + +#define mucse_for_each_ring(pos, head)\ + for (typeof((head).ring) __pos =3D (head).ring;\ + __pos ? ({ pos =3D __pos; 1; }) : 0;\ + __pos =3D __pos->next) + +int rnpgbe_init_interrupt_scheme(struct mucse *mucse); +void rnpgbe_clear_interrupt_scheme(struct mucse *mucse); +int rnpgbe_request_mbx_irq(struct mucse *mucse); +void rnpgbe_free_mbx_irq(struct mucse *mucse); +int rnpgbe_request_irq(struct mucse *mucse); +void rnpgbe_free_irq(struct mucse *mucse); +void rnpgbe_irq_disable(struct mucse *mucse); +void rnpgbe_down(struct mucse *mucse); +void rnpgbe_up_complete(struct mucse *mucse); +#endif diff --git a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_main.c b/drivers/net/= ethernet/mucse/rnpgbe/rnpgbe_main.c index 70a2b0082ba8..f0b90bf8bd55 100644 --- a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_main.c +++ b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_main.c @@ -7,6 +7,7 @@ =20 #include "rnpgbe.h" #include "rnpgbe_hw.h" +#include "rnpgbe_lib.h" #include "rnpgbe_mbx_fw.h" =20 static const char rnpgbe_driver_name[] =3D "rnpgbe"; @@ -32,11 +33,28 @@ static struct pci_device_id rnpgbe_pci_tbl[] =3D { * The open entry point is called when a network interface is made * active by the system (IFF_UP). * - * Return: 0 + * Return: 0 on success, negative value on failure **/ static int rnpgbe_open(struct net_device *netdev) { + struct mucse *mucse =3D netdev_priv(netdev); + int err; + + err =3D rnpgbe_request_irq(mucse); + if (err) + return err; + + err =3D netif_set_real_num_queues(netdev, mucse->num_tx_queues, + mucse->num_rx_queues); + if (err) + goto err_free_irqs; + + rnpgbe_up_complete(mucse); + return 0; +err_free_irqs: + rnpgbe_free_irq(mucse); + return err; } =20 /** @@ -50,6 +68,11 @@ static int rnpgbe_open(struct net_device *netdev) **/ static int rnpgbe_close(struct net_device *netdev) { + struct mucse *mucse =3D netdev_priv(netdev); + + rnpgbe_down(mucse); + rnpgbe_free_irq(mucse); + return 0; } =20 @@ -106,6 +129,7 @@ static int rnpgbe_add_adapter(struct pci_dev *pdev, mucse =3D netdev_priv(netdev); mucse->netdev =3D netdev; mucse->pdev =3D pdev; + set_bit(__MUCSE_DOWN, &mucse->state); pci_set_drvdata(pdev, mucse); =20 hw =3D &mucse->hw; @@ -166,11 +190,28 @@ static int rnpgbe_add_adapter(struct pci_dev *pdev, goto err_powerdown; } =20 + err =3D rnpgbe_init_interrupt_scheme(mucse); + if (err) { + dev_err(&pdev->dev, "init interrupt failed %d\n", err); + goto err_powerdown; + } + + err =3D rnpgbe_request_mbx_irq(mucse); + if (err) { + dev_err(&pdev->dev, "register mbx irq failed %d\n", err); + goto err_clear_interrupt; + } + err =3D register_netdev(netdev); if (err) - goto err_powerdown; + goto err_remove_mbx; =20 return 0; + +err_remove_mbx: + rnpgbe_free_mbx_irq(mucse); +err_clear_interrupt: + rnpgbe_clear_interrupt_scheme(mucse); err_powerdown: /* notify powerdown only powerup ok */ if (!err_notify) { @@ -180,6 +221,7 @@ static int rnpgbe_add_adapter(struct pci_dev *pdev, err_notify); } err_free_net: + pci_set_drvdata(pdev, NULL); free_netdev(netdev); return err; } @@ -253,9 +295,11 @@ static void rnpgbe_rm_adapter(struct pci_dev *pdev) return; netdev =3D mucse->netdev; unregister_netdev(netdev); + rnpgbe_free_mbx_irq(mucse); err =3D rnpgbe_send_notify(hw, false, mucse_fw_powerup); if (err) dev_warn(&pdev->dev, "Send powerdown to hw failed %d\n", err); + rnpgbe_clear_interrupt_scheme(mucse); free_netdev(netdev); } =20 diff --git a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.c b/drivers/ne= t/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.c index 8c8bd5e8e1db..05684d716792 100644 --- a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.c +++ b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.c @@ -189,3 +189,11 @@ int mucse_mbx_get_macaddr(struct mucse_hw *hw, int pfv= fnum, =20 return 0; } + +/** + * mucse_fw_irq_handler - Try to handle a req from hw + * @hw: pointer to the HW structure + **/ +void mucse_fw_irq_handler(struct mucse_hw *hw) +{ +} diff --git a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.h b/drivers/ne= t/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.h index fb24fc12b613..aa26c729588c 100644 --- a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.h +++ b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.h @@ -85,4 +85,5 @@ int mucse_mbx_powerup(struct mucse_hw *hw, bool is_poweru= p); int mucse_mbx_reset_hw(struct mucse_hw *hw); int mucse_mbx_get_macaddr(struct mucse_hw *hw, int pfvfnum, u8 *mac_addr, int port); +void mucse_fw_irq_handler(struct mucse_hw *hw); #endif /* _RNPGBE_MBX_FW_H */ --=20 2.25.1 From nobody Mon Jun 8 09:49:26 2026 Received: from smtpbgbr2.qq.com (smtpbgbr2.qq.com [54.207.22.56]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 37853306774 for ; Thu, 4 Jun 2026 11:29:24 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=54.207.22.56 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780572569; cv=none; b=Qf7FOJgkkDX70AY01QsGM/PRWMf9c4dUbPxbqxu4FyHQCyoi+zj8rV2O11MkpkGOHg7JVrRKWMKnDHJ2wm9vcdvFHWv8GX7Syjd3jo2SWcP7xgdxRsDaMmbUjlOnRce+Iodp9uGRi450hvcq4svo4f3S+EUnFX4+ssazJCyBf+o= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780572569; c=relaxed/simple; bh=qWACVxx08iz9GKBHOr38wdRBbf9dY/R68Mcf5TZ1uBw=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=PIy/5ipdGjwEPu7gVOdgTqlq5uMBb3m3RNtyl9kfMKnY8dUggzWXMmM4qnsoyTtrYFMh2HDZtcn2ZQqaf8p9D7NO5GUYxfmEWV7eV4td+uhINjoLLr2O7TTKQmyMRVrOkBLwRTqrmPN8Wm1P2NcgXx65/0XFFL2kgQNMIhy6WOo= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=mucse.com; spf=pass smtp.mailfrom=mucse.com; arc=none smtp.client-ip=54.207.22.56 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=mucse.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=mucse.com X-QQ-mid: zesmtpgz8t1780572485td143ed65 X-QQ-Originating-IP: as4TRjOAJpN02mIYeEX+nLOWQUiT9+5/sQh+fK6WK0U= Received: from localhost.localdomain ( [203.174.112.180]) by bizesmtp.qq.com (ESMTP) with id ; Thu, 04 Jun 2026 19:28:03 +0800 (CST) X-QQ-SSF: 0000000000000000000000000000000 X-QQ-GoodBg: 0 X-BIZMAIL-ID: 14109646198676352084 EX-QQ-RecipientCnt: 13 From: Dong Yibo To: andrew+netdev@lunn.ch, davem@davemloft.net, edumazet@google.com, kuba@kernel.org, pabeni@redhat.com, danishanwar@ti.com, vadim.fedorenko@linux.dev, horms@kernel.org, u.kleine-koenig@baylibre.com Cc: linux-kernel@vger.kernel.org, netdev@vger.kernel.org, dong100@mucse.com, yaojun@mucse.com Subject: [PATCH net-next v6 2/4] net: rnpgbe: Add basic TX packet transmission support Date: Thu, 4 Jun 2026 19:27:48 +0800 Message-Id: <20260604112750.769215-3-dong100@mucse.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20260604112750.769215-1-dong100@mucse.com> References: <20260604112750.769215-1-dong100@mucse.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-QQ-SENDSIZE: 520 Feedback-ID: zesmtpgz:mucse.com:qybglogicsvrgz:qybglogicsvrgz3a-1 X-QQ-XMAILINFO: N0yntFC9EIUMYNlpF0H1pV0S9HUSCsWX7ce5zch9Zd9Gtc67cwWRs0rx tQABde0n17lsy/oLTss+Ok4vTxdtTudOgf9Pb/FsyMReJ9Fn3e07juFB5h9LUEDzl2T4K5I qP0+yhw1qlApYSMmr+6/GM0zuKkzOPSjqYTSdv16MJ4ICY8JwjQIg9QkudfwdBmpL2Y5CPy Jvu6Os51W+2IuTMQtNPillB/JkrWzrtNpOcMMpI3bUHTOriyv90Afpoit491eqZChsYz3VF uqRbiLvAZJZqSBCTFjgWVsqGbEwteU+uMkhoSFFQjGGXgDUxPg3lmLpadyyQtyGE+grLai9 yPvF2Xhqt0XEu5ExRl9irHoHXA/YKsmXl5IMLSMHqmYd12EF4bmhD+mK3IUTyEVogu1tEIW IV4vq6rafJE+/x+p3UKgB41ADF1xi714wXhlT81sxLvgIVJQDbW+Cyip6QRfzh+ohBDRU2W EwVbfePc+yQ20OD9Wx1YKBFrSCecVW6jUYrtFe+8LVawdwXk3HgfZ8MfaiMenS5y/mzenXO rH6dz87klHp9GFL3jhaMKDYYz4T3Hq7BZCiuUkp+MMDXJkIz8oO1k+qJrtbfiVuctXvqxSW ZFsmbLhrZImszfGIwrKwZo+q5MC6kKg0yvDtWdZrdX2Rc3YHIQ7fJBmag6d0Tg9gXXetEey FWKN186OvOoN4utonaaM4UTQSzO8XnAfSPt3JzrYZ7USyTyfkMv5ZpOotpACqhQ43JaMxUY 4v+bK051X81BfzDPl4Ds95sec6Krgp4Qybd+MQSfk2iIXhtHrebyiyF8f8ca3wbm7uT03PX Ls+a3nI7mUHU+C1wk0h0cnIJBKuCLVaLgBmIKhjUv1kPNZKmLiVmzisuVgjHhs+hnkIAEKh hJZbjzqhZixnaPW0z8uCUnmH1jCSqbzgJ365AYofj2wEOuj5tyvOW4YrWIXEe/x2N7pZ0no 3wALuADYgN0BbbCg1fbn0+3ajHJ3C7AtG+lNn2wFd9jM0lWqczi7JX6yx2nZrjs4gnr2M5Z MpXlpKOINmb+7nEDhrDZmZ4x/5WD3rraPyPaNhXZVHucdKblpeGkQzm89jQoOQNbRexdXWH zyOiJ8h2D0jpEOGHtSM4hzuo8aLo90bmQ== X-QQ-XMRINFO: MSVp+SPm3vtSI1QTLgDHQqIV1w2oNKDqfg== X-QQ-RECHKSPAM: 0 Content-Type: text/plain; charset="utf-8" Implement basic transmit path for the RNPGBE driver: - Add TX descriptor structure (rnpgbe_tx_desc) and TX buffer management - Implement rnpgbe_xmit_frame_ring() for packet transmission - Add TX ring resource allocation and cleanup functions - Implement TX completion handling via rnpgbe_clean_tx_irq() - Implement statistics collection for TX packets/bytes - introduces atomic64_t dropped and tx_dropped reporting via ndo_get_stats64 - Fix DMA mask setup to use dma_set_mask_and_coherent() with proper fallback - Adds cycles_per_us to struct mucse_hw to scale RNPGBE_TX_INT_TIME This enables basic packet transmission functionality for the RNPGBE driver. Signed-off-by: Dong Yibo --- drivers/net/ethernet/mucse/rnpgbe/rnpgbe.h | 79 ++- .../net/ethernet/mucse/rnpgbe/rnpgbe_chip.c | 4 + drivers/net/ethernet/mucse/rnpgbe/rnpgbe_hw.h | 3 + .../net/ethernet/mucse/rnpgbe/rnpgbe_lib.c | 632 ++++++++++++++++++ .../net/ethernet/mucse/rnpgbe/rnpgbe_lib.h | 30 + .../net/ethernet/mucse/rnpgbe/rnpgbe_main.c | 38 +- 6 files changed, 776 insertions(+), 10 deletions(-) diff --git a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe.h b/drivers/net/ether= net/mucse/rnpgbe/rnpgbe.h index fa6c7a8507c5..b2045891f205 100644 --- a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe.h +++ b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe.h @@ -43,20 +43,85 @@ struct mucse_hw { struct pci_dev *pdev; struct mucse_mbx_info mbx; int port; + u16 cycles_per_us; u8 pfvfnum; }; =20 +struct rnpgbe_tx_desc { + __le64 pkt_addr; /* Packet buffer address */ + union { + __le64 vlan_cmd_bsz; + struct { + __le32 blen_mac_ip_len; + __le32 vlan_cmd; /* vlan & cmd status */ + }; + }; +#define M_TXD_CMD_RS 0x040000 /* Report Status */ +#define M_TXD_STAT_DD 0x020000 /* Descriptor Done */ +#define M_TXD_CMD_EOP 0x010000 /* End of Packet */ +}; + +#define M_TX_DESC(R, i) (&(((struct rnpgbe_tx_desc *)((R)->desc))[i])) + +struct mucse_tx_buffer { + struct rnpgbe_tx_desc *next_to_watch; + struct sk_buff *skb; + unsigned int bytecount; + unsigned short gso_segs; + DEFINE_DMA_UNMAP_ADDR(dma); + DEFINE_DMA_UNMAP_LEN(len); + bool mapped_as_page; /* true if dma was mapped with dma_map_page */ +}; + +struct mucse_queue_stats { + u64 packets; + u64 bytes; + atomic64_t dropped; +}; + struct mucse_ring { struct mucse_ring *next; struct mucse_q_vector *q_vector; + struct net_device *netdev; + struct device *dev; + void *desc; + struct mucse_tx_buffer *tx_buffer_info; void __iomem *ring_addr; + void __iomem *tail; void __iomem *irq_mask; void __iomem *trig; u8 queue_index; /* hw ring idx */ u8 rnpgbe_queue_idx; + u8 pfvfnum; + u16 count; + u16 next_to_use; + u16 next_to_clean; + dma_addr_t dma; + unsigned int size; + struct mucse_queue_stats stats; + struct u64_stats_sync syncp; } ____cacheline_internodealigned_in_smp; =20 +static inline u16 mucse_desc_unused(struct mucse_ring *ring) +{ + u16 ntc =3D ring->next_to_clean; + u16 ntu =3D ring->next_to_use; + + return ((ntc > ntu) ? 0 : ring->count) + ntc - ntu - 1; +} + +static inline __le64 build_ctob(u32 vlan_cmd, u32 mac_ip_len, u32 size) +{ + return cpu_to_le64(((u64)vlan_cmd << 32) | ((u64)mac_ip_len << 16) | + ((u64)size)); +} + +static inline struct netdev_queue *txring_txq(const struct mucse_ring *rin= g) +{ + return netdev_get_tx_queue(ring->netdev, ring->queue_index); +} + struct mucse_ring_container { struct mucse_ring *ring; u16 count; @@ -72,12 +137,11 @@ struct mucse_q_vector { struct mucse_ring ring[] ____cacheline_internodealigned_in_smp; }; =20 -struct mucse_stats { - u64 tx_dropped; -}; - #define MAX_Q_VECTORS 8 =20 +#define M_DEFAULT_TXD 512 +#define M_DEFAULT_TX_WORK 256 + enum mucse_state_t { __MUCSE_DOWN, }; @@ -86,7 +150,6 @@ struct mucse { struct net_device *netdev; struct pci_dev *pdev; struct mucse_hw hw; - struct mucse_stats stats; #define M_FLAG_MSI_EN BIT(0) #define M_FLAG_MSIX_SINGLE_EN BIT(1) #define M_FLAG_MSIX_EN BIT(2) @@ -96,6 +159,8 @@ struct mucse { struct mucse_ring *rx_ring[RNPGBE_MAX_QUEUES] ____cacheline_aligned_in_smp; struct mucse_q_vector *q_vector[MAX_Q_VECTORS]; + int tx_ring_item_count; + int tx_work_limit; int num_tx_queues; int num_q_vectors; int num_rx_queues; @@ -121,4 +186,8 @@ int rnpgbe_init_hw(struct mucse_hw *hw, int board_type); writel((val), (hw)->hw_addr + (reg)) #define mucse_hw_rd32(hw, reg) \ readl((hw)->hw_addr + (reg)) +#define mucse_ring_wr32(ring, reg, val) \ + writel((val), (ring)->ring_addr + (reg)) +#define mucse_ring_rd32(ring, reg) \ + readl((ring)->ring_addr + (reg)) #endif /* _RNPGBE_H */ diff --git a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_chip.c b/drivers/net/= ethernet/mucse/rnpgbe/rnpgbe_chip.c index 921cc325a991..291e77d573fe 100644 --- a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_chip.c +++ b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_chip.c @@ -93,6 +93,8 @@ static void rnpgbe_init_n500(struct mucse_hw *hw) =20 mbx->fwpf_ctrl_base =3D MUCSE_N500_FWPF_CTRL_BASE; mbx->fwpf_shm_base =3D MUCSE_N500_FWPF_SHM_BASE; + + hw->cycles_per_us =3D M_DEFAULT_N500_MHZ; } =20 /** @@ -110,6 +112,8 @@ static void rnpgbe_init_n210(struct mucse_hw *hw) =20 mbx->fwpf_ctrl_base =3D MUCSE_N210_FWPF_CTRL_BASE; mbx->fwpf_shm_base =3D MUCSE_N210_FWPF_SHM_BASE; + + hw->cycles_per_us =3D M_DEFAULT_N210_MHZ; } =20 /** diff --git a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_hw.h b/drivers/net/et= hernet/mucse/rnpgbe/rnpgbe_hw.h index 0dce78e4a91b..cbc593902030 100644 --- a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_hw.h +++ b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_hw.h @@ -7,12 +7,15 @@ #define MUCSE_N500_FWPF_CTRL_BASE 0x28b00 #define MUCSE_N500_FWPF_SHM_BASE 0x2d000 #define MUCSE_N500_RING_MSIX_BASE 0x28700 +#define M_DEFAULT_N500_MHZ 125 #define MUCSE_GBE_PFFW_MBX_CTRL_OFFSET 0x5500 #define MUCSE_GBE_FWPF_MBX_MASK_OFFSET 0x5700 #define MUCSE_N210_FWPF_CTRL_BASE 0x29400 #define MUCSE_N210_FWPF_SHM_BASE 0x2d900 #define MUCSE_N210_RING_MSIX_BASE 0x29000 +#define M_DEFAULT_N210_MHZ 62 =20 +#define TX_AXI_RW_EN 0xc #define RNPGBE_DMA_AXI_EN 0x0010 =20 #define RNPGBE_MAX_QUEUES 8 diff --git a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c b/drivers/net/e= thernet/mucse/rnpgbe/rnpgbe_lib.c index cb241dc339d8..1f7d2cf4af12 100644 --- a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c +++ b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c @@ -3,6 +3,8 @@ =20 #include #include +#include +#include =20 #include "rnpgbe_lib.h" #include "rnpgbe.h" @@ -26,6 +28,7 @@ static irqreturn_t rnpgbe_msix_other(int irq, void *data) =20 static void rnpgbe_irq_disable_queues(struct mucse_q_vector *q_vector) { + struct mucse_hw *hw =3D &q_vector->mucse->hw; struct mucse_ring *ring; =20 /* tx/rx use one register, different bit */ @@ -33,6 +36,8 @@ static void rnpgbe_irq_disable_queues(struct mucse_q_vect= or *q_vector) writel(INT_VALID, ring->trig); writel((RX_INT_MASK | TX_INT_MASK), ring->irq_mask); } + /* flush posted writes to ensure hardware sees the mask */ + readl(hw->hw_addr); } =20 /** @@ -76,6 +81,114 @@ static void rnpgbe_irq_enable_queues(struct mucse_q_vec= tor *q_vector) } } =20 +/** + * rnpgbe_clean_tx_irq - Reclaim resources after transmit completes + * @q_vector: structure containing interrupt and ring information + * @tx_ring: tx ring to clean + * @napi_budget: Used to determine if we are in netpoll + * + * @return: true is for work done within budget, otherwise false + **/ +static bool rnpgbe_clean_tx_irq(struct mucse_q_vector *q_vector, + struct mucse_ring *tx_ring, + int napi_budget) +{ + int budget =3D q_vector->mucse->tx_work_limit; + u64 total_bytes =3D 0, total_packets =3D 0; + struct mucse *mucse =3D q_vector->mucse; + struct mucse_tx_buffer *tx_buffer; + struct rnpgbe_tx_desc *tx_desc; + int i =3D tx_ring->next_to_clean; + + tx_buffer =3D &tx_ring->tx_buffer_info[i]; + tx_desc =3D M_TX_DESC(tx_ring, i); + i -=3D tx_ring->count; + + do { + struct rnpgbe_tx_desc *eop_desc =3D tx_buffer->next_to_watch; + + /* if next_to_watch is not set then there is no work pending */ + if (!eop_desc) + break; + + /* prevent any other reads prior to eop_desc */ + dma_rmb(); + + /* if eop DD is not set pending work has not been completed */ + if (!(eop_desc->vlan_cmd & cpu_to_le32(M_TXD_STAT_DD))) + break; + /* clear next_to_watch to prevent false hangs */ + tx_buffer->next_to_watch =3D NULL; + total_bytes +=3D tx_buffer->bytecount; + total_packets +=3D tx_buffer->gso_segs; + napi_consume_skb(tx_buffer->skb, napi_budget); + if (tx_buffer->mapped_as_page) { + dma_unmap_page(tx_ring->dev, + dma_unmap_addr(tx_buffer, dma), + dma_unmap_len(tx_buffer, len), + DMA_TO_DEVICE); + } else { + dma_unmap_single(tx_ring->dev, + dma_unmap_addr(tx_buffer, dma), + dma_unmap_len(tx_buffer, len), + DMA_TO_DEVICE); + } + tx_buffer->skb =3D NULL; + dma_unmap_len_set(tx_buffer, len, 0); + + /* unmap remaining buffers */ + while (tx_desc !=3D eop_desc) { + tx_buffer++; + tx_desc++; + i++; + if (unlikely(!i)) { + i -=3D tx_ring->count; + tx_buffer =3D tx_ring->tx_buffer_info; + tx_desc =3D M_TX_DESC(tx_ring, 0); + } + + /* unmap any remaining paged data */ + if (dma_unmap_len(tx_buffer, len)) { + dma_unmap_page(tx_ring->dev, + dma_unmap_addr(tx_buffer, dma), + dma_unmap_len(tx_buffer, len), + DMA_TO_DEVICE); + dma_unmap_len_set(tx_buffer, len, 0); + } + } + + /* move us one more past the eop_desc for start of next pkt */ + tx_buffer++; + tx_desc++; + i++; + if (unlikely(!i)) { + i -=3D tx_ring->count; + tx_buffer =3D tx_ring->tx_buffer_info; + tx_desc =3D M_TX_DESC(tx_ring, 0); + } + + prefetch(tx_desc); + budget--; + } while (likely(budget > 0)); + + i +=3D tx_ring->count; + tx_ring->next_to_clean =3D i; + u64_stats_update_begin(&tx_ring->syncp); + tx_ring->stats.bytes +=3D total_bytes; + tx_ring->stats.packets +=3D total_packets; + u64_stats_update_end(&tx_ring->syncp); + +#define TX_WAKE_THRESHOLD (DESC_NEEDED * 2) + __netif_txq_completed_wake(txring_txq(tx_ring), + total_packets, total_bytes, + mucse_desc_unused(tx_ring), + TX_WAKE_THRESHOLD, + !netif_carrier_ok(tx_ring->netdev) || + test_bit(__MUCSE_DOWN, &mucse->state)); + + return !!budget; +} + /** * rnpgbe_poll - NAPI Rx polling callback * @napi: structure for representing this polling device @@ -88,12 +201,22 @@ static int rnpgbe_poll(struct napi_struct *napi, int b= udget) { struct mucse_q_vector *q_vector =3D container_of(napi, struct mucse_q_vector, napi); + bool clean_complete =3D true; + struct mucse_ring *ring; int work_done =3D 0; =20 + mucse_for_each_ring(ring, q_vector->tx) { + if (!rnpgbe_clean_tx_irq(q_vector, ring, budget)) + clean_complete =3D false; + } + /* Exit if we are called by netpoll */ if (unlikely(!budget)) return 0; =20 + if (!clean_complete) + return budget; + if (likely(napi_complete_done(napi, work_done))) rnpgbe_irq_enable_queues(q_vector); =20 @@ -256,12 +379,17 @@ static int rnpgbe_alloc_q_vector(struct mucse *mucse, ring =3D q_vector->ring; =20 for (idx =3D 0; idx < txr_count; idx++) { + ring->dev =3D &mucse->pdev->dev; mucse_add_ring(ring, &q_vector->tx); + ring->count =3D mucse->tx_ring_item_count; + ring->netdev =3D mucse->netdev; ring->queue_index =3D eth_queue_idx + idx; ring->rnpgbe_queue_idx =3D txr_idx; ring->ring_addr =3D hw->hw_addr + RING_OFFSET(txr_idx); ring->irq_mask =3D ring->ring_addr + RNPGBE_DMA_INT_MASK; ring->trig =3D ring->ring_addr + RNPGBE_DMA_INT_TRIG; + ring->pfvfnum =3D hw->pfvfnum; + u64_stats_init(&ring->syncp); mucse->tx_ring[ring->queue_index] =3D ring; txr_idx +=3D step; ring++; @@ -613,11 +741,118 @@ static void rnpgbe_napi_disable_all(struct mucse *mu= cse) napi_disable(&mucse->q_vector[i]->napi); } =20 +/** + * rnpgbe_clean_tx_ring - Free Tx Buffers + * @tx_ring: ring to be cleaned + **/ +static void rnpgbe_clean_tx_ring(struct mucse_ring *tx_ring) +{ + struct mucse_tx_buffer *tx_buffer; + u16 i =3D tx_ring->next_to_clean; + unsigned long size; + + /* Stop hw. hardware design guarantees: + * - No new descriptors will be fetched after TX_START=3D0 + * - No DMA will be initiated for already-fetched descriptors + */ + mucse_ring_wr32(tx_ring, RNPGBE_TX_START, 0); + /* Flush posted write to ensure hardware sees the disable command. + * After this read completes, all TX DMA for this ring is + * guaranteed quiesced. + */ + (void)mucse_ring_rd32(tx_ring, RNPGBE_TX_START); + /* Wait for in-flight DMA to quiesce */ + usleep_range(300, 500); + + /* ring already cleared, nothing to do */ + if (!tx_ring->tx_buffer_info) + return; + + tx_buffer =3D &tx_ring->tx_buffer_info[i]; + + while (i !=3D tx_ring->next_to_use) { + struct rnpgbe_tx_desc *eop_desc, *tx_desc; + + dev_kfree_skb_any(tx_buffer->skb); + /* unmap skb header data */ + if (dma_unmap_len(tx_buffer, len)) { + if (tx_buffer->mapped_as_page) { + dma_unmap_page(tx_ring->dev, + dma_unmap_addr(tx_buffer, dma), + dma_unmap_len(tx_buffer, len), + DMA_TO_DEVICE); + } else { + dma_unmap_single(tx_ring->dev, + dma_unmap_addr(tx_buffer, dma), + dma_unmap_len(tx_buffer, len), + DMA_TO_DEVICE); + } + } + eop_desc =3D tx_buffer->next_to_watch; + tx_desc =3D M_TX_DESC(tx_ring, i); + /* unmap remaining buffers */ + while (tx_desc !=3D eop_desc) { + tx_buffer++; + tx_desc++; + i++; + if (unlikely(i =3D=3D tx_ring->count)) { + i =3D 0; + tx_buffer =3D tx_ring->tx_buffer_info; + tx_desc =3D M_TX_DESC(tx_ring, 0); + } + + /* unmap any remaining paged data */ + if (dma_unmap_len(tx_buffer, len)) + dma_unmap_page(tx_ring->dev, + dma_unmap_addr(tx_buffer, dma), + dma_unmap_len(tx_buffer, len), + DMA_TO_DEVICE); + } + /* move us one more past the eop_desc for start of next pkt */ + tx_buffer++; + i++; + if (unlikely(i =3D=3D tx_ring->count)) { + i =3D 0; + tx_buffer =3D tx_ring->tx_buffer_info; + } + } + + netdev_tx_reset_queue(txring_txq(tx_ring)); + size =3D sizeof(struct mucse_tx_buffer) * tx_ring->count; + memset(tx_ring->tx_buffer_info, 0, size); + /* Zero out the descriptor ring */ + memset(tx_ring->desc, 0, tx_ring->size); + tx_ring->next_to_use =3D 0; + tx_ring->next_to_clean =3D 0; +} + +/** + * rnpgbe_clean_all_tx_rings - Free Tx Buffers for all queues + * @mucse: board private structure + **/ +static void rnpgbe_clean_all_tx_rings(struct mucse *mucse) +{ + struct mucse_hw *hw =3D &mucse->hw; + u32 dma_axi_ctl; + + for (int i =3D 0; i < mucse->num_tx_queues; i++) + rnpgbe_clean_tx_ring(mucse->tx_ring[i]); + + dma_axi_ctl =3D mucse_hw_rd32(hw, RNPGBE_DMA_AXI_EN); + dma_axi_ctl &=3D ~TX_AXI_RW_EN; + mucse_hw_wr32(hw, RNPGBE_DMA_AXI_EN, dma_axi_ctl); +} + void rnpgbe_down(struct mucse *mucse) { + struct net_device *netdev =3D mucse->netdev; + set_bit(__MUCSE_DOWN, &mucse->state); + netif_tx_stop_all_queues(netdev); + netif_tx_disable(netdev); rnpgbe_napi_disable_all(mucse); rnpgbe_irq_disable(mucse); + rnpgbe_clean_all_tx_rings(mucse); } =20 /** @@ -626,11 +861,408 @@ void rnpgbe_down(struct mucse *mucse) **/ void rnpgbe_up_complete(struct mucse *mucse) { + struct net_device *netdev =3D mucse->netdev; + if (mucse->flags & (M_FLAG_MSIX_EN | M_FLAG_MSIX_SINGLE_EN)) rnpgbe_configure_msix(mucse); else rnpgbe_configure_msi(mucse); + rnpgbe_napi_enable_all(mucse); clear_bit(__MUCSE_DOWN, &mucse->state); rnpgbe_irq_enable(mucse); + netif_tx_start_all_queues(netdev); +} + +/** + * rnpgbe_free_tx_resources - Free Tx Resources per Queue + * @tx_ring: tx descriptor ring for a specific queue + * + * Free all transmit software resources + **/ +static void rnpgbe_free_tx_resources(struct mucse_ring *tx_ring) +{ + rnpgbe_clean_tx_ring(tx_ring); + vfree(tx_ring->tx_buffer_info); + tx_ring->tx_buffer_info =3D NULL; + /* if not set, then don't free */ + if (!tx_ring->desc) + return; + + dma_free_coherent(tx_ring->dev, tx_ring->size, tx_ring->desc, + tx_ring->dma); + tx_ring->desc =3D NULL; +} + +/** + * rnpgbe_setup_tx_resources - allocate Tx resources (Descriptors) + * @tx_ring: tx descriptor ring (for a specific queue) to setup + * @mucse: pointer to private structure + * + * @return: 0 on success, negative on failure + **/ +static int rnpgbe_setup_tx_resources(struct mucse_ring *tx_ring, + struct mucse *mucse) +{ + struct device *dev =3D tx_ring->dev; + int size; + + size =3D sizeof(struct mucse_tx_buffer) * tx_ring->count; + + tx_ring->tx_buffer_info =3D vzalloc(size); + if (!tx_ring->tx_buffer_info) + goto err_return; + /* round up to nearest 4K */ + tx_ring->size =3D tx_ring->count * sizeof(struct rnpgbe_tx_desc); + tx_ring->size =3D ALIGN(tx_ring->size, 4096); + tx_ring->desc =3D dma_alloc_coherent(dev, tx_ring->size, &tx_ring->dma, + GFP_KERNEL); + if (!tx_ring->desc) + goto err_free_buffer; + + tx_ring->next_to_use =3D 0; + tx_ring->next_to_clean =3D 0; + + return 0; + +err_free_buffer: + vfree(tx_ring->tx_buffer_info); +err_return: + tx_ring->tx_buffer_info =3D NULL; + return -ENOMEM; +} + +/** + * rnpgbe_configure_tx_ring - Configure Tx ring after Reset + * @mucse: pointer to private structure + * @ring: structure containing ring specific data + * + * Configure the Tx descriptor ring after a reset. + **/ +static void rnpgbe_configure_tx_ring(struct mucse *mucse, + struct mucse_ring *ring) +{ + struct mucse_hw *hw =3D &mucse->hw; + + /* Stop hw. hardware design guarantees: + * - No new descriptors will be fetched after TX_START=3D0 + * - No DMA will be initiated for already-fetched descriptors + */ + mucse_ring_wr32(ring, RNPGBE_TX_START, 0); + /* Flush posted write to ensure hardware sees the disable command. + * After this read completes, all TX DMA for this ring is + * guaranteed quiesced. + */ + (void)mucse_ring_rd32(ring, RNPGBE_TX_START); + /* Wait for in-flight DMA to quiesce */ + usleep_range(300, 500); + + mucse_ring_wr32(ring, RNPGBE_TX_BASE_ADDR_LO, (u32)ring->dma); + mucse_ring_wr32(ring, RNPGBE_TX_BASE_ADDR_HI, + (u32)(((u64)ring->dma) >> 32) | (hw->pfvfnum << 24)); + mucse_ring_wr32(ring, RNPGBE_TX_LEN, ring->count); + ring->next_to_clean =3D mucse_ring_rd32(ring, RNPGBE_TX_HEAD) & + (ring->count - 1); + ring->next_to_use =3D ring->next_to_clean; + ring->tail =3D ring->ring_addr + RNPGBE_TX_TAIL; + writel(ring->next_to_use, ring->tail); + mucse_ring_wr32(ring, RNPGBE_TX_FETCH_CTRL, M_DEFAULT_TX_FETCH); + mucse_ring_wr32(ring, RNPGBE_TX_INT_TIMER, + M_DEFAULT_INT_TIMER * hw->cycles_per_us); + mucse_ring_wr32(ring, RNPGBE_TX_INT_PKTCNT, M_DEFAULT_INT_PKTCNT); + /* Ensure all config is written before enabling queue */ + wmb(); + mucse_ring_wr32(ring, RNPGBE_TX_START, 1); +} + +/** + * rnpgbe_configure_tx - Configure Transmit Unit after Reset + * @mucse: pointer to private structure + * + * Configure the Tx DMA after a reset. + **/ +void rnpgbe_configure_tx(struct mucse *mucse) +{ + struct mucse_hw *hw =3D &mucse->hw; + u32 i, dma_axi_ctl; + + dma_axi_ctl =3D mucse_hw_rd32(hw, RNPGBE_DMA_AXI_EN); + dma_axi_ctl |=3D TX_AXI_RW_EN; + mucse_hw_wr32(hw, RNPGBE_DMA_AXI_EN, dma_axi_ctl); + /* Setup the HW Tx Head and Tail descriptor pointers */ + for (i =3D 0; i < mucse->num_tx_queues; i++) + rnpgbe_configure_tx_ring(mucse, mucse->tx_ring[i]); +} + +/** + * rnpgbe_setup_all_tx_resources - allocate all queues Tx resources + * @mucse: pointer to private structure + * + * Allocate memory for tx_ring. + * + * @return: 0 on success, negative on failure + **/ +int rnpgbe_setup_all_tx_resources(struct mucse *mucse) +{ + int i, err =3D 0; + + for (i =3D 0; i < mucse->num_tx_queues; i++) { + err =3D rnpgbe_setup_tx_resources(mucse->tx_ring[i], mucse); + if (!err) + continue; + + goto err_free_res; + } + + return 0; +err_free_res: + while (i--) + rnpgbe_free_tx_resources(mucse->tx_ring[i]); + return err; +} + +/** + * rnpgbe_free_all_tx_resources - Free Tx Resources for All Queues + * @mucse: pointer to private structure + * + * Free all transmit software resources + **/ +void rnpgbe_free_all_tx_resources(struct mucse *mucse) +{ + for (int i =3D 0; i < (mucse->num_tx_queues); i++) + rnpgbe_free_tx_resources(mucse->tx_ring[i]); +} + +static int rnpgbe_tx_map(struct mucse_ring *tx_ring, + struct mucse_tx_buffer *first, u32 mac_ip_len, + u32 tx_flags) +{ + struct sk_buff *skb =3D first->skb; + struct skb_shared_info *shinfo =3D skb_shinfo(skb); + /* hw need this in high 8 bytes desc */ + u64 fun_id =3D ((u64)(tx_ring->pfvfnum) << (56)); + struct mucse_tx_buffer *tx_buffer; + int nr_frags =3D shinfo->nr_frags; + struct rnpgbe_tx_desc *tx_desc; + u16 i =3D tx_ring->next_to_use; + unsigned int data_len, size; + int frag_idx =3D 0; + skb_frag_t *frag; + dma_addr_t dma; + + tx_desc =3D M_TX_DESC(tx_ring, i); + size =3D skb_headlen(skb); + data_len =3D skb->data_len; + + if (size) { + dma =3D dma_map_single(tx_ring->dev, skb->data, size, + DMA_TO_DEVICE); + first->mapped_as_page =3D false; + } else if (data_len) { + while (frag_idx < nr_frags && + !skb_frag_size(&shinfo->frags[frag_idx])) + frag_idx++; + + if (frag_idx =3D=3D nr_frags) + goto err_unmap; + + frag =3D &shinfo->frags[frag_idx]; + size =3D skb_frag_size(frag); + + dma =3D skb_frag_dma_map(tx_ring->dev, frag, 0, + size, DMA_TO_DEVICE); + first->mapped_as_page =3D true; + data_len -=3D size; + frag++; + } else { + goto err_unmap; + } + + tx_buffer =3D first; + + dma_unmap_len_set(tx_buffer, len, 0); + dma_unmap_addr_set(tx_buffer, dma, 0); + + for (;; frag_idx++) { + if (dma_mapping_error(tx_ring->dev, dma)) + goto err_unmap; + + /* record length, and DMA address */ + dma_unmap_len_set(tx_buffer, len, size); + dma_unmap_addr_set(tx_buffer, dma, dma); + + tx_desc->pkt_addr =3D cpu_to_le64(dma | fun_id); + + while (unlikely(size > M_MAX_DATA_PER_TXD)) { + tx_desc->vlan_cmd_bsz =3D build_ctob(tx_flags, + mac_ip_len, + M_MAX_DATA_PER_TXD); + i++; + tx_desc++; + if (i =3D=3D tx_ring->count) { + tx_desc =3D M_TX_DESC(tx_ring, 0); + i =3D 0; + } + dma +=3D M_MAX_DATA_PER_TXD; + size -=3D M_MAX_DATA_PER_TXD; + tx_desc->pkt_addr =3D cpu_to_le64(dma | fun_id); + } + + if (likely(!data_len)) + break; + tx_desc->vlan_cmd_bsz =3D build_ctob(tx_flags, mac_ip_len, size); + i++; + tx_desc++; + if (i =3D=3D tx_ring->count) { + tx_desc =3D M_TX_DESC(tx_ring, 0); + i =3D 0; + } + + while (frag_idx < nr_frags && + !skb_frag_size(&shinfo->frags[frag_idx])) + frag_idx++; + + if (frag_idx =3D=3D nr_frags) + goto err_unmap; + + frag =3D &shinfo->frags[frag_idx]; + size =3D skb_frag_size(frag); + data_len -=3D size; + dma =3D skb_frag_dma_map(tx_ring->dev, frag, 0, size, + DMA_TO_DEVICE); + tx_buffer =3D &tx_ring->tx_buffer_info[i]; + tx_buffer->mapped_as_page =3D true; + } + + /* write last descriptor with RS and EOP bits */ + tx_desc->vlan_cmd_bsz =3D build_ctob(tx_flags | M_TXD_CMD_EOP | + M_TXD_CMD_RS, + mac_ip_len, size); + + /* + * Force memory writes to complete before letting h/w know there + * are new descriptors to fetch. (Only applicable for weak-ordered + * memory model archs, such as IA-64). + * + * We also need this memory barrier to make certain all of the + * status bits have been updated before next_to_watch is written. + */ + dma_wmb(); + /* set next_to_watch value indicating a packet is present */ + first->next_to_watch =3D tx_desc; + i++; + if (i =3D=3D tx_ring->count) + i =3D 0; + tx_ring->next_to_use =3D i; + skb_tx_timestamp(skb); + netdev_tx_sent_queue(txring_txq(tx_ring), first->bytecount); + /* notify HW of packet */ + writel(i, tx_ring->tail); + + return 0; +err_unmap: + for (;;) { + tx_buffer =3D &tx_ring->tx_buffer_info[i]; + if (dma_unmap_len(tx_buffer, len)) { + if (tx_buffer->mapped_as_page) { + dma_unmap_page(tx_ring->dev, + dma_unmap_addr(tx_buffer, dma), + dma_unmap_len(tx_buffer, len), + DMA_TO_DEVICE); + } else { + dma_unmap_single(tx_ring->dev, + dma_unmap_addr(tx_buffer, dma), + dma_unmap_len(tx_buffer, len), + DMA_TO_DEVICE); + } + } + dma_unmap_len_set(tx_buffer, len, 0); + dma_unmap_addr_set(tx_buffer, dma, 0); + if (tx_buffer =3D=3D first) + break; + if (i =3D=3D 0) + i +=3D tx_ring->count; + i--; + } + dev_kfree_skb_any(first->skb); + first->skb =3D NULL; + tx_ring->next_to_use =3D i; + + return -ENOMEM; +} + +netdev_tx_t rnpgbe_xmit_frame_ring(struct sk_buff *skb, + struct mucse_ring *tx_ring) +{ + u16 count =3D TXD_USE_COUNT(skb_headlen(skb)); + /* hw requires non-zero mac_ip_len even without offload; + * Will be updated per-packet when offload is added + */ + u32 mac_ip_len =3D M_DEFAULT_MAC_IP_LEN; + struct mucse_tx_buffer *first; + u32 tx_flags =3D 0; + unsigned short f; + + for (f =3D 0; f < skb_shinfo(skb)->nr_frags; f++) { + skb_frag_t *frag_temp =3D &skb_shinfo(skb)->frags[f]; + + count +=3D TXD_USE_COUNT(skb_frag_size(frag_temp)); + } + + if (!netif_txq_maybe_stop(txring_txq(tx_ring), + mucse_desc_unused(tx_ring), + count + RESV_DESC_NEEDED, + count + RESV_DESC_NEEDED)) + return NETDEV_TX_BUSY; + + /* record the location of the first descriptor for this packet */ + first =3D &tx_ring->tx_buffer_info[tx_ring->next_to_use]; + first->skb =3D skb; + first->bytecount =3D skb->len; + first->gso_segs =3D 1; + + if (rnpgbe_tx_map(tx_ring, first, mac_ip_len, tx_flags)) { + atomic64_inc(&tx_ring->stats.dropped); + + goto out; + } + + /* NETDEV_TX_BUSY is expensive. So stop advancing the TX queue. */ + netif_txq_maybe_stop(txring_txq(tx_ring), + mucse_desc_unused(tx_ring), + DESC_NEEDED, DESC_NEEDED); +out: + return NETDEV_TX_OK; +} + +/** + * rnpgbe_get_stats64 - Get stats for this netdev + * @netdev: network interface device structure + * @stats: stats data + **/ +void rnpgbe_get_stats64(struct net_device *netdev, + struct rtnl_link_stats64 *stats) +{ + struct mucse *mucse =3D netdev_priv(netdev); + int i; + + rcu_read_lock(); + for (i =3D 0; i < mucse->num_tx_queues; i++) { + struct mucse_ring *ring =3D READ_ONCE(mucse->tx_ring[i]); + u64 bytes, packets, dropped; + unsigned int start; + + if (ring) { + do { + start =3D u64_stats_fetch_begin(&ring->syncp); + packets =3D ring->stats.packets; + bytes =3D ring->stats.bytes; + dropped =3D atomic64_read(&ring->stats.dropped); + } while (u64_stats_fetch_retry(&ring->syncp, start)); + stats->tx_packets +=3D packets; + stats->tx_dropped +=3D dropped; + stats->tx_bytes +=3D bytes; + } + } + rcu_read_unlock(); } diff --git a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.h b/drivers/net/e= thernet/mucse/rnpgbe/rnpgbe_lib.h index d38c08df6b7c..ba9206678ede 100644 --- a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.h +++ b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.h @@ -6,17 +6,40 @@ =20 struct mucse; struct mucse_hw; +struct mucse_ring; =20 #define RING_OFFSET(n) (0x1000 + 0x100 * (n)) +#define RNPGBE_TX_START 0x18 #define RNPGBE_DMA_INT_MASK 0x24 #define TX_INT_MASK BIT(1) #define RX_INT_MASK BIT(0) #define INT_VALID (BIT(16) | BIT(17)) #define RNPGBE_DMA_INT_TRIG 0x2c /* lost-interrupt recovery trigger = */ +#define RNPGBE_TX_BASE_ADDR_HI 0x60 +#define RNPGBE_TX_BASE_ADDR_LO 0x64 +#define RNPGBE_TX_LEN 0x68 +#define RNPGBE_TX_HEAD 0x6c +#define RNPGBE_TX_TAIL 0x70 +#define M_DEFAULT_TX_FETCH 0x80008 +#define RNPGBE_TX_FETCH_CTRL 0x74 +#define M_DEFAULT_INT_TIMER 100 +#define RNPGBE_TX_INT_TIMER 0x78 +#define M_DEFAULT_INT_PKTCNT 48 +#define RNPGBE_TX_INT_PKTCNT 0x7c +#define RNPGBE_DMA_INT_TRIG 0x2c /* | 31:24 | .... | 15:8 | 7:0 | */ /* | pfvfnum | | tx vector | rx vector | */ #define RING_VECTOR(n) (0x04 * (n)) =20 +#define M_MAX_TXD_PWR 12 +#define M_MAX_DATA_PER_TXD (0x1 << M_MAX_TXD_PWR) +#define TXD_USE_COUNT(S) DIV_ROUND_UP((S), M_MAX_DATA_PER_TXD) +#define DESC_NEEDED (MAX_SKB_FRAGS + 4) +/* 2 desc gap to keep tail from touching head */ +/* 1 desc for context descriptor */ +#define RESV_DESC_NEEDED 3 +/* hw require this not zero */ +#define M_DEFAULT_MAC_IP_LEN 20 #define mucse_for_each_ring(pos, head)\ for (typeof((head).ring) __pos =3D (head).ring;\ __pos ? ({ pos =3D __pos; 1; }) : 0;\ @@ -31,4 +54,11 @@ void rnpgbe_free_irq(struct mucse *mucse); void rnpgbe_irq_disable(struct mucse *mucse); void rnpgbe_down(struct mucse *mucse); void rnpgbe_up_complete(struct mucse *mucse); +void rnpgbe_configure_tx(struct mucse *mucse); +int rnpgbe_setup_all_tx_resources(struct mucse *mucse); +void rnpgbe_free_all_tx_resources(struct mucse *mucse); +netdev_tx_t rnpgbe_xmit_frame_ring(struct sk_buff *skb, + struct mucse_ring *tx_ring); +void rnpgbe_get_stats64(struct net_device *netdev, + struct rtnl_link_stats64 *stats); #endif diff --git a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_main.c b/drivers/net/= ethernet/mucse/rnpgbe/rnpgbe_main.c index f0b90bf8bd55..b8f79603dd97 100644 --- a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_main.c +++ b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_main.c @@ -26,6 +26,17 @@ static struct pci_device_id rnpgbe_pci_tbl[] =3D { { }, }; =20 +/** + * rnpgbe_configure - Configure info to hw + * @mucse: pointer to private structure + * + * rnpgbe_configure configure mac, tx, rx regs to hw + **/ +static void rnpgbe_configure(struct mucse *mucse) +{ + rnpgbe_configure_tx(mucse); +} + /** * rnpgbe_open - Called when a network interface is made active * @netdev: network interface device structure @@ -49,6 +60,11 @@ static int rnpgbe_open(struct net_device *netdev) if (err) goto err_free_irqs; =20 + err =3D rnpgbe_setup_all_tx_resources(mucse); + if (err) + goto err_free_irqs; + + rnpgbe_configure(mucse); rnpgbe_up_complete(mucse); =20 return 0; @@ -72,6 +88,7 @@ static int rnpgbe_close(struct net_device *netdev) =20 rnpgbe_down(mucse); rnpgbe_free_irq(mucse); + rnpgbe_free_all_tx_resources(mucse); =20 return 0; } @@ -81,25 +98,32 @@ static int rnpgbe_close(struct net_device *netdev) * @skb: skb structure to be sent * @netdev: network interface device structure * - * Return: NETDEV_TX_OK + * Return: NETDEV_TX_OK or NETDEV_TX_BUSY when insufficient descriptors **/ static netdev_tx_t rnpgbe_xmit_frame(struct sk_buff *skb, struct net_device *netdev) { struct mucse *mucse =3D netdev_priv(netdev); + struct mucse_ring *tx_ring; =20 - dev_kfree_skb_any(skb); - mucse->stats.tx_dropped++; + tx_ring =3D mucse->tx_ring[skb_get_queue_mapping(skb)]; =20 - return NETDEV_TX_OK; + return rnpgbe_xmit_frame_ring(skb, tx_ring); } =20 static const struct net_device_ops rnpgbe_netdev_ops =3D { .ndo_open =3D rnpgbe_open, .ndo_stop =3D rnpgbe_close, .ndo_start_xmit =3D rnpgbe_xmit_frame, + .ndo_get_stats64 =3D rnpgbe_get_stats64, }; =20 +static void rnpgbe_sw_init(struct mucse *mucse) +{ + mucse->tx_ring_item_count =3D M_DEFAULT_TXD; + mucse->tx_work_limit =3D M_DEFAULT_TX_WORK; +} + /** * rnpgbe_add_adapter - Add netdev for this pci_dev * @pdev: PCI device information structure @@ -173,6 +197,7 @@ static int rnpgbe_add_adapter(struct pci_dev *pdev, } =20 netdev->netdev_ops =3D &rnpgbe_netdev_ops; + rnpgbe_sw_init(mucse); err =3D rnpgbe_reset_hw(hw); if (err) { dev_err(&pdev->dev, "Hw reset failed %d\n", err); @@ -202,6 +227,9 @@ static int rnpgbe_add_adapter(struct pci_dev *pdev, goto err_clear_interrupt; } =20 + netdev->features |=3D NETIF_F_SG; + netdev->features |=3D NETIF_F_HIGHDMA; + err =3D register_netdev(netdev); if (err) goto err_remove_mbx; @@ -245,7 +273,7 @@ static int rnpgbe_probe(struct pci_dev *pdev, const str= uct pci_device_id *id) if (err) return err; =20 - err =3D dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(56)); + err =3D dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(56)); if (err) { dev_err(&pdev->dev, "No usable DMA configuration, aborting %d\n", err); --=20 2.25.1 From nobody Mon Jun 8 09:49:26 2026 Received: from smtpbgau2.qq.com (smtpbgau2.qq.com [54.206.34.216]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 1B9393C5522 for ; Thu, 4 Jun 2026 11:29:23 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=54.206.34.216 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780572567; cv=none; b=XTqNf0ZNWL6sKRZzdc6DXxmIgcfLyxSdZcMNvYOoLroHOqRlTfjK5nXDEGeuRfbcO9zDkPowvmk6NiBWtV2sIoipXmEz1P5rrV3JusHlaqlDVp/bPpXSYOyKGmsGD5JWZ3VC9pYq32LgWRvKIoGJ91UIn1K34oJfYgaPmBjna6U= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780572567; c=relaxed/simple; bh=BO5vidjIzoLHYxN2VfLHFJ9ffomq4tDQiQwy7sG/8KI=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=I9E6fjKXCELfqm1vfExiY7S4PkS9KbbUA6UjIw/X177wYmyFhLDrB/P172HbrHrbdDO712uiVR92RkHLkddRNJblElpXYItgvXTwCVs+d3mmzbH51a9hPzEV3EughRLnQkqDodgpY+D17RMq9V46Ne4M+7vfBjff2K9w9aHA9RU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=mucse.com; spf=pass smtp.mailfrom=mucse.com; arc=none smtp.client-ip=54.206.34.216 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=mucse.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=mucse.com X-QQ-mid: zesmtpgz8t1780572489t5c59570d X-QQ-Originating-IP: bb597Gz5+AmLoqsHg08aG7MsynYBU6CsBbPEvKcemD0= Received: from localhost.localdomain ( [203.174.112.180]) by bizesmtp.qq.com (ESMTP) with id ; Thu, 04 Jun 2026 19:28:07 +0800 (CST) X-QQ-SSF: 0000000000000000000000000000000 X-QQ-GoodBg: 0 X-BIZMAIL-ID: 5194656920311394621 EX-QQ-RecipientCnt: 13 From: Dong Yibo To: andrew+netdev@lunn.ch, davem@davemloft.net, edumazet@google.com, kuba@kernel.org, pabeni@redhat.com, danishanwar@ti.com, vadim.fedorenko@linux.dev, horms@kernel.org, u.kleine-koenig@baylibre.com Cc: linux-kernel@vger.kernel.org, netdev@vger.kernel.org, dong100@mucse.com, yaojun@mucse.com Subject: [PATCH net-next v6 3/4] net: rnpgbe: Add RX packet reception support Date: Thu, 4 Jun 2026 19:27:49 +0800 Message-Id: <20260604112750.769215-4-dong100@mucse.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20260604112750.769215-1-dong100@mucse.com> References: <20260604112750.769215-1-dong100@mucse.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-QQ-SENDSIZE: 520 Feedback-ID: zesmtpgz:mucse.com:qybglogicsvrgz:qybglogicsvrgz3a-1 X-QQ-XMAILINFO: OXWhkXa0b/rw/y3NvoBL3n55RxnhzOx/+R4k43DyjRr9PWjCtFuSlnIp v9t7ZdyNvp/lUDmalwEwYhux6cXlYKTS55dqJhGr035rgQ+PeV+BBXTmHjDhPoYulO3MKWw jS9fLEyK/pxXzZ4TPYniKg5SNfrZFeDQFvdAIjxm1cOq6XDpGCoV1xiWXDulPohQNcIUqt5 GWUq8jNCnc0KzUqoAJUbjhbaw53ssseCh0SPzZlN/0qjX6Zz3wQ1OT/x8fUG836yOjH0mOe GMvU39Q1bnyGOEdGqECe0DvZiqn3GKxBho1UYCjIWmnkyGPW+pKPvr68omyqC24D6FyGSG6 1oyy8jNGkI8f7zukaUm5iZ6bJaXvDtPalfI6z05SRu1sBoKu9hQhZRIMnGL+7cd3d6V/lyO gDxlOivlKqSQ73zdqjF+7uESQ3RD1APgJqydMjTR6+c0fyAjqpwXsK47FdkbrNcZI81tyfE 8F0CfNw84Q1cqxyWiiZHNJ0pXHkMzFaeF9EYlG5KSYKTJ5oLNqcdmR5ELXbfWeNzJ//R83J CjsgzgC76IW2n5IPDl4Vb1B12BIMOwYYe3VNbn3vwzIA1/bv8UT3PU20RHFAwiptAyKRzhT 1TSy0/Wo/9TdN63wb379gzJ1kIlMbvfGwpKhrTUuxUKzrDrRRpK/Z2k29tTiMwvricT/gab WtCDubj1Xt13EZN4d6ctNxyNNzTeFfZFy1zZzPVuClU/4OkFbU/CD2rZdRw4iIIGCkqXhKS yUtV2FFjtm+o5KCmxLBrFQuXHu6AvwSboe7XgNYjwyEur6VmONq5jINUg0Qb3/oo9w4C1Uv c3i+dg/N52jZEKA1bhmBzeiB/+IPKeV/D2aQ9NoGFnlZSLMQOC94TlGUytGCsEdJmHsTGij fs9BfUDILdJJIPlXTZ4SlDNNiaekyMg5/f3RK2tBdNcnlO0VlBu/stnXsZHZI6fIQvBQypU XIzPw2vbRqheqgLIFiGeudWFFCvu/chsojaFm3ycSllXnUZA402/3W0Fu2HTU8LNWnAao3z RCn/u8nTAsC8lBiGtc8Sv3jRA99UYGG+XntK9CJ1iC7Pd9E57PQ2dAj3fM/DI4dZM/qR4c2 A== X-QQ-XMRINFO: MPJ6Tf5t3I/ylTmHUqvI8+Wpn+Gzalws3A== X-QQ-RECHKSPAM: 0 Content-Type: text/plain; charset="utf-8" Add basic RX packet reception infrastructure to the rnpgbe driver: - Add RX descriptor structure (union rnpgbe_rx_desc) with write-back format for hardware status - Add RX buffer management using page_pool for efficient page recycling (basic version, 1 page for 1 descriptor, no page splitting; ring depth is fixed at 512, 'ethtool ringparm' not yet supported) - Implement NAPI poll callback (rnpgbe_poll) for RX processing - Add RX ring setup and cleanup functions - Implement packet building from page buffer - Add RX statistics tracking Signed-off-by: Dong Yibo --- drivers/net/ethernet/mucse/Kconfig | 1 + drivers/net/ethernet/mucse/rnpgbe/rnpgbe.h | 60 +- drivers/net/ethernet/mucse/rnpgbe/rnpgbe_hw.h | 1 + .../net/ethernet/mucse/rnpgbe/rnpgbe_lib.c | 696 ++++++++++++++++++ .../net/ethernet/mucse/rnpgbe/rnpgbe_lib.h | 24 +- .../net/ethernet/mucse/rnpgbe/rnpgbe_main.c | 8 + 6 files changed, 788 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/mucse/Kconfig b/drivers/net/ethernet/mucs= e/Kconfig index 0b3e853d625f..be0fdf268484 100644 --- a/drivers/net/ethernet/mucse/Kconfig +++ b/drivers/net/ethernet/mucse/Kconfig @@ -19,6 +19,7 @@ if NET_VENDOR_MUCSE config MGBE tristate "Mucse(R) 1GbE PCI Express adapters support" depends on PCI + select PAGE_POOL help This driver supports Mucse(R) 1GbE PCI Express family of adapters. diff --git a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe.h b/drivers/net/ether= net/mucse/rnpgbe/rnpgbe.h index b2045891f205..5982fb975642 100644 --- a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe.h +++ b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe.h @@ -8,6 +8,7 @@ #include #include #include +#include =20 #include "rnpgbe_hw.h" =20 @@ -61,7 +62,32 @@ struct rnpgbe_tx_desc { #define M_TXD_CMD_EOP 0x010000 /* End of Packet */ }; =20 +union rnpgbe_rx_desc { + struct { + __le64 pkt_addr; /* Packet buffer address */ + __le64 resv_cmd; /* cmd status */ + }; + struct { + __le32 rss_hash; /* RSS HASH */ + __le16 mark; /* mark info */ + __le16 rev1; + __le16 len; /* Packet length */ + __le16 padding_len; + __le16 vlan; /* VLAN tag */ + __le16 cmd; /* cmd status */ +#define M_RXD_STAT_DD BIT(1) /* Descriptor Done */ +#define M_RXD_STAT_EOP BIT(0) /* End of Packet */ + } wb; +}; + #define M_TX_DESC(R, i) (&(((struct rnpgbe_tx_desc *)((R)->desc))[i])) +#define M_RX_DESC(R, i) (&(((union rnpgbe_rx_desc *)((R)->desc))[i])) + +static inline __le16 rnpgbe_test_staterr(union rnpgbe_rx_desc *rx_desc, + const u16 stat_err_bits) +{ + return rx_desc->wb.cmd & cpu_to_le16(stat_err_bits); +} =20 struct mucse_tx_buffer { struct rnpgbe_tx_desc *next_to_watch; @@ -79,13 +105,24 @@ struct mucse_queue_stats { atomic64_t dropped; }; =20 +struct mucse_rx_buffer { + struct sk_buff *skb; + dma_addr_t dma; + struct page *page; + u32 page_offset; +}; + struct mucse_ring { struct mucse_ring *next; struct mucse_q_vector *q_vector; struct net_device *netdev; struct device *dev; + struct page_pool *page_pool; void *desc; - struct mucse_tx_buffer *tx_buffer_info; + union { + struct mucse_tx_buffer *tx_buffer_info; + struct mucse_rx_buffer *rx_buffer_info; + }; void __iomem *ring_addr; void __iomem *tail; void __iomem *irq_mask; @@ -101,6 +138,8 @@ struct mucse_ring { unsigned int size; struct mucse_queue_stats stats; struct u64_stats_sync syncp; + bool drop_status; + struct delayed_work alloc_retry_work; } ____cacheline_internodealigned_in_smp; =20 static inline u16 mucse_desc_unused(struct mucse_ring *ring) @@ -111,6 +150,23 @@ static inline u16 mucse_desc_unused(struct mucse_ring = *ring) return ((ntc > ntu) ? 0 : ring->count) + ntc - ntu - 1; } =20 +static inline u16 mucse_desc_unused_rx(struct mucse_ring *ring) +{ + u16 ntc =3D ring->next_to_clean; + u16 ntu =3D ring->next_to_use; + + /* Reserve 16 descriptors to keep the RX head aligned to a 16-descriptor + * boundary after refill. Buffers are always refilled in batches of 16 + * (M_RX_BUFFER_WRITE) and the hardware DMA engine performs cache-line + * prefetch based on the current head position. Keeping the head at a + * 16-aligned boundary prevents partial cache-line fetches and improves + * DMA efficiency. + * + * Effective ring depth: ring->count - 16 + */ + return ((ntc > ntu) ? 0 : ring->count) + ntc - ntu - 16; +} + static inline __le64 build_ctob(u32 vlan_cmd, u32 mac_ip_len, u32 size) { return cpu_to_le64(((u64)vlan_cmd << 32) | ((u64)mac_ip_len << 16) | @@ -140,6 +196,7 @@ struct mucse_q_vector { #define MAX_Q_VECTORS 8 =20 #define M_DEFAULT_TXD 512 +#define M_DEFAULT_RXD 512 #define M_DEFAULT_TX_WORK 256 =20 enum mucse_state_t { @@ -163,6 +220,7 @@ struct mucse { int tx_work_limit; int num_tx_queues; int num_q_vectors; + int rx_ring_item_count; int num_rx_queues; char mbx_name[32]; unsigned long state; diff --git a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_hw.h b/drivers/net/et= hernet/mucse/rnpgbe/rnpgbe_hw.h index cbc593902030..03688586b447 100644 --- a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_hw.h +++ b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_hw.h @@ -16,6 +16,7 @@ #define M_DEFAULT_N210_MHZ 62 =20 #define TX_AXI_RW_EN 0xc +#define RX_AXI_RW_EN 0x03 #define RNPGBE_DMA_AXI_EN 0x0010 =20 #define RNPGBE_MAX_QUEUES 8 diff --git a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c b/drivers/net/e= thernet/mucse/rnpgbe/rnpgbe_lib.c index 1f7d2cf4af12..86017aa8d83b 100644 --- a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c +++ b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c @@ -3,8 +3,10 @@ =20 #include #include +#include #include #include +#include =20 #include "rnpgbe_lib.h" #include "rnpgbe.h" @@ -189,6 +191,398 @@ static bool rnpgbe_clean_tx_irq(struct mucse_q_vector= *q_vector, return !!budget; } =20 +static bool mucse_alloc_mapped_page(struct mucse_ring *rx_ring, + struct mucse_rx_buffer *bi) +{ + struct page *page =3D bi->page; + dma_addr_t dma; + + if (page) { + /* Buffer is being reused without going back through the + * page_pool. Do dma_sync for hw use. + */ + dma_sync_single_range_for_device(rx_ring->dev, bi->dma, + bi->page_offset, + PAGE_SIZE - bi->page_offset, + DMA_FROM_DEVICE); + return true; + } + + page =3D page_pool_dev_alloc_pages(rx_ring->page_pool); + if (unlikely(!page)) + return false; + dma =3D page_pool_get_dma_addr(page); + + bi->dma =3D dma; + bi->page =3D page; + bi->page_offset =3D RNPGBE_SKB_PAD; + + return true; +} + +static void mucse_update_rx_tail(struct mucse_ring *rx_ring, + u32 val) +{ + rx_ring->next_to_use =3D val; + writel(val, rx_ring->tail); +} + +/** + * rnpgbe_alloc_rx_buffers - Replace used receive buffers + * @rx_ring: ring to place buffers on + * @cleaned_count: number of buffers to replace + * @return: true if alloc failed + **/ +static bool rnpgbe_alloc_rx_buffers(struct mucse_ring *rx_ring, + u16 cleaned_count) +{ + u64 fun_id =3D ((u64)(rx_ring->pfvfnum) << 56); + union rnpgbe_rx_desc *rx_desc; + u16 i =3D rx_ring->next_to_use; + struct mucse_rx_buffer *bi; + bool err =3D false; + u64 addr; + /* nothing to do */ + if (!cleaned_count) + return err; + + rx_desc =3D M_RX_DESC(rx_ring, i); + bi =3D &rx_ring->rx_buffer_info[i]; + i -=3D rx_ring->count; + + do { + if (!mucse_alloc_mapped_page(rx_ring, bi)) { + err =3D true; + break; + } + + addr =3D (u64)(bi->dma + bi->page_offset); + rx_desc->pkt_addr =3D cpu_to_le64(addr | fun_id); + /* clean dd */ + rx_desc->resv_cmd =3D 0; + rx_desc++; + bi++; + i++; + if (unlikely(!i)) { + rx_desc =3D M_RX_DESC(rx_ring, 0); + bi =3D rx_ring->rx_buffer_info; + i -=3D rx_ring->count; + } + cleaned_count--; + } while (cleaned_count); + + i +=3D rx_ring->count; + + if (rx_ring->next_to_use !=3D i) { + /* + * Force memory writes to complete before letting h/w know + * there are new rx descriptors to fetch. (Only applicable + * for weak-ordered memory model archs, such as IA-64). + */ + dma_wmb(); + /* Notify hw new rx descriptors is ready */ + mucse_update_rx_tail(rx_ring, i); + } + + return err; +} + +/** + * rnpgbe_get_buffer - Get the rx_buffer to be used + * @rx_ring: pointer to rx ring + * @skb: pointer skb for this packet + * @size: data size in this desc + * @return: rx_buffer. + **/ +static struct mucse_rx_buffer *rnpgbe_get_buffer(struct mucse_ring *rx_rin= g, + struct sk_buff **skb, + const unsigned int size) +{ + struct mucse_rx_buffer *rx_buffer; + + rx_buffer =3D &rx_ring->rx_buffer_info[rx_ring->next_to_clean]; + *skb =3D rx_buffer->skb; + prefetchw(page_address(rx_buffer->page) + rx_buffer->page_offset); + /* we are reusing so sync this buffer for CPU use */ + dma_sync_single_range_for_cpu(rx_ring->dev, rx_buffer->dma, + rx_buffer->page_offset, size, + DMA_FROM_DEVICE); + + return rx_buffer; +} + +/** + * rnpgbe_add_rx_frag - Add no-linear data to the skb + * @rx_buffer: pointer to rx_buffer + * @skb: pointer skb for this packet + * @size: data size in this desc + **/ +static void rnpgbe_add_rx_frag(struct mucse_rx_buffer *rx_buffer, + struct sk_buff *skb, + unsigned int size) +{ + /* FIXME: truesize is PAGE_SIZE for 1 page =3D 1 descriptor. + * Optimize with page splitting later when refactoring + * the Rx buffer management. + */ + unsigned int truesize =3D PAGE_SIZE; + + skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, rx_buffer->page, + rx_buffer->page_offset, size, truesize); +} + +/** + * rnpgbe_build_skb - Try to build a sbk based on rx_buffer + * @rx_buffer: pointer to rx_buffer + * @size: data size in this desc + * @return: skb for this rx_buffer + **/ +static struct sk_buff *rnpgbe_build_skb(struct mucse_rx_buffer *rx_buffer, + unsigned int size) +{ + void *va =3D page_address(rx_buffer->page) + rx_buffer->page_offset; + unsigned int truesize =3D PAGE_SIZE; + struct sk_buff *skb; + + net_prefetch(va); + /* build an skb around the page buffer */ + skb =3D build_skb(va - RNPGBE_SKB_PAD, truesize); + if (unlikely(!skb)) + return NULL; + /* update pointers within the skb to store the data */ + skb_reserve(skb, RNPGBE_SKB_PAD); + __skb_put(skb, size); + skb_mark_for_recycle(skb); + + return skb; +} + +/** + * rnpgbe_is_non_eop - Process handling of non-EOP buffers + * @rx_ring: rx ring being processed + * @rx_desc: rx descriptor for current buffer + * @skb: current socket buffer containing buffer in progress + * + * This function updates next to clean. If the buffer is an EOP buffer + * this function exits returning false, otherwise it will place the + * sk_buff in the next buffer to be chained and return true indicating + * that this is in fact a non-EOP buffer. + * + * @return: true for not end of packet + **/ +static bool rnpgbe_is_non_eop(struct mucse_ring *rx_ring, + union rnpgbe_rx_desc *rx_desc, + struct sk_buff *skb) +{ + u32 ntc =3D rx_ring->next_to_clean + 1; + + /* fetch, update, and store next to clean */ + ntc =3D (ntc < rx_ring->count) ? ntc : 0; + rx_ring->next_to_clean =3D ntc; + prefetch(M_RX_DESC(rx_ring, ntc)); + /* if we are the last buffer then there is nothing else to do */ + if (likely(rnpgbe_test_staterr(rx_desc, M_RXD_STAT_EOP))) + return false; + if (skb_shinfo(skb)->nr_frags < MAX_SKB_FRAGS) { + /* place skb in next buffer to be received */ + rx_ring->rx_buffer_info[ntc].skb =3D skb; + } else { + atomic64_inc(&rx_ring->stats.dropped); + /* too much frags, force free */ + dev_kfree_skb_any(skb); + rx_ring->drop_status =3D true; + } + /* we should clean it since we used all info in it */ + rx_desc->wb.cmd =3D 0; + + return true; +} + +/** + * rnpgbe_cleanup_headers - Correct corrupted or empty headers + * @skb: current socket buffer containing buffer in progress + * @return: true if an error was encountered and skb was freed. + **/ +static bool rnpgbe_cleanup_headers(struct sk_buff *skb) +{ + /* if eth_skb_pad returns an error the skb was freed */ + if (eth_skb_pad(skb)) + return true; + + return false; +} + +/** + * rnpgbe_process_skb_fields - Setup skb header fields from desc + * @rx_ring: structure containing ring specific data + * @skb: skb currently being received and modified + * + * rnpgbe_process_skb_fields checks the ring, descriptor information + * in order to setup the hash, chksum, vlan, protocol, and other + * fields within the skb. + **/ +static void rnpgbe_process_skb_fields(struct mucse_ring *rx_ring, + struct sk_buff *skb) +{ + struct net_device *dev =3D rx_ring->netdev; + + skb_record_rx_queue(skb, rx_ring->queue_index); + skb->protocol =3D eth_type_trans(skb, dev); +} + +/** + * rnpgbe_alloc_retry_work - Deferred alloc retry callback + * @work: pointer to delayed_work embedded in mucse_ring + * + * Called after a delay when page_pool allocation failed in the NAPI + * poll loop. Re-schedules NAPI to retry descriptor refill without + * busy-looping. + **/ +static void rnpgbe_alloc_retry_work(struct work_struct *work) +{ + struct delayed_work *dw =3D to_delayed_work(work); + struct mucse_ring *rx_ring =3D + container_of(dw, struct mucse_ring, alloc_retry_work); + struct mucse_q_vector *q_vector =3D rx_ring->q_vector; + + napi_schedule(&q_vector->napi); +} + +/** + * rnpgbe_clean_rx_irq - Clean completed descriptors from Rx ring + * @q_vector: structure containing interrupt and ring information + * @rx_ring: rx descriptor ring to transact packets on + * @budget: total limit on number of packets to process + * + * rnpgbe_clean_rx_irq tries to check dd in desc, handle this desc + * if dd is set which means data is write-back by hw + * + * @return: amount of work completed. + **/ +static int rnpgbe_clean_rx_irq(struct mucse_q_vector *q_vector, + struct mucse_ring *rx_ring, + int budget) +{ + unsigned int total_rx_bytes =3D 0, total_rx_packets =3D 0; + u16 cleaned_count =3D mucse_desc_unused_rx(rx_ring); + + while (likely(total_rx_packets < budget)) { + struct mucse_rx_buffer *rx_buffer; + union rnpgbe_rx_desc *rx_desc; + struct sk_buff *skb; + unsigned int size; + + if (cleaned_count >=3D M_RX_BUFFER_WRITE) { + if (rnpgbe_alloc_rx_buffers(rx_ring, cleaned_count)) { + queue_delayed_work(system_wq, + &rx_ring->alloc_retry_work, + msecs_to_jiffies(500)); + cleaned_count =3D mucse_desc_unused_rx(rx_ring); + } else { + cancel_delayed_work(&rx_ring->alloc_retry_work); + cleaned_count =3D 0; + } + } + rx_desc =3D M_RX_DESC(rx_ring, rx_ring->next_to_clean); + + if (!rnpgbe_test_staterr(rx_desc, M_RXD_STAT_DD)) + break; + + /* This memory barrier is needed to keep us from reading + * any other fields out of the rx_desc until we know the + * descriptor has been written back + */ + dma_rmb(); + /* Hardware enforces: minimum 33-bytes descriptor(no 1-13 byte + * size), multi-descriptors only for jumbo frames > 1536 bytes + * (controlled by M_DEFAULT_SG=3D96, each descriptor no more than + * 1536 bytes). Small packets use single descriptor. + */ + size =3D le16_to_cpu(rx_desc->wb.len); + + if (unlikely(rx_ring->drop_status)) { + cleaned_count++; + /* drop data until eop */ + if (rnpgbe_test_staterr(rx_desc, M_RXD_STAT_EOP)) + rx_ring->drop_status =3D false; + + rx_desc->wb.cmd =3D 0; + rx_ring->next_to_clean++; + if (rx_ring->next_to_clean >=3D rx_ring->count) + rx_ring->next_to_clean =3D 0; + continue; + } + + if (unlikely(!size || size > (PAGE_SIZE - RNPGBE_SKB_PAD))) { + cleaned_count++; + atomic64_inc(&rx_ring->stats.dropped); + rx_desc->wb.cmd =3D 0; + rx_ring->next_to_clean++; + if (rx_ring->next_to_clean >=3D rx_ring->count) + rx_ring->next_to_clean =3D 0; + continue; + } + + /* TODO: hardware error checks (wb.status: crc_err, etc.) in + * the future. For now the basic RX path relies on the stack + * to drop malformed packets naturally. + */ + + rx_buffer =3D rnpgbe_get_buffer(rx_ring, &skb, size); + + if (skb) + rnpgbe_add_rx_frag(rx_buffer, skb, size); + else + skb =3D rnpgbe_build_skb(rx_buffer, size); + + if (!skb) { + cleaned_count++; + + /* drop until eop if multiple descriptors */ + if (!(rnpgbe_test_staterr(rx_desc, M_RXD_STAT_EOP))) + rx_ring->drop_status =3D true; + + rx_desc->wb.cmd =3D 0; + rx_ring->next_to_clean++; + atomic64_inc(&rx_ring->stats.dropped); + if (rx_ring->next_to_clean >=3D rx_ring->count) + rx_ring->next_to_clean =3D 0; + + continue; + } + + rx_buffer->page =3D NULL; + rx_buffer->skb =3D NULL; + cleaned_count++; + + if (rnpgbe_is_non_eop(rx_ring, rx_desc, skb)) + continue; + + /* verify the packet layout is correct */ + if (rnpgbe_cleanup_headers(skb)) { + /* we should clean it since we used all info in it */ + atomic64_inc(&rx_ring->stats.dropped); + rx_desc->wb.cmd =3D 0; + continue; + } + + /* probably a little skewed due to removing CRC */ + total_rx_bytes +=3D skb->len; + rnpgbe_process_skb_fields(rx_ring, skb); + rx_desc->wb.cmd =3D 0; + napi_gro_receive(&q_vector->napi, skb); + /* update budget accounting */ + total_rx_packets++; + } + + u64_stats_update_begin(&rx_ring->syncp); + rx_ring->stats.packets +=3D total_rx_packets; + rx_ring->stats.bytes +=3D total_rx_bytes; + u64_stats_update_end(&rx_ring->syncp); + + return total_rx_packets; +} + /** * rnpgbe_poll - NAPI Rx polling callback * @napi: structure for representing this polling device @@ -203,6 +597,7 @@ static int rnpgbe_poll(struct napi_struct *napi, int bu= dget) container_of(napi, struct mucse_q_vector, napi); bool clean_complete =3D true; struct mucse_ring *ring; + int per_ring_budget; int work_done =3D 0; =20 mucse_for_each_ring(ring, q_vector->tx) { @@ -214,6 +609,20 @@ static int rnpgbe_poll(struct napi_struct *napi, int b= udget) if (unlikely(!budget)) return 0; =20 + if (q_vector->rx.count > 1) + per_ring_budget =3D max(budget / q_vector->rx.count, 1); + else + per_ring_budget =3D budget; + + mucse_for_each_ring(ring, q_vector->rx) { + int cleaned =3D 0; + + cleaned =3D rnpgbe_clean_rx_irq(q_vector, ring, per_ring_budget); + work_done +=3D cleaned; + if (cleaned >=3D per_ring_budget) + clean_complete =3D false; + } + if (!clean_complete) return budget; =20 @@ -396,12 +805,17 @@ static int rnpgbe_alloc_q_vector(struct mucse *mucse, } =20 for (idx =3D 0; idx < rxr_count; idx++) { + ring->dev =3D &mucse->pdev->dev; mucse_add_ring(ring, &q_vector->rx); + ring->count =3D mucse->rx_ring_item_count; + ring->netdev =3D mucse->netdev; ring->queue_index =3D eth_queue_idx + idx; ring->rnpgbe_queue_idx =3D rxr_idx; ring->ring_addr =3D hw->hw_addr + RING_OFFSET(rxr_idx); ring->irq_mask =3D ring->ring_addr + RNPGBE_DMA_INT_MASK; ring->trig =3D ring->ring_addr + RNPGBE_DMA_INT_TRIG; + ring->pfvfnum =3D hw->pfvfnum; + u64_stats_init(&ring->syncp); mucse->rx_ring[ring->queue_index] =3D ring; rxr_idx +=3D step; ring++; @@ -843,6 +1257,23 @@ static void rnpgbe_clean_all_tx_rings(struct mucse *m= ucse) mucse_hw_wr32(hw, RNPGBE_DMA_AXI_EN, dma_axi_ctl); } =20 +/** + * rnpgbe_clean_all_rx_rings - Free Rx Buffers for all queues + * @mucse: board private structure + **/ +static void rnpgbe_clean_all_rx_rings(struct mucse *mucse) +{ + struct mucse_hw *hw =3D &mucse->hw; + u32 dma_axi_ctl; + + for (int i =3D 0; i < mucse->num_rx_queues; i++) + rnpgbe_clean_rx_ring(mucse->rx_ring[i]); + + dma_axi_ctl =3D mucse_hw_rd32(hw, RNPGBE_DMA_AXI_EN); + dma_axi_ctl &=3D ~RX_AXI_RW_EN; + mucse_hw_wr32(hw, RNPGBE_DMA_AXI_EN, dma_axi_ctl); +} + void rnpgbe_down(struct mucse *mucse) { struct net_device *netdev =3D mucse->netdev; @@ -853,6 +1284,7 @@ void rnpgbe_down(struct mucse *mucse) rnpgbe_napi_disable_all(mucse); rnpgbe_irq_disable(mucse); rnpgbe_clean_all_tx_rings(mucse); + rnpgbe_clean_all_rx_rings(mucse); } =20 /** @@ -872,6 +1304,8 @@ void rnpgbe_up_complete(struct mucse *mucse) clear_bit(__MUCSE_DOWN, &mucse->state); rnpgbe_irq_enable(mucse); netif_tx_start_all_queues(netdev); + for (int i =3D 0; i < mucse->num_rx_queues; i++) + mucse_ring_wr32(mucse->rx_ring[i], RNPGBE_RX_START, 1); } =20 /** @@ -1264,5 +1698,267 @@ void rnpgbe_get_stats64(struct net_device *netdev, stats->tx_bytes +=3D bytes; } } + + for (i =3D 0; i < mucse->num_rx_queues; i++) { + struct mucse_ring *ring =3D READ_ONCE(mucse->rx_ring[i]); + u64 bytes, packets, dropped; + unsigned int start; + + if (ring) { + do { + start =3D u64_stats_fetch_begin(&ring->syncp); + packets =3D ring->stats.packets; + bytes =3D ring->stats.bytes; + dropped =3D atomic64_read(&ring->stats.dropped); + } while (u64_stats_fetch_retry(&ring->syncp, start)); + stats->rx_packets +=3D packets; + stats->rx_dropped +=3D dropped; + stats->rx_bytes +=3D bytes; + } + } rcu_read_unlock(); } + +static int mucse_alloc_page_pool(struct mucse_ring *rx_ring) +{ + int ret =3D 0; + + struct page_pool_params pp_params =3D { + .flags =3D PP_FLAG_DMA_MAP | PP_FLAG_DMA_SYNC_DEV, + .order =3D 0, + .pool_size =3D rx_ring->count, + .nid =3D dev_to_node(rx_ring->dev), + .dev =3D rx_ring->dev, + .dma_dir =3D DMA_FROM_DEVICE, + .offset =3D 0, + .max_len =3D PAGE_SIZE, + }; + + rx_ring->page_pool =3D page_pool_create(&pp_params); + if (IS_ERR(rx_ring->page_pool)) { + ret =3D PTR_ERR(rx_ring->page_pool); + rx_ring->page_pool =3D NULL; + } + + return ret; +} + +/** + * rnpgbe_setup_rx_resources - allocate Rx resources (Descriptors) + * @rx_ring: rx descriptor ring (for a specific queue) to setup + * @mucse: pointer to private structure + * + * @return: 0 on success, negative on failure + **/ +static int rnpgbe_setup_rx_resources(struct mucse_ring *rx_ring, + struct mucse *mucse) +{ + struct device *dev =3D rx_ring->dev; + int size; + + size =3D sizeof(struct mucse_rx_buffer) * rx_ring->count; + + rx_ring->rx_buffer_info =3D vzalloc(size); + + if (!rx_ring->rx_buffer_info) + goto err_return; + /* Round up to nearest 4K */ + rx_ring->size =3D rx_ring->count * sizeof(union rnpgbe_rx_desc); + rx_ring->size =3D ALIGN(rx_ring->size, 4096); + rx_ring->desc =3D dma_alloc_coherent(dev, rx_ring->size, &rx_ring->dma, + GFP_KERNEL); + if (!rx_ring->desc) + goto err_free_buffer; + + rx_ring->next_to_clean =3D 0; + rx_ring->next_to_use =3D 0; + + if (mucse_alloc_page_pool(rx_ring)) + goto err_free_desc; + + INIT_DELAYED_WORK(&rx_ring->alloc_retry_work, + rnpgbe_alloc_retry_work); + + return 0; +err_free_desc: + dma_free_coherent(dev, rx_ring->size, rx_ring->desc, + rx_ring->dma); + rx_ring->desc =3D NULL; +err_free_buffer: + vfree(rx_ring->rx_buffer_info); +err_return: + rx_ring->rx_buffer_info =3D NULL; + return -ENOMEM; +} + +/** + * rnpgbe_clean_rx_ring - Free Rx Buffers per Queue + * @rx_ring: ring to free buffers from + **/ +void rnpgbe_clean_rx_ring(struct mucse_ring *rx_ring) +{ + struct mucse_rx_buffer *rx_buffer; + u16 i; + + /* Stop hw. hardware design guarantees: + * - No new descriptors will be fetched after RX_START=3D0 + * - No DMA will be initiated for already-fetched descriptors + */ + mucse_ring_wr32(rx_ring, RNPGBE_RX_START, 0); + /* Flush posted write to ensure hardware sees the disable command. + * After this read completes, all RX DMA for this ring is + * guaranteed quiesced. + */ + (void)mucse_ring_rd32(rx_ring, RNPGBE_RX_START); + /* Wait for in-flight DMA to quiesce */ + usleep_range(300, 500); + + cancel_delayed_work_sync(&rx_ring->alloc_retry_work); + + /* ring already cleared, nothing to do */ + if (!rx_ring->rx_buffer_info) + return; + /* Free all the Rx ring sk_buffs */ + for (i =3D 0; i < rx_ring->count; i++) { + rx_buffer =3D &rx_ring->rx_buffer_info[i]; + + if (rx_buffer->skb) { + struct sk_buff *skb =3D rx_buffer->skb; + + dev_kfree_skb(skb); + rx_buffer->skb =3D NULL; + } + + if (rx_buffer->page) { + page_pool_put_full_page(rx_ring->page_pool, + rx_buffer->page, false); + rx_buffer->page =3D NULL; + } + } + + rx_ring->next_to_clean =3D 0; + rx_ring->next_to_use =3D 0; +} + +/** + * rnpgbe_free_rx_resources - Free Rx Resources + * @rx_ring: ring to clean the resources from + * + * Free all receive software resources + **/ +static void rnpgbe_free_rx_resources(struct mucse_ring *rx_ring) +{ + rnpgbe_clean_rx_ring(rx_ring); + vfree(rx_ring->rx_buffer_info); + rx_ring->rx_buffer_info =3D NULL; + /* if not set, then don't free */ + if (!rx_ring->desc) + return; + + dma_free_coherent(rx_ring->dev, rx_ring->size, rx_ring->desc, + rx_ring->dma); + rx_ring->desc =3D NULL; + if (rx_ring->page_pool) { + page_pool_destroy(rx_ring->page_pool); + rx_ring->page_pool =3D NULL; + } +} + +/** + * rnpgbe_setup_all_rx_resources - allocate all queues Rx resources + * @mucse: pointer to private structure + * + * @return: 0 on success, negative on failure + **/ +int rnpgbe_setup_all_rx_resources(struct mucse *mucse) +{ + int i, err =3D 0; + + for (i =3D 0; i < mucse->num_rx_queues; i++) { + err =3D rnpgbe_setup_rx_resources(mucse->rx_ring[i], mucse); + if (!err) + continue; + + goto err_setup_rx; + } + + return 0; +err_setup_rx: + while (i--) + rnpgbe_free_rx_resources(mucse->rx_ring[i]); + return err; +} + +/** + * rnpgbe_free_all_rx_resources - Free Rx Resources for All Queues + * @mucse: pointer to private structure + * + * Free all receive software resources + **/ +void rnpgbe_free_all_rx_resources(struct mucse *mucse) +{ + for (int i =3D 0; i < (mucse->num_rx_queues); i++) + rnpgbe_free_rx_resources(mucse->rx_ring[i]); +} + +/** + * rnpgbe_configure_rx_ring - Configure Rx ring info to hw + * @mucse: pointer to private structure + * @ring: structure containing ring specific data + * + * Configure the Rx descriptor ring after a reset. + **/ +static void rnpgbe_configure_rx_ring(struct mucse *mucse, + struct mucse_ring *ring) +{ + struct mucse_hw *hw =3D &mucse->hw; + + /* Stop hw. hardware design guarantees: + * - No new descriptors will be fetched after RX_START=3D0 + * - No DMA will be initiated for already-fetched descriptors + */ + mucse_ring_wr32(ring, RNPGBE_RX_START, 0); + /* Flush posted write to ensure hardware sees the disable command. + * After this read completes, all RX DMA for this ring is + * guaranteed quiesced. + */ + (void)mucse_ring_rd32(ring, RNPGBE_RX_START); + /* Wait for in-flight DMA to quiesce */ + usleep_range(300, 500); + /* set descripts registers*/ + mucse_ring_wr32(ring, RNPGBE_RX_BASE_ADDR_LO, (u32)ring->dma); + mucse_ring_wr32(ring, RNPGBE_RX_BASE_ADDR_HI, + (u32)((u64)ring->dma >> 32) | (hw->pfvfnum << 24)); + mucse_ring_wr32(ring, RNPGBE_RX_LEN, ring->count); + ring->tail =3D ring->ring_addr + RNPGBE_RX_TAIL; + ring->next_to_clean =3D mucse_ring_rd32(ring, RNPGBE_RX_HEAD) & + (ring->count - 1); + ring->next_to_use =3D ring->next_to_clean; + ring->drop_status =3D false; + mucse_ring_wr32(ring, RNPGBE_RX_SG_LEN, M_DEFAULT_SG); + mucse_ring_wr32(ring, RNPGBE_RX_FETCH, M_DEFAULT_RX_FETCH); + mucse_ring_wr32(ring, RNPGBE_RX_TIMEOUT_TH, 0); + mucse_ring_wr32(ring, RNPGBE_RX_INT_TIMER, + M_DEFAULT_INT_TIMER_R * hw->cycles_per_us); + mucse_ring_wr32(ring, RNPGBE_RX_INT_PKTCNT, M_DEFAULT_RX_INT_PKTCNT); + rnpgbe_alloc_rx_buffers(ring, mucse_desc_unused_rx(ring)); +} + +/** + * rnpgbe_configure_rx - Configure Receive Unit after Reset + * @mucse: pointer to private structure + * + * Configure the Rx unit after a reset. + **/ +void rnpgbe_configure_rx(struct mucse *mucse) +{ + struct mucse_hw *hw =3D &mucse->hw; + u32 dma_axi_ctl; + + for (int i =3D 0; i < mucse->num_rx_queues; i++) + rnpgbe_configure_rx_ring(mucse, mucse->rx_ring[i]); + + dma_axi_ctl =3D mucse_hw_rd32(hw, RNPGBE_DMA_AXI_EN); + dma_axi_ctl |=3D RX_AXI_RW_EN; + mucse_hw_wr32(hw, RNPGBE_DMA_AXI_EN, dma_axi_ctl); +} diff --git a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.h b/drivers/net/e= thernet/mucse/rnpgbe/rnpgbe_lib.h index ba9206678ede..29fa6cd56e21 100644 --- a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.h +++ b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.h @@ -9,12 +9,28 @@ struct mucse_hw; struct mucse_ring; =20 #define RING_OFFSET(n) (0x1000 + 0x100 * (n)) +#define RNPGBE_RX_START 0x10 #define RNPGBE_TX_START 0x18 #define RNPGBE_DMA_INT_MASK 0x24 #define TX_INT_MASK BIT(1) #define RX_INT_MASK BIT(0) #define INT_VALID (BIT(16) | BIT(17)) #define RNPGBE_DMA_INT_TRIG 0x2c /* lost-interrupt recovery trigger = */ +#define RNPGBE_RX_BASE_ADDR_HI 0x30 +#define RNPGBE_RX_BASE_ADDR_LO 0x34 +#define RNPGBE_RX_LEN 0x38 +#define RNPGBE_RX_HEAD 0x3c +#define RNPGBE_RX_TAIL 0x40 +#define M_DEFAULT_RX_FETCH 0x100020 +#define RNPGBE_RX_FETCH 0x44 +#define M_DEFAULT_INT_TIMER_R 30 +#define RNPGBE_RX_INT_TIMER 0x48 +#define M_DEFAULT_RX_INT_PKTCNT 64 +#define RNPGBE_RX_INT_PKTCNT 0x4c +#define RNPGBE_RX_ARB_DEF_LVL 0x50 +#define RNPGBE_RX_TIMEOUT_TH 0x54 +#define M_DEFAULT_SG 96 /* unit 16b, 1536 bytes */ +#define RNPGBE_RX_SG_LEN 0x58 #define RNPGBE_TX_BASE_ADDR_HI 0x60 #define RNPGBE_TX_BASE_ADDR_LO 0x64 #define RNPGBE_TX_LEN 0x68 @@ -38,13 +54,15 @@ struct mucse_ring; /* 2 desc gap to keep tail from touching head */ /* 1 desc for context descriptor */ #define RESV_DESC_NEEDED 3 +#define RNPGBE_SKB_PAD (NET_SKB_PAD + NET_IP_ALIGN) +#define M_RX_BUFFER_WRITE 16 + /* hw require this not zero */ #define M_DEFAULT_MAC_IP_LEN 20 #define mucse_for_each_ring(pos, head)\ for (typeof((head).ring) __pos =3D (head).ring;\ __pos ? ({ pos =3D __pos; 1; }) : 0;\ __pos =3D __pos->next) - int rnpgbe_init_interrupt_scheme(struct mucse *mucse); void rnpgbe_clear_interrupt_scheme(struct mucse *mucse); int rnpgbe_request_mbx_irq(struct mucse *mucse); @@ -55,10 +73,14 @@ void rnpgbe_irq_disable(struct mucse *mucse); void rnpgbe_down(struct mucse *mucse); void rnpgbe_up_complete(struct mucse *mucse); void rnpgbe_configure_tx(struct mucse *mucse); +void rnpgbe_configure_rx(struct mucse *mucse); int rnpgbe_setup_all_tx_resources(struct mucse *mucse); void rnpgbe_free_all_tx_resources(struct mucse *mucse); netdev_tx_t rnpgbe_xmit_frame_ring(struct sk_buff *skb, struct mucse_ring *tx_ring); void rnpgbe_get_stats64(struct net_device *netdev, struct rtnl_link_stats64 *stats); +void rnpgbe_clean_rx_ring(struct mucse_ring *rx_ring); +int rnpgbe_setup_all_rx_resources(struct mucse *mucse); +void rnpgbe_free_all_rx_resources(struct mucse *mucse); #endif diff --git a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_main.c b/drivers/net/= ethernet/mucse/rnpgbe/rnpgbe_main.c index b8f79603dd97..cfb2328bfb75 100644 --- a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_main.c +++ b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_main.c @@ -35,6 +35,7 @@ static struct pci_device_id rnpgbe_pci_tbl[] =3D { static void rnpgbe_configure(struct mucse *mucse) { rnpgbe_configure_tx(mucse); + rnpgbe_configure_rx(mucse); } =20 /** @@ -63,11 +64,16 @@ static int rnpgbe_open(struct net_device *netdev) err =3D rnpgbe_setup_all_tx_resources(mucse); if (err) goto err_free_irqs; + err =3D rnpgbe_setup_all_rx_resources(mucse); + if (err) + goto err_free_tx; =20 rnpgbe_configure(mucse); rnpgbe_up_complete(mucse); =20 return 0; +err_free_tx: + rnpgbe_free_all_tx_resources(mucse); err_free_irqs: rnpgbe_free_irq(mucse); return err; @@ -89,6 +95,7 @@ static int rnpgbe_close(struct net_device *netdev) rnpgbe_down(mucse); rnpgbe_free_irq(mucse); rnpgbe_free_all_tx_resources(mucse); + rnpgbe_free_all_rx_resources(mucse); =20 return 0; } @@ -121,6 +128,7 @@ static const struct net_device_ops rnpgbe_netdev_ops = =3D { static void rnpgbe_sw_init(struct mucse *mucse) { mucse->tx_ring_item_count =3D M_DEFAULT_TXD; + mucse->rx_ring_item_count =3D M_DEFAULT_RXD; mucse->tx_work_limit =3D M_DEFAULT_TX_WORK; } =20 --=20 2.25.1 From nobody Mon Jun 8 09:49:26 2026 Received: from smtpbgbr2.qq.com (smtpbgbr2.qq.com [54.207.22.56]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 7950641325E for ; Thu, 4 Jun 2026 11:29:58 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=54.207.22.56 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780572603; cv=none; b=gC2TyS7w0/KOFOV/H6/ezn8LaL2LKXuhC00QBu2j8H8CTBh714YxDJ4xyQrMn6E+BuuEzEWRC5JgX719raaeGJGnHveTR9WKb7aVkVyn7q82/VttPLFvhiH710v0ilMjXDiWIndJdHyFBgm//jsH2JRNtStyZ9GDBI3LDLDGXLY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780572603; c=relaxed/simple; bh=nuH7QysM15GgPfxVM4RHJZQhTOYsovpiSseY29yaTaE=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=Y4b/KzAs+72+l0UMM+yMIqpkYmVEXTuC6G26lhkjzhB4fbEqtpB8lmIFfh3RKNNo4Dqn4knHmu8j1NsguQ5xJspJ40Dd3ip/hMa7ihB/+yi8QNj3tekvJb9elXUcKQnq87zqJhvcDGw/ZFFg3pwSNt9JiFwkC+qZE5SkuRX44CE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=mucse.com; spf=pass smtp.mailfrom=mucse.com; arc=none smtp.client-ip=54.207.22.56 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=mucse.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=mucse.com X-QQ-mid: zesmtpgz8t1780572492tdf6638d6 X-QQ-Originating-IP: 8o26GYS5rjcWugU6BlIJEp8eZWvi4U1iDqL6xdjNwYg= Received: from localhost.localdomain ( [203.174.112.180]) by bizesmtp.qq.com (ESMTP) with id ; Thu, 04 Jun 2026 19:28:10 +0800 (CST) X-QQ-SSF: 0000000000000000000000000000000 X-QQ-GoodBg: 0 X-BIZMAIL-ID: 14141746813252914663 EX-QQ-RecipientCnt: 13 From: Dong Yibo To: andrew+netdev@lunn.ch, davem@davemloft.net, edumazet@google.com, kuba@kernel.org, pabeni@redhat.com, danishanwar@ti.com, vadim.fedorenko@linux.dev, horms@kernel.org, u.kleine-koenig@baylibre.com Cc: linux-kernel@vger.kernel.org, netdev@vger.kernel.org, dong100@mucse.com, yaojun@mucse.com Subject: [PATCH net-next v6 4/4] net: rnpgbe: Add link status handling support Date: Thu, 4 Jun 2026 19:27:50 +0800 Message-Id: <20260604112750.769215-5-dong100@mucse.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20260604112750.769215-1-dong100@mucse.com> References: <20260604112750.769215-1-dong100@mucse.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-QQ-SENDSIZE: 520 Feedback-ID: zesmtpgz:mucse.com:qybglogicsvrgz:qybglogicsvrgz3a-1 X-QQ-XMAILINFO: M9oQrxULcDvja/3sAe2NGLBms9YMLCzc/8w8fS4lqA9aDVRz6Z+qFpA2 I9Y8Lab0W8IOVgpAMRPGdkICjPx96nbrpXy/Og7o/3wr9cM1TwIAliEPvjnQ1Dwa60VJx4T lm1qL+lZMuS3dk67zstFjM/T6qPnRhlc1yawEjUI/UPEYuJ9W3phY/GzHoMo9y0nB8VWa4j mIuA6FB+E/yIfYJNhoie7dSjfqV4W5mEVUYZhUfaMdMP/Zz5Q+WzwV3OpoPstwrtNbxWbWS XJGVpZGjop4s5/YULMeabGNmZvd52pfTbgAxLOFruRSHFLD8MUgbMQ1Wm2ebf6DcTTRux3/ lUW4ZHIsyZQ7kCCJpwt2QmTT67Puds00ASMvdOvyri4LavnbmodxVJ3xa0FL63H2PUdVGGo dj7neiYAax7ofHTB89p4nVxbSnTYUHYunr+SSdpgFDe6PuGGaxI+zduzhcZuJUaMxKJPFko IpIomER6PriyH1VKja2+zG2ZYLf/QgBgeSeMKJJ6Cl15OiZc9c1nfRd0GlcaT5SRh9khiHO zXbjauYJByukZlF2PZQ7TMRD9Di0qX1VYor9DQMVxk+ZuFR0WkGblFG46/2HkQCZEDveOHt kvMLJv1z2tCK/2RZLeQsvqo1i8PCxlVRHBLzPSf/IiIScutX806OdnoKd/0efZapVv0f0X0 aLGnoK+6kfgP+Dr4Wy5z7e59o/n6zItgMzYK2wyoSGZEXPX2fIGxoEhPQgaKVrpunJM06WZ VmTR7Gfm2poc+KiMrbWEsP+zK5TrT5V2Z7ukbEFnGXb/vYHLdAlrEpG71CIfboT08jNqP3j yEXP+8b77rvDpKhP5Fatd1OKyAPNi6qQUwRWHNmFe6yLZQmvQwOciPKm4YNqR4agofu0/Gh UNxjN3Bb0F3vLVilKD4fItxnqeoxZiEixiH0UOeAGTLzcpUhvLRJZ2NwVmte9GEtJ8OAiMw KqjMmqnxuF9/02LPM4NBmUzDMOs6TMRbyhwMiCCIuBO0pVBn7HrIe3MahvFT4kH3RSL2TYJ cSgtCrDjt8lkF5TwoMdS0LbB3sueBZZrWb7UuhwCaXjfianvAs/Kg3RjzO0h9+UH5Ip7V9P iV1xXJ0SZA+B5bP4kVZAmqxsoO+AqimXJbus4KFiTDgOgvr0pHZdJG77k9xJ+d7Ow== X-QQ-XMRINFO: Mp0Kj//9VHAxzExpfF+O8yhSrljjwrznVg== X-QQ-RECHKSPAM: 0 Content-Type: text/plain; charset="utf-8" Add link status management infrastructure to the rnpgbe driver: - Add link status related data structures (speed, duplex, link state) - Implement firmware link event handling via mailboxa (fw controls link states, don't support phylink) - Add service task for periodic link status monitoring - Implement carrier status management (netif_carrier_on/off) - Add port up/down notification to firmware This enables the driver to properly track and report link status changes. Signed-off-by: Dong Yibo --- drivers/net/ethernet/mucse/rnpgbe/rnpgbe.h | 21 +- .../net/ethernet/mucse/rnpgbe/rnpgbe_chip.c | 37 +++- drivers/net/ethernet/mucse/rnpgbe/rnpgbe_hw.h | 13 ++ .../net/ethernet/mucse/rnpgbe/rnpgbe_lib.c | 180 +++++++++++++++- .../net/ethernet/mucse/rnpgbe/rnpgbe_lib.h | 1 + .../net/ethernet/mucse/rnpgbe/rnpgbe_main.c | 5 + .../net/ethernet/mucse/rnpgbe/rnpgbe_mbx.c | 23 ++ .../net/ethernet/mucse/rnpgbe/rnpgbe_mbx.h | 1 + .../net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.c | 203 +++++++++++++++++- .../net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.h | 38 ++++ 10 files changed, 513 insertions(+), 9 deletions(-) diff --git a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe.h b/drivers/net/ether= net/mucse/rnpgbe/rnpgbe.h index 5982fb975642..e7731b588355 100644 --- a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe.h +++ b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe.h @@ -17,25 +17,33 @@ enum rnpgbe_boards { board_n210 }; =20 +struct mbx_req_cookie { + int timeout; + struct completion comp; + /* must match sizeof(struct mbx_fw_cmd_reply) */ + u8 cmd[56]; +}; + struct mucse_mbx_info { u32 timeout_us; u32 delay_us; u16 fw_req; u16 fw_ack; + struct mbx_req_cookie cookie; /* lock for only one use mbx */ struct mutex lock; /* fw <--> pf mbx */ + bool irq_en; u32 fwpf_shm_base; u32 pf2fw_mbx_ctrl; u32 fwpf_mbx_mask; u32 fwpf_ctrl_base; }; =20 -/* Enum for firmware notification modes, - * more modes (e.g., portup, link_report) will be added in future - **/ enum { mucse_fw_powerup, + mucse_fw_portup, + mucse_fw_link_report_en, }; =20 struct mucse_hw { @@ -44,8 +52,11 @@ struct mucse_hw { struct pci_dev *pdev; struct mucse_mbx_info mbx; int port; + int speed; + bool link; u16 cycles_per_us; u8 pfvfnum; + u8 duplex; }; =20 struct rnpgbe_tx_desc { @@ -210,6 +221,7 @@ struct mucse { #define M_FLAG_MSI_EN BIT(0) #define M_FLAG_MSIX_SINGLE_EN BIT(1) #define M_FLAG_MSIX_EN BIT(2) +#define M_FLAG_NEED_LINK_UPDATE BIT(3) u32 flags; struct mucse_ring *tx_ring[RNPGBE_MAX_QUEUES] ____cacheline_aligned_in_smp; @@ -224,6 +236,8 @@ struct mucse { int num_rx_queues; char mbx_name[32]; unsigned long state; + struct delayed_work serv_task; + spinlock_t link_lock; /* spinlock for link update */ }; =20 int rnpgbe_get_permanent_mac(struct mucse_hw *hw, u8 *perm_addr); @@ -232,6 +246,7 @@ int rnpgbe_send_notify(struct mucse_hw *hw, bool enable, int mode); int rnpgbe_init_hw(struct mucse_hw *hw, int board_type); +void rnpgbe_set_link(struct mucse_hw *hw, bool linkup); =20 /* Device IDs */ #define PCI_VENDOR_ID_MUCSE 0x8848 diff --git a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_chip.c b/drivers/net/= ethernet/mucse/rnpgbe/rnpgbe_chip.c index 291e77d573fe..98c7c1492f6a 100644 --- a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_chip.c +++ b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_chip.c @@ -66,11 +66,17 @@ int rnpgbe_send_notify(struct mucse_hw *hw, int mode) { int err; - /* Keep switch struct to support more modes in the future */ + switch (mode) { case mucse_fw_powerup: err =3D mucse_mbx_powerup(hw, enable); break; + case mucse_fw_portup: + err =3D mucse_mbx_phyup(hw, enable); + break; + case mucse_fw_link_report_en: + err =3D mucse_mbx_link_report(hw, enable); + break; default: err =3D -EINVAL; } @@ -149,3 +155,32 @@ int rnpgbe_init_hw(struct mucse_hw *hw, int board_type) =20 return 0; } + +/** + * rnpgbe_set_link - Setup link state + * @hw: hw information structure + * @linkup: link on or not + * + * rnpgbe_set_link setup link status + * + **/ +void rnpgbe_set_link(struct mucse_hw *hw, bool linkup) +{ + u32 value =3D mucse_hw_rd32(hw, GMAC_CONTROL); + + if (linkup) + value |=3D GMAC_CONTROL_RE; + else + value &=3D ~GMAC_CONTROL_RE; + + mucse_hw_wr32(hw, GMAC_CONTROL, value); + + /* RX_ALL always be set when link is up -- the GMAC requires it + * for passing all packet. Promiscuous/unicast filtering is + * handled by other separate chip-level register. + */ + if (linkup) + mucse_hw_wr32(hw, GMAC_FRAME_FILTER, GMAC_RX_ALL); + else + mucse_hw_wr32(hw, GMAC_FRAME_FILTER, 0); +} diff --git a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_hw.h b/drivers/net/et= hernet/mucse/rnpgbe/rnpgbe_hw.h index 03688586b447..4d1a9a386e9d 100644 --- a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_hw.h +++ b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_hw.h @@ -17,7 +17,20 @@ =20 #define TX_AXI_RW_EN 0xc #define RX_AXI_RW_EN 0x03 +/* mask all valid info */ +#define M_ST_MASK 0xff000f11 +/* 31:28 set 0xa to valid it is a driver set info */ +#define M_DEFAULT_ST 0xa0000000 +/* driver setup this by own info */ +/*bit: 25:24 | 11:8 | 4 | 0 */ +/*fun: pause | speed | duplex | up/down */ +#define RNPGBE_LINK_ST 0x000c #define RNPGBE_DMA_AXI_EN 0x0010 =20 +#define MUCSE_GMAC_OFF(_n) (0x20000 + (_n)) +#define GMAC_CONTROL_RE 0x00000004 +#define GMAC_CONTROL MUCSE_GMAC_OFF(0) +#define GMAC_RX_ALL (BIT(31) | BIT(0)) +#define GMAC_FRAME_FILTER MUCSE_GMAC_OFF(0x4) #define RNPGBE_MAX_QUEUES 8 #endif /* _RNPGBE_HW_H */ diff --git a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c b/drivers/net/e= thernet/mucse/rnpgbe/rnpgbe_lib.c index 86017aa8d83b..13177f09de85 100644 --- a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c +++ b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c @@ -623,11 +623,16 @@ static int rnpgbe_poll(struct napi_struct *napi, int = budget) clean_complete =3D false; } =20 + if (test_bit(__MUCSE_DOWN, &q_vector->mucse->state)) + clean_complete =3D true; + if (!clean_complete) return budget; =20 - if (likely(napi_complete_done(napi, work_done))) - rnpgbe_irq_enable_queues(q_vector); + if (likely(napi_complete_done(napi, work_done))) { + if (!test_bit(__MUCSE_DOWN, &q_vector->mucse->state)) + rnpgbe_irq_enable_queues(q_vector); + } =20 return work_done; } @@ -645,6 +650,7 @@ static int rnpgbe_poll(struct napi_struct *napi, int bu= dget) int rnpgbe_request_mbx_irq(struct mucse *mucse) { struct pci_dev *pdev =3D mucse->pdev; + struct mucse_hw *hw =3D &mucse->hw; int err =3D 0; =20 snprintf(mucse->mbx_name, sizeof(mucse->mbx_name), @@ -660,6 +666,9 @@ int rnpgbe_request_mbx_irq(struct mucse *mucse) mucse); } =20 + if (!err) + hw->mbx.irq_en =3D true; + return err; } =20 @@ -670,8 +679,10 @@ int rnpgbe_request_mbx_irq(struct mucse *mucse) void rnpgbe_free_mbx_irq(struct mucse *mucse) { struct pci_dev *pdev =3D mucse->pdev; + struct mucse_hw *hw =3D &mucse->hw; =20 free_irq(pci_irq_vector(pdev, 0), mucse); + hw->mbx.irq_en =3D false; } =20 /** @@ -1277,8 +1288,35 @@ static void rnpgbe_clean_all_rx_rings(struct mucse *= mucse) void rnpgbe_down(struct mucse *mucse) { struct net_device *netdev =3D mucse->netdev; + struct mucse_hw *hw =3D &mucse->hw; + unsigned long flags; + int err; =20 set_bit(__MUCSE_DOWN, &mucse->state); + cancel_delayed_work_sync(&mucse->serv_task); + + spin_lock_irqsave(&mucse->link_lock, flags); + hw->link =3D false; + hw->speed =3D 0; + hw->duplex =3D 0; + mucse->flags &=3D ~M_FLAG_NEED_LINK_UPDATE; + spin_unlock_irqrestore(&mucse->link_lock, flags); + rnpgbe_set_link(hw, false); + + err =3D rnpgbe_send_notify(hw, false, mucse_fw_link_report_en); + if (err) { + dev_warn(&hw->pdev->dev, "Send link report to hw failed %d\n", + err); + dev_warn(&hw->pdev->dev, "Fw will still report link event\n"); + } + + err =3D rnpgbe_send_notify(hw, false, mucse_fw_portup); + if (err) { + dev_warn(&hw->pdev->dev, "Send port down to hw failed %d\n", + err); + dev_warn(&hw->pdev->dev, "Port is not truly down\n"); + } + netif_carrier_off(netdev); netif_tx_stop_all_queues(netdev); netif_tx_disable(netdev); rnpgbe_napi_disable_all(mucse); @@ -1294,6 +1332,9 @@ void rnpgbe_down(struct mucse *mucse) void rnpgbe_up_complete(struct mucse *mucse) { struct net_device *netdev =3D mucse->netdev; + struct mucse_hw *hw =3D &mucse->hw; + unsigned long flags; + int err; =20 if (mucse->flags & (M_FLAG_MSIX_EN | M_FLAG_MSIX_SINGLE_EN)) rnpgbe_configure_msix(mucse); @@ -1306,6 +1347,31 @@ void rnpgbe_up_complete(struct mucse *mucse) netif_tx_start_all_queues(netdev); for (int i =3D 0; i < mucse->num_rx_queues; i++) mucse_ring_wr32(mucse->rx_ring[i], RNPGBE_RX_START, 1); + + err =3D rnpgbe_send_notify(hw, true, mucse_fw_portup); + if (err) { + dev_warn(&hw->pdev->dev, "Send portup to hw failed %d\n", err); + dev_warn(&hw->pdev->dev, "Port is not truly up\n"); + } + + spin_lock_irqsave(&mucse->link_lock, flags); + hw->link =3D false; + hw->speed =3D 0; + hw->duplex =3D 0; + mucse->flags &=3D ~M_FLAG_NEED_LINK_UPDATE; + /* echo driver down to hw */ + mucse_hw_wr32(hw, RNPGBE_LINK_ST, M_DEFAULT_ST); + spin_unlock_irqrestore(&mucse->link_lock, flags); + + err =3D rnpgbe_send_notify(hw, true, mucse_fw_link_report_en); + if (err) { + dev_warn(&hw->pdev->dev, "Send link report to hw failed %d\n", + err); + dev_warn(&hw->pdev->dev, "Fw will not report link event\n"); + } + + queue_delayed_work(system_wq, &mucse->serv_task, + msecs_to_jiffies(500)); } =20 /** @@ -1962,3 +2028,113 @@ void rnpgbe_configure_rx(struct mucse *mucse) dma_axi_ctl |=3D RX_AXI_RW_EN; mucse_hw_wr32(hw, RNPGBE_DMA_AXI_EN, dma_axi_ctl); } + +/** + * rnpgbe_process_link_event - Update the link status + * @mucse: pointer to the device private structure + * + * @return: link status + **/ +static int rnpgbe_process_link_event(struct mucse *mucse) +{ + struct net_device *netdev =3D mucse->netdev; + struct mucse_hw *hw =3D &mucse->hw; + unsigned long flags; + bool link; + int speed; + u8 duplex; + + /* lockless fast-path ,serv_task retry handles race */ + if (!(mucse->flags & M_FLAG_NEED_LINK_UPDATE)) + return hw->link; + + spin_lock_irqsave(&mucse->link_lock, flags); + + link =3D hw->link; + speed =3D hw->speed; + duplex =3D hw->duplex; + + mucse->flags &=3D ~M_FLAG_NEED_LINK_UPDATE; + spin_unlock_irqrestore(&mucse->link_lock, flags); + + if (link) { + netdev_info(netdev, "NIC Link is Up %d Mbps, %s Duplex\n", + speed, + duplex ? "Full" : "Half"); + } + + return link; +} + +/** + * rnpgbe_link_is_up - Update netif_carrier status and + * print link up message + * @mucse: pointer to the device private structure + **/ +static void rnpgbe_link_is_up(struct mucse *mucse) +{ + struct net_device *netdev =3D mucse->netdev; + struct mucse_hw *hw =3D &mucse->hw; + + /* Only continue if link was previously down */ + if (netif_carrier_ok(netdev)) + return; + rnpgbe_set_link(hw, true); + netif_carrier_on(netdev); +} + +/** + * rnpgbe_link_is_down - Update netif_carrier status and + * print link down message + * @mucse: pointer to the private structure + **/ +static void rnpgbe_link_is_down(struct mucse *mucse) +{ + struct net_device *netdev =3D mucse->netdev; + struct mucse_hw *hw =3D &mucse->hw; + + /* Only continue if link was up previously */ + if (!netif_carrier_ok(netdev)) + return; + netdev_info(netdev, "NIC Link is Down\n"); + rnpgbe_set_link(hw, false); + netif_carrier_off(netdev); +} + +/** + * rnpgbe_process_link_subtask - Check and bring link up + * @mucse: pointer to the device private structure + **/ +static void rnpgbe_process_link_subtask(struct mucse *mucse) +{ + /* if interface is down do nothing */ + if (test_bit(__MUCSE_DOWN, &mucse->state)) + return; + + if (rnpgbe_process_link_event(mucse)) + rnpgbe_link_is_up(mucse); + else + rnpgbe_link_is_down(mucse); +} + +/** + * rnpgbe_service_task - Manages and runs subtasks + * @work: pointer to work_struct containing our data + **/ +void rnpgbe_service_task(struct work_struct *work) +{ + struct mucse *mucse =3D container_of(work, struct mucse, serv_task.work); + + if (test_bit(__MUCSE_DOWN, &mucse->state)) + return; + + rnpgbe_process_link_subtask(mucse); + + /* Periodic requeue is intentional: future patches will add + * statistics polling and other housekeeping tasks beyond + * link state handling. + */ + if (!test_bit(__MUCSE_DOWN, &mucse->state)) + queue_delayed_work(system_wq, &mucse->serv_task, + msecs_to_jiffies(500)); +} diff --git a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.h b/drivers/net/e= thernet/mucse/rnpgbe/rnpgbe_lib.h index 29fa6cd56e21..9511f4c96457 100644 --- a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.h +++ b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.h @@ -83,4 +83,5 @@ void rnpgbe_get_stats64(struct net_device *netdev, void rnpgbe_clean_rx_ring(struct mucse_ring *rx_ring); int rnpgbe_setup_all_rx_resources(struct mucse *mucse); void rnpgbe_free_all_rx_resources(struct mucse *mucse); +void rnpgbe_service_task(struct work_struct *work); #endif diff --git a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_main.c b/drivers/net/= ethernet/mucse/rnpgbe/rnpgbe_main.c index cfb2328bfb75..5d7cb51fcb0b 100644 --- a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_main.c +++ b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_main.c @@ -52,6 +52,7 @@ static int rnpgbe_open(struct net_device *netdev) struct mucse *mucse =3D netdev_priv(netdev); int err; =20 + netif_carrier_off(netdev); err =3D rnpgbe_request_irq(mucse); if (err) return err; @@ -181,6 +182,7 @@ static int rnpgbe_add_adapter(struct pci_dev *pdev, dev_err(&pdev->dev, "Init hw err %d\n", err); goto err_free_net; } + /* Step 1: Send power-up notification to firmware (no response expected) * This informs firmware to initialize hardware power state, but * firmware only acknowledges receipt without returning data. Must be @@ -223,6 +225,9 @@ static int rnpgbe_add_adapter(struct pci_dev *pdev, goto err_powerdown; } =20 + INIT_DELAYED_WORK(&mucse->serv_task, rnpgbe_service_task); + spin_lock_init(&mucse->link_lock); + err =3D rnpgbe_init_interrupt_scheme(mucse); if (err) { dev_err(&pdev->dev, "init interrupt failed %d\n", err); diff --git a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx.c b/drivers/net/e= thernet/mucse/rnpgbe/rnpgbe_mbx.c index de5e29230b3c..3891e94dbdca 100644 --- a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx.c +++ b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx.c @@ -247,6 +247,26 @@ int mucse_poll_and_read_mbx(struct mucse_hw *hw, u32 *= msg, u16 size) return mucse_read_mbx_pf(hw, msg, size); } =20 +/** + * mucse_check_and_read_mbx - check if there is notification and receive m= essage + * @hw: pointer to the HW structure + * @msg: the message buffer + * @size: length of buffer + * + * Return: 0 if it successfully received a message notification and + * copied it into the receive buffer, negative errno on failure + **/ +int mucse_check_and_read_mbx(struct mucse_hw *hw, u32 *msg, u16 size) +{ + int err; + + err =3D mucse_check_for_msg_pf(hw); + if (err) + return err; + + return mucse_read_mbx_pf(hw, msg, size); +} + /** * mucse_mbx_get_fwack - Read fw ack from reg * @mbx: pointer to the MBX structure @@ -402,5 +422,8 @@ void mucse_init_mbx_params_pf(struct mucse_hw *hw) mbx->delay_us =3D 100; mbx->timeout_us =3D 4 * USEC_PER_SEC; mutex_init(&mbx->lock); + init_completion(&mbx->cookie.comp); + mbx->cookie.timeout =3D 5 * HZ; + mbx->irq_en =3D false; mucse_mbx_reset(hw); } diff --git a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx.h b/drivers/net/e= thernet/mucse/rnpgbe/rnpgbe_mbx.h index e6fcc8d1d3ca..cba54a07a7fa 100644 --- a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx.h +++ b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx.h @@ -17,4 +17,5 @@ int mucse_write_and_wait_ack_mbx(struct mucse_hw *hw, u32 *msg, u16 size); void mucse_init_mbx_params_pf(struct mucse_hw *hw); int mucse_poll_and_read_mbx(struct mucse_hw *hw, u32 *msg, u16 size); +int mucse_check_and_read_mbx(struct mucse_hw *hw, u32 *msg, u16 size); #endif /* _RNPGBE_MBX_H */ diff --git a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.c b/drivers/ne= t/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.c index 05684d716792..9193d726f71b 100644 --- a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.c +++ b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.c @@ -3,6 +3,7 @@ =20 #include #include +#include =20 #include "rnpgbe.h" #include "rnpgbe_mbx.h" @@ -23,19 +24,40 @@ static int mucse_fw_send_cmd_wait_resp(struct mucse_hw = *hw, struct mbx_fw_cmd_req *req, struct mbx_fw_cmd_reply *reply) { + struct mbx_req_cookie *cookie =3D &hw->mbx.cookie; int len =3D le16_to_cpu(req->datalen); int retry_cnt =3D 3; int err; =20 + BUILD_BUG_ON(sizeof(struct mbx_fw_cmd_reply) !=3D 56); + mutex_lock(&hw->mbx.lock); + + if (hw->mbx.irq_en) + reinit_completion(&cookie->comp); + err =3D mucse_write_and_wait_ack_mbx(hw, (u32 *)req, len); if (err) goto out; do { - err =3D mucse_poll_and_read_mbx(hw, (u32 *)reply, - sizeof(*reply)); - if (err) + if (hw->mbx.irq_en) { + err =3D wait_for_completion_timeout(&cookie->comp, + cookie->timeout); + if (err) { + memcpy((u8 *)reply, cookie->cmd, + sizeof(*reply)); + err =3D 0; + } else { + err =3D -ETIMEDOUT; + } + goto out; + } else { + err =3D mucse_poll_and_read_mbx(hw, (u32 *)reply, + sizeof(*reply)); + if (err) + goto out; + } /* mucse_write_and_wait_ack_mbx return 0 means fw has * received request, wait for the expect opcode * reply with 'retry_cnt' times. @@ -190,10 +212,185 @@ int mucse_mbx_get_macaddr(struct mucse_hw *hw, int p= fvfnum, return 0; } =20 +/** + * mucse_mbx_phyup - Echo fw let the phy up + * @hw: pointer to the HW structure + * @is_phyup: true for up, false for down + * + * mucse_mbx_phyup echo fw to change phy status + * + * Return: 0 on success, negative errno on failure + **/ +int mucse_mbx_phyup(struct mucse_hw *hw, bool is_phyup) +{ + struct mbx_fw_cmd_req req =3D { + .datalen =3D cpu_to_le16(sizeof(req.phy_status) + + MUCSE_MBX_REQ_HDR_LEN), + .opcode =3D cpu_to_le16(SET_PHY_UP), + .phy_status =3D { + .port_mask =3D cpu_to_le32(BIT(hw->port)), + .status =3D cpu_to_le32(is_phyup ? 1 : 0), + }, + }; + int len, err; + + len =3D le16_to_cpu(req.datalen); + mutex_lock(&hw->mbx.lock); + err =3D mucse_write_and_wait_ack_mbx(hw, (u32 *)&req, len); + mutex_unlock(&hw->mbx.lock); + + return err; +} + +/** + * mucse_mbx_link_report - Echo fw report link change event or not + * @hw: pointer to the HW structure + * @is_report: true for report, false for no + * + * mucse_mbx_link_eventup echo fw to change event report state + * + * Return: 0 on success, negative errno on failure + **/ +int mucse_mbx_link_report(struct mucse_hw *hw, bool is_report) +{ + struct mbx_fw_cmd_req req =3D { + .datalen =3D cpu_to_le16(sizeof(req.report_status) + + MUCSE_MBX_REQ_HDR_LEN), + .opcode =3D cpu_to_le16(LINK_REPORT_EN), + .report_status =3D { + .port_mask =3D cpu_to_le16(BIT(hw->port)), + .status =3D cpu_to_le16(is_report ? 1 : 0), + }, + }; + int len, err; + + len =3D le16_to_cpu(req.datalen); + mutex_lock(&hw->mbx.lock); + err =3D mucse_write_and_wait_ack_mbx(hw, (u32 *)&req, len); + mutex_unlock(&hw->mbx.lock); + + return err; +} + +/** + * mucse_update_link_status_reg - update driver speed inf to reg + * @hw: pointer to the HW structure + * @req: pointer to req data + * + * mucse_update_link_status_reg update reg according to driver info, + * fw will send irq if status is differ with reg + * + **/ +static void mucse_update_link_status_reg(struct mucse_hw *hw, + struct mbx_fw_cmd_req *req) +{ + u16 status =3D le16_to_cpu(req->link_stat.st.status); + u32 value; + + value =3D mucse_hw_rd32(hw, RNPGBE_LINK_ST); + value &=3D ~M_ST_MASK; + value |=3D M_DEFAULT_ST; + + if (le16_to_cpu(req->link_stat.port_status)) { + value |=3D BIT(0); + switch (hw->speed) { + case 10: + value |=3D (mucse_speed_10 << 8); + break; + case 100: + value |=3D (mucse_speed_100 << 8); + break; + case 1000: + value |=3D (mucse_speed_1000 << 8); + break; + default: + /* invalid speed do nothing */ + break; + } + + value |=3D FIELD_PREP(BIT(4), !!hw->duplex); + value |=3D FIELD_PREP(GENMASK_U32(25, 24), + status & GENMASK(1, 0)); + } else { + value &=3D ~BIT(0); + } + + if (status & ST_STATUS_LLDP_STATUS_MASK) + value |=3D BIT(6); + else + value &=3D ~BIT(6); + + mucse_hw_wr32(hw, RNPGBE_LINK_ST, value); +} + +/** + * mucse_mbx_fw_req_handler - Handle fw req + * @hw: pointer to the HW structure + * @req: pointer to req data + * + * rnpgbe_mbx_fw_req_handler handler fw req, such as a link event req. + * + * @return: 0 on success, negative on failure + **/ +static void mucse_mbx_fw_req_handler(struct mucse_hw *hw, + struct mbx_fw_cmd_req *req) +{ + struct mucse *mucse =3D container_of(hw, struct mucse, hw); + u32 magic =3D le32_to_cpu(req->link_stat.port_magic); + unsigned long flags; + + if (le16_to_cpu(req->opcode) =3D=3D LINK_CHANGE_EVT) { + spin_lock_irqsave(&mucse->link_lock, flags); + + if (le16_to_cpu(req->link_stat.port_status)) + hw->link =3D true; + else + hw->link =3D false; + + if (magic =3D=3D ST_VALID_MAGIC) { + hw->speed =3D le16_to_cpu(req->link_stat.st.speed); + hw->duplex =3D req->link_stat.st.flags & DUPLEX_BIT; + /* update regs to notify link info is received */ + mucse_update_link_status_reg(hw, req); + } else { + hw->speed =3D 0; + hw->duplex =3D 0; + mucse_hw_wr32(hw, RNPGBE_LINK_ST, M_DEFAULT_ST); + } + mucse->flags |=3D M_FLAG_NEED_LINK_UPDATE; + spin_unlock_irqrestore(&mucse->link_lock, flags); + } +} + +static void mucse_mbx_fw_reply_handler(struct mucse_hw *hw, + struct mbx_fw_cmd_reply *reply) +{ + struct mbx_req_cookie *cookie =3D &hw->mbx.cookie; + + BUILD_BUG_ON(sizeof(struct mbx_fw_cmd_reply) !=3D 56); + + /* avoid splice fields from two different replies */ + if (completion_done(&cookie->comp)) + return; + + memcpy(cookie->cmd, (u8 *)reply, sizeof(*reply)); + complete(&cookie->comp); +} + /** * mucse_fw_irq_handler - Try to handle a req from hw * @hw: pointer to the HW structure **/ void mucse_fw_irq_handler(struct mucse_hw *hw) { + struct mbx_fw_cmd_reply reply =3D {}; + + /* try to check and read fw req */ + if (mucse_check_and_read_mbx(hw, (u32 *)&reply, sizeof(reply))) + return; + + if (le16_to_cpu(reply.flags) & FLAGS_REPLY) + mucse_mbx_fw_reply_handler(hw, &reply); + else + mucse_mbx_fw_req_handler(hw, (struct mbx_fw_cmd_req *)&reply); } diff --git a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.h b/drivers/ne= t/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.h index aa26c729588c..044d8dfd2c2b 100644 --- a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.h +++ b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.h @@ -14,6 +14,9 @@ enum MUCSE_FW_CMD { GET_HW_INFO =3D 0x0601, GET_MAC_ADDRESS =3D 0x0602, RESET_HW =3D 0x0603, + LINK_CHANGE_EVT =3D 0x0608, + LINK_REPORT_EN =3D 0x0613, + SET_PHY_UP =3D 0x0800, POWER_UP =3D 0x0803, }; =20 @@ -36,6 +39,16 @@ struct mucse_hw_info { __le32 ext_info; } __packed; =20 +#define ST_STATUS_LLDP_STATUS_MASK BIT(12) + +#define DUPLEX_BIT BIT(0) +struct st_status { + u8 phyid; + u8 flags; + __le16 speed; + __le16 status; +} __packed; + struct mbx_fw_cmd_req { __le16 flags; __le16 opcode; @@ -55,10 +68,27 @@ struct mbx_fw_cmd_req { __le32 port_mask; __le32 pfvf_num; } get_mac_addr; + struct { + __le32 port_mask; + __le32 status; + } phy_status; + struct { + __le16 status; + __le16 port_mask; + } report_status; + struct { + __le16 changed_lanes; + __le16 port_status; + __le32 port_magic; +#define ST_VALID_MAGIC 0xa4a6a8a9 + struct st_status st; + } link_stat; }; } __packed; =20 struct mbx_fw_cmd_reply { +#define FLAGS_REPLY BIT(0) +#define FLAGS_ERR BIT(2) __le16 flags; __le16 opcode; __le16 error_code; @@ -80,10 +110,18 @@ struct mbx_fw_cmd_reply { }; } __packed; =20 +enum mucse_speed { + mucse_speed_10, + mucse_speed_100, + mucse_speed_1000, +}; + int mucse_mbx_sync_fw(struct mucse_hw *hw); int mucse_mbx_powerup(struct mucse_hw *hw, bool is_powerup); int mucse_mbx_reset_hw(struct mucse_hw *hw); int mucse_mbx_get_macaddr(struct mucse_hw *hw, int pfvfnum, u8 *mac_addr, int port); +int mucse_mbx_phyup(struct mucse_hw *hw, bool is_phyup); +int mucse_mbx_link_report(struct mucse_hw *hw, bool is_report); void mucse_fw_irq_handler(struct mucse_hw *hw); #endif /* _RNPGBE_MBX_FW_H */ --=20 2.25.1