Merge branch 'net-rnpgbe-add-tx-rx-and-link-status-support'

Dong Yibo says:

====================
net: rnpgbe: Add TX/RX and link status support

This patch series adds the packet transmission, reception, and link status
management features to the RNPGBE driver, building upon the previously
introduced mailbox communication and basic driver infrastructure.

The series introduces:
- MSI-X interrupt handling with NAPI support
- TX path with scatter-gather DMA and completion handling
- RX path with page pool buffer management
- Unicast/Multicast filter.
- Firmware link-event processing and carrier management

These changes enable the RNPGBE driver to support basic TX/RX
network operations.
====================

Link: https://patch.msgid.link/3B42BE25B3696EAE+20261013022920.63529-1-dong100@mucse.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Jakub Kicinski 2026-10-19 19:50:13 -07:00
commit 6ac0d7ba21
12 changed files with 3478 additions and 39 deletions

View File

@ -19,6 +19,8 @@ if NET_VENDOR_MUCSE
config MGBE
tristate "Mucse(R) 1GbE PCI Express adapters support"
depends on PCI
depends on PCI_MSI
select PAGE_POOL
help
This driver supports Mucse(R) 1GbE PCI Express family of
adapters.

View File

@ -8,4 +8,5 @@ obj-$(CONFIG_MGBE) += rnpgbe.o
rnpgbe-objs := rnpgbe_main.o\
rnpgbe_chip.o\
rnpgbe_mbx.o\
rnpgbe_mbx_fw.o
rnpgbe_mbx_fw.o\
rnpgbe_lib.o

View File

@ -5,7 +5,14 @@
#define _RNPGBE_H
#include <linux/types.h>
#include <linux/atomic.h>
#include <linux/mutex.h>
#include <linux/netdevice.h>
#include <linux/timer.h>
#include <linux/if.h>
#include <linux/workqueue.h>
#include "rnpgbe_hw.h"
enum rnpgbe_boards {
board_n500,
@ -26,38 +33,229 @@ struct mucse_mbx_info {
u32 fwpf_ctrl_base;
};
/* 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,
};
struct mucse_hw {
void __iomem *hw_addr;
void __iomem *ring_msix_base;
struct pci_dev *pdev;
struct mucse_mbx_info mbx;
int port;
int speed;
bool link;
u16 cycles_per_us;
u8 pfvfnum;
u8 duplex;
};
struct mucse_stats {
u64 tx_dropped;
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 */
};
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);
}
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;
u64 length_errors;
atomic64_t dropped;
struct u64_stats_sync syncp;
} ____cacheline_aligned_in_smp;
struct mucse_rx_buffer {
struct sk_buff *skb;
dma_addr_t dma;
struct page *page;
u32 page_offset;
};
enum mucse_rx_drop_reason {
MUCSE_RX_DROP_NONE,
MUCSE_RX_DROP_RESOURCE,
MUCSE_RX_DROP_LENGTH,
};
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;
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;
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;
u32 drop_bytes; /* bytes in the current frame being dropped */
struct mucse_queue_stats *stats;
enum mucse_rx_drop_reason drop_reason;
} ____cacheline_internodealigned_in_smp;
/* Refill RX descriptors in batches of this size. */
#define M_RX_BUFFER_WRITE 16
static inline u16 mucse_desc_unused(struct mucse_ring *ring)
{
u16 ntc = ring->next_to_clean;
u16 ntu = ring->next_to_use;
return ((ntc > ntu) ? 0 : ring->count) + ntc - ntu - 1;
}
static inline u16 mucse_desc_unused_rx(struct mucse_ring *ring)
{
u16 ntc = ring->next_to_clean;
u16 ntu = ring->next_to_use;
/* Keep M_RX_BUFFER_WRITE descriptors unused so the ring is not filled
* completely. Refill is attempted once at least this many descriptors
* are available.
*/
return ((ntc > ntu) ? 0 : ring->count) + ntc - ntu - M_RX_BUFFER_WRITE;
}
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 *ring)
{
return netdev_get_tx_queue(ring->netdev, ring->queue_index);
}
struct mucse_ring_container {
struct mucse_ring *ring;
u16 count;
};
struct mucse_q_vector {
struct mucse *mucse;
/* hardware interrupt vector number */
int hw_vector;
struct mucse_ring_container rx, tx;
struct napi_struct napi;
struct timer_list rx_alloc_timer;
char name[IFNAMSIZ + 18];
/* for dynamic allocation of rings associated with this q_vector */
struct mucse_ring ring[] ____cacheline_internodealigned_in_smp;
};
#define MAX_Q_VECTORS 8
#define M_DEFAULT_TXD 512
#define M_DEFAULT_RXD 512
#define M_DEFAULT_TX_WORK 256
enum mucse_state_t {
__MUCSE_AXI_FAULT,
};
struct mucse {
struct net_device *netdev;
struct pci_dev *pdev;
struct mucse_hw hw;
struct mucse_stats stats;
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_queue_stats tx_stats[RNPGBE_MAX_QUEUES];
struct mucse_queue_stats rx_stats[RNPGBE_MAX_QUEUES];
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 rx_ring_item_count;
int num_rx_queues;
char mbx_name[32];
bool mbx_irq_requested;
bool link_event_ready;
bool link_pending;
unsigned long state;
atomic_t mbx_irq_seq;
struct work_struct mbx_work;
struct delayed_work serv_task;
/* Protects event readiness, pending state, cached link state and
* driver-side link register updates.
*/
spinlock_t link_lock;
};
int rnpgbe_get_permanent_mac(struct mucse_hw *hw, u8 *perm_addr);
int rnpgbe_reset_hw(struct mucse_hw *hw);
int rnpgbe_setup_default_link(struct mucse_hw *hw);
int rnpgbe_send_notify(struct mucse_hw *hw,
bool enable,
int mode);
int rnpgbe_init_hw(struct mucse_hw *hw, int board_type);
int rnpgbe_set_rx_mode(struct net_device *netdev,
struct netdev_hw_addr_list *uc,
struct netdev_hw_addr_list *mc);
void rnpgbe_set_link(struct mucse_hw *hw, bool linkup);
/* Device IDs */
#define PCI_VENDOR_ID_MUCSE 0x8848
@ -68,4 +266,10 @@ int rnpgbe_init_hw(struct mucse_hw *hw, int board_type);
#define mucse_hw_wr32(hw, reg, val) \
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 */

View File

@ -4,12 +4,19 @@
#include <linux/pci.h>
#include <linux/errno.h>
#include <linux/etherdevice.h>
#include <linux/ethtool.h>
#include "rnpgbe.h"
#include "rnpgbe_hw.h"
#include "rnpgbe_mbx.h"
#include "rnpgbe_mbx_fw.h"
#define RNPGBE_DEFAULT_ADV (RNPGBE_FW_ADV_10_FULL | \
RNPGBE_FW_ADV_100_FULL | \
RNPGBE_FW_ADV_1000_FULL | \
RNPGBE_FW_ADV_10_HALF | \
RNPGBE_FW_ADV_100_HALF)
/**
* rnpgbe_get_permanent_mac - Get permanent mac
* @hw: hw information structure
@ -53,6 +60,18 @@ int rnpgbe_reset_hw(struct mucse_hw *hw)
return mucse_mbx_reset_hw(hw);
}
/**
* rnpgbe_setup_default_link - Configure the default link settings
* @hw: hardware information structure
*
* Return: 0 on success, negative errno on failure
**/
int rnpgbe_setup_default_link(struct mucse_hw *hw)
{
return mucse_mbx_set_link(hw, RNPGBE_DEFAULT_ADV, true, 0, 0,
ETH_TP_MDI_AUTO);
}
/**
* rnpgbe_send_notify - Echo fw status
* @hw: hw information structure
@ -66,11 +85,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 = mucse_mbx_powerup(hw, enable);
break;
case mucse_fw_portup:
err = mucse_mbx_phyup(hw, enable);
break;
case mucse_fw_link_report_en:
err = mucse_mbx_link_report(hw, enable);
break;
default:
err = -EINVAL;
}
@ -89,8 +114,12 @@ static void rnpgbe_init_n500(struct mucse_hw *hw)
{
struct mucse_mbx_info *mbx = &hw->mbx;
hw->ring_msix_base = hw->hw_addr + MUCSE_N500_RING_MSIX_BASE;
mbx->fwpf_ctrl_base = MUCSE_N500_FWPF_CTRL_BASE;
mbx->fwpf_shm_base = MUCSE_N500_FWPF_SHM_BASE;
hw->cycles_per_us = M_DEFAULT_N500_MHZ;
}
/**
@ -104,8 +133,12 @@ static void rnpgbe_init_n210(struct mucse_hw *hw)
{
struct mucse_mbx_info *mbx = &hw->mbx;
hw->ring_msix_base = hw->hw_addr + MUCSE_N210_RING_MSIX_BASE;
mbx->fwpf_ctrl_base = MUCSE_N210_FWPF_CTRL_BASE;
mbx->fwpf_shm_base = MUCSE_N210_FWPF_SHM_BASE;
hw->cycles_per_us = M_DEFAULT_N210_MHZ;
}
/**
@ -141,3 +174,135 @@ int rnpgbe_init_hw(struct mucse_hw *hw, int board_type)
return 0;
}
static void rnpgbe_set_rar(struct mucse_hw *hw, u32 index, const u8 *addr)
{
u32 old_low, old_high;
u32 rar_low, rar_high;
/* The RAR stores the Ethernet address in reverse byte order. */
rar_low = (u32)addr[5] | ((u32)addr[4] << 8) |
((u32)addr[3] << 16) | ((u32)addr[2] << 24);
rar_high = (u32)addr[1] | ((u32)addr[0] << 8) |
RNPGBE_RX_RAR_VALID;
old_low = mucse_hw_rd32(hw, RNPGBE_RX_RAR_LOW(index));
old_high = mucse_hw_rd32(hw, RNPGBE_RX_RAR_HIGH(index));
if (old_low == rar_low && old_high == rar_high)
return;
mucse_hw_wr32(hw, RNPGBE_RX_RAR_HIGH(index),
rar_high & ~RNPGBE_RX_RAR_VALID);
mucse_hw_wr32(hw, RNPGBE_RX_RAR_LOW(index), rar_low);
mucse_hw_wr32(hw, RNPGBE_RX_RAR_HIGH(index), rar_high);
}
static void rnpgbe_clear_rar(struct mucse_hw *hw, u32 index)
{
u32 rar_high;
rar_high = mucse_hw_rd32(hw, RNPGBE_RX_RAR_HIGH(index));
if (!(rar_high & RNPGBE_RX_RAR_VALID))
return;
mucse_hw_wr32(hw, RNPGBE_RX_RAR_HIGH(index), 0);
mucse_hw_wr32(hw, RNPGBE_RX_RAR_LOW(index), 0);
}
int rnpgbe_set_rx_mode(struct net_device *netdev,
struct netdev_hw_addr_list *uc,
struct netdev_hw_addr_list *mc)
{
u32 mcast_hash[RNPGBE_RX_MCAST_HASH_ENTRIES] = {};
u32 mcast_ctrl = RNPGBE_RX_UCAST_TABLE_EN;
u32 filter_ctrl = RNPGBE_RX_FILTER_BCAST;
struct netdev_hw_addr *ha;
struct mucse_hw *hw;
struct mucse *mucse;
int rar = 1;
u16 hash;
int i;
mucse = netdev_priv(netdev);
hw = &mucse->hw;
/* RAR 0 always holds the interface's primary unicast address. */
rnpgbe_set_rar(hw, 0, netdev->dev_addr);
netdev_hw_addr_list_for_each(ha, uc) {
if (rar == RNPGBE_RX_RAR_ENTRIES) {
filter_ctrl |= RNPGBE_RX_FILTER_UCAST_ALL;
break;
}
rnpgbe_set_rar(hw, rar, ha->addr);
rar++;
}
for (; rar < RNPGBE_RX_RAR_ENTRIES; rar++)
rnpgbe_clear_rar(hw, rar);
if (netdev->flags & IFF_PROMISC) {
filter_ctrl |= RNPGBE_RX_FILTER_UCAST_ALL |
RNPGBE_RX_FILTER_MCAST_ALL;
} else if (netdev->flags & IFF_ALLMULTI) {
filter_ctrl |= RNPGBE_RX_FILTER_MCAST_ALL;
} else {
/* MTA type 0 uses ((addr[4] << 8) | addr[5]) & 0xfff. */
netdev_hw_addr_list_for_each(ha, mc) {
hash = ((ha->addr[4] & 0xf) << 8) | ha->addr[5];
mcast_hash[hash >> 5] |= BIT(hash & 0x1f);
}
if (!netdev_hw_addr_list_empty(mc))
mcast_ctrl |= RNPGBE_RX_MCAST_HASH_EN;
}
/* Widen multicast filtering before invalidating hash entries. */
if (filter_ctrl & RNPGBE_RX_FILTER_MCAST_ALL)
mucse_hw_wr32(hw, RNPGBE_RX_FILTER_CTRL, filter_ctrl);
for (i = 0; i < RNPGBE_RX_MCAST_HASH_ENTRIES; i++)
mucse_hw_wr32(hw, RNPGBE_RX_MCAST_HASH(i), mcast_hash[i]);
mucse_hw_wr32(hw, RNPGBE_RX_MCAST_CTRL, mcast_ctrl);
if (!(filter_ctrl & RNPGBE_RX_FILTER_MCAST_ALL))
mucse_hw_wr32(hw, RNPGBE_RX_FILTER_CTRL, filter_ctrl);
return 0;
}
/**
* rnpgbe_set_link - Set the hardware link state
* @hw: hw information structure
* @linkup: link on or not
*
* Apply the firmware-reported link state to the GMAC. This helper is only
* called while processing a firmware link event. The caller must hold the
* containing struct mucse's link_lock.
* Firmware updates the speed and duplex fields before posting a link event
* and waits for the PF acknowledgment, so event handling completes this
* read-modify-write before firmware accesses GMAC_CONTROL again.
*
**/
void rnpgbe_set_link(struct mucse_hw *hw, bool linkup)
{
struct mucse *mucse = container_of(hw, struct mucse, hw);
u32 value;
lockdep_assert_held(&mucse->link_lock);
value = mucse_hw_rd32(hw, GMAC_CONTROL);
/* ndo_set_rx_mode_async() programs the chip-level filter. Keep the GMAC
* in receive-all mode so it does not discard frames accepted by that
* filter.
*/
if (linkup) {
mucse_hw_wr32(hw, GMAC_FRAME_FILTER, GMAC_RX_ALL);
value |= GMAC_CONTROL_RE;
mucse_hw_wr32(hw, GMAC_CONTROL, value);
} else {
value &= ~GMAC_CONTROL_RE;
mucse_hw_wr32(hw, GMAC_CONTROL, value);
mucse_hw_wr32(hw, GMAC_FRAME_FILTER, 0);
}
}

View File

@ -4,14 +4,59 @@
#ifndef _RNPGBE_HW_H
#define _RNPGBE_HW_H
#include <linux/bits.h>
#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
#define RNPGBE_DMA_STATUS 0x0008
#define TX_AXI_RW_EN 0xc
/* DMA_STATUS_REG[23:20]: tx_wr, tx_rd, rx_wr, rx_rd done status. */
#define RNPGBE_DMA_TX_STATUS GENMASK_U32(23, 22)
#define RNPGBE_DMA_RX_STATUS GENMASK_U32(21, 20)
#define RX_AXI_RW_EN 0x03
/* RNPGBE_LINK_ST is a driver-owned link-state snapshot consumed by firmware.
* Set the driver-state marker; all other driver status fields start clear.
*/
#define M_DEFAULT_ST 0xa0000000
/* Firmware never reports 0xf in the speed field. */
#define M_INVALID_ST (M_DEFAULT_ST | GENMASK_U32(11, 8))
/* Driver-reported fields: 25:24 pause, 11:8 speed, 6 LLDP, 4 duplex,
* and 0 link up/down. M_DEFAULT_ST resets these fields so firmware reports
* the current hardware state again.
*/
#define RNPGBE_LINK_ST 0x000c
#define RNPGBE_DMA_AXI_EN 0x0010
#define RNPGBE_TX_MIN_PKT_LEN 33
#define MUCSE_ETH_OFF(_n) (0x10000 + (_n))
#define RNPGBE_RX_RAR_LOW(_n) MUCSE_ETH_OFF(0xa000 + 4 * (_n))
#define RNPGBE_RX_RAR_HIGH(_n) MUCSE_ETH_OFF(0xa400 + 4 * (_n))
#define RNPGBE_RX_MCAST_HASH(_n) MUCSE_ETH_OFF(0xac00 + 4 * (_n))
#define RNPGBE_RX_FILTER_CTRL MUCSE_ETH_OFF(0x9110)
#define RNPGBE_RX_MCAST_CTRL MUCSE_ETH_OFF(0x9114)
#define RNPGBE_RX_RAR_VALID BIT(31)
#define RNPGBE_RX_FILTER_BCAST BIT(10)
#define RNPGBE_RX_FILTER_UCAST_ALL BIT(9)
#define RNPGBE_RX_FILTER_MCAST_ALL BIT(8)
#define RNPGBE_RX_MCAST_HASH_EN BIT(4)
#define RNPGBE_RX_UCAST_TABLE_EN BIT(3)
/* The final two of the 32 hardware RAR entries are reserved for NCSI. */
#define RNPGBE_RX_RAR_ENTRIES 30
#define RNPGBE_RX_MCAST_HASH_ENTRIES 128
#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 */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,88 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright(c) 2020 - 2025 Mucse Corporation. */
#ifndef _RNPGBE_LIB_H
#define _RNPGBE_LIB_H
#include <linux/bits.h>
#include <linux/netdevice.h>
#include <linux/skbuff.h>
struct mucse;
struct mucse_hw;
struct mucse_ring;
#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_TIMEOUT_TH 0x54
#define RNPGBE_RX_SG_UNIT_BYTES 16
#define M_DEFAULT_SG 96
#define RNPGBE_RX_DESC_DATA_LEN (M_DEFAULT_SG * RNPGBE_RX_SG_UNIT_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
#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
/* | 31:24 | .... | 15:8 | 7:0 | */
/* | pfvfnum | | tx vector | rx vector | */
#define RING_VECTOR(n) (0x04 * (n))
#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)
/* Leave two descriptors between the tail and head. */
#define RESV_DESC_NEEDED 2
#define RNPGBE_SKB_PAD (NET_SKB_PAD + NET_IP_ALIGN)
/* Hardware requires this to be nonzero */
#define M_DEFAULT_MAC_IP_LEN 20
#define mucse_for_each_ring(pos, head)\
for (typeof((head).ring) __pos = (head).ring;\
__pos ? ({ pos = __pos; 1; }) : 0;\
__pos = __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_down(struct mucse *mucse);
int rnpgbe_up_complete(struct mucse *mucse);
int rnpgbe_configure_tx(struct mucse *mucse);
int rnpgbe_configure_rx(struct mucse *mucse);
void rnpgbe_clean_all_tx_rings(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);
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

View File

@ -2,11 +2,13 @@
/* Copyright(c) 2020 - 2025 Mucse Corporation. */
#include <linux/pci.h>
#include <linux/skbuff.h>
#include <net/rtnetlink.h>
#include <linux/etherdevice.h>
#include "rnpgbe.h"
#include "rnpgbe_hw.h"
#include "rnpgbe_lib.h"
#include "rnpgbe_mbx_fw.h"
static const char rnpgbe_driver_name[] = "rnpgbe";
@ -25,6 +27,32 @@ static struct pci_device_id rnpgbe_pci_tbl[] = {
{ },
};
/**
* rnpgbe_configure - Configure the hardware
* @mucse: pointer to private structure
*
* Configure Tx and Rx registers in hardware.
*
* Return: 0 on success, negative errno if hardware configuration fails
**/
static int rnpgbe_configure(struct mucse *mucse)
{
int err;
err = rnpgbe_configure_tx(mucse);
if (err)
return err;
netif_addr_lock_bh(mucse->netdev);
err = rnpgbe_set_rx_mode(mucse->netdev, &mucse->netdev->uc,
&mucse->netdev->mc);
netif_addr_unlock_bh(mucse->netdev);
if (err)
return err;
return rnpgbe_configure_rx(mucse);
}
/**
* rnpgbe_open - Called when a network interface is made active
* @netdev: network interface device structure
@ -32,11 +60,54 @@ static struct pci_device_id rnpgbe_pci_tbl[] = {
* 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 = netdev_priv(netdev);
int err;
if (test_bit(__MUCSE_AXI_FAULT, &mucse->state))
return -EIO;
netif_carrier_off(netdev);
err = rnpgbe_request_irq(mucse);
if (err)
return err;
err = netif_set_real_num_queues(netdev, mucse->num_tx_queues,
mucse->num_rx_queues);
if (err)
goto err_free_irqs;
err = rnpgbe_setup_all_tx_resources(mucse);
if (err)
goto err_free_irqs;
err = rnpgbe_setup_all_rx_resources(mucse);
if (err)
goto err_free_tx;
err = rnpgbe_configure(mucse);
if (err)
goto err_free_rx;
err = rnpgbe_up_complete(mucse);
if (err)
goto err_down;
return 0;
err_down:
rnpgbe_down(mucse);
rnpgbe_free_all_rx_resources(mucse);
rnpgbe_free_all_tx_resources(mucse);
goto err_free_irqs;
err_free_rx:
rnpgbe_free_all_rx_resources(mucse);
err_free_tx:
rnpgbe_clean_all_tx_rings(mucse);
rnpgbe_free_all_tx_resources(mucse);
err_free_irqs:
rnpgbe_free_irq(mucse);
return err;
}
/**
@ -50,6 +121,13 @@ static int rnpgbe_open(struct net_device *netdev)
**/
static int rnpgbe_close(struct net_device *netdev)
{
struct mucse *mucse = netdev_priv(netdev);
rnpgbe_down(mucse);
rnpgbe_free_irq(mucse);
rnpgbe_free_all_tx_resources(mucse);
rnpgbe_free_all_rx_resources(mucse);
return 0;
}
@ -58,25 +136,48 @@ 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 = netdev_priv(netdev);
struct mucse_ring *tx_ring;
dev_kfree_skb_any(skb);
mucse->stats.tx_dropped++;
tx_ring = mucse->tx_ring[skb_get_queue_mapping(skb)];
return NETDEV_TX_OK;
if (unlikely(skb_put_padto(skb, RNPGBE_TX_MIN_PKT_LEN))) {
atomic64_inc(&tx_ring->stats->dropped);
return NETDEV_TX_OK;
}
return rnpgbe_xmit_frame_ring(skb, tx_ring);
}
static const struct net_device_ops rnpgbe_netdev_ops = {
.ndo_open = rnpgbe_open,
.ndo_stop = rnpgbe_close,
.ndo_start_xmit = rnpgbe_xmit_frame,
.ndo_open = rnpgbe_open,
.ndo_stop = rnpgbe_close,
.ndo_start_xmit = rnpgbe_xmit_frame,
.ndo_set_rx_mode_async = rnpgbe_set_rx_mode,
.ndo_get_stats64 = rnpgbe_get_stats64,
};
static void rnpgbe_sw_init(struct mucse *mucse)
{
int i;
mucse->tx_ring_item_count = M_DEFAULT_TXD;
mucse->rx_ring_item_count = M_DEFAULT_RXD;
mucse->tx_work_limit = M_DEFAULT_TX_WORK;
for (i = 0; i < RNPGBE_MAX_QUEUES; i++) {
u64_stats_init(&mucse->tx_stats[i].syncp);
atomic64_set(&mucse->tx_stats[i].dropped, 0);
u64_stats_init(&mucse->rx_stats[i].syncp);
atomic64_set(&mucse->rx_stats[i].dropped, 0);
}
}
/**
* rnpgbe_add_adapter - Add netdev for this pci_dev
* @pdev: PCI device information structure
@ -149,11 +250,18 @@ static int rnpgbe_add_adapter(struct pci_dev *pdev,
}
netdev->netdev_ops = &rnpgbe_netdev_ops;
netdev->priv_flags |= IFF_UNICAST_FLT;
rnpgbe_sw_init(mucse);
err = rnpgbe_reset_hw(hw);
if (err) {
dev_err(&pdev->dev, "Hw reset failed %d\n", err);
goto err_powerdown;
}
err = rnpgbe_setup_default_link(hw);
if (err) {
dev_err(&pdev->dev, "Setup link failed %d\n", err);
goto err_powerdown;
}
err = rnpgbe_get_permanent_mac(hw, perm_addr);
if (!err) {
@ -166,11 +274,46 @@ static int rnpgbe_add_adapter(struct pci_dev *pdev,
goto err_powerdown;
}
INIT_DELAYED_WORK(&mucse->serv_task, rnpgbe_service_task);
spin_lock_init(&mucse->link_lock);
mucse->link_event_ready = false;
mucse->link_pending = false;
err = rnpgbe_init_interrupt_scheme(mucse);
if (err) {
dev_err(&pdev->dev, "init interrupt failed %d\n", err);
goto err_powerdown;
}
err = netif_set_real_num_queues(netdev, mucse->num_tx_queues,
mucse->num_rx_queues);
if (err)
goto err_clear_interrupt;
err = rnpgbe_request_mbx_irq(mucse);
if (err) {
dev_err(&pdev->dev, "register mbx irq failed %d\n", err);
goto err_clear_interrupt;
}
netdev->features |= NETIF_F_SG;
netdev->hw_features |= NETIF_F_SG;
if (dma_get_mask(&pdev->dev) > DMA_BIT_MASK(32)) {
netdev->features |= NETIF_F_HIGHDMA;
netdev->hw_features |= NETIF_F_HIGHDMA;
}
netif_carrier_off(netdev);
err = register_netdev(netdev);
if (err)
goto err_powerdown;
goto err_remove_mbx;
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) {
@ -203,12 +346,7 @@ static int rnpgbe_probe(struct pci_dev *pdev, const struct pci_device_id *id)
if (err)
return err;
err = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(56));
if (err) {
dev_err(&pdev->dev,
"No usable DMA configuration, aborting %d\n", err);
goto err_disable_dev;
}
dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(56));
err = pci_request_mem_regions(pdev, rnpgbe_driver_name);
if (err) {
@ -236,6 +374,26 @@ static int rnpgbe_probe(struct pci_dev *pdev, const struct pci_device_id *id)
return err;
}
/**
* rnpgbe_notify_powerdown - Notify firmware that the driver is going down
* @mucse: pointer to private structure
*
* Skip the mailbox access if an earlier shutdown has already disabled the
* PCI device.
**/
static void rnpgbe_notify_powerdown(struct mucse *mucse)
{
int err;
if (!pci_is_enabled(mucse->pdev))
return;
err = rnpgbe_send_notify(&mucse->hw, false, mucse_fw_powerup);
if (err)
dev_warn(&mucse->pdev->dev,
"Send powerdown to hw failed %d\n", err);
}
/**
* rnpgbe_rm_adapter - Remove netdev for this mucse structure
* @pdev: PCI device information struct
@ -245,17 +403,15 @@ static int rnpgbe_probe(struct pci_dev *pdev, const struct pci_device_id *id)
static void rnpgbe_rm_adapter(struct pci_dev *pdev)
{
struct mucse *mucse = pci_get_drvdata(pdev);
struct mucse_hw *hw = &mucse->hw;
struct net_device *netdev;
int err;
if (!mucse)
return;
netdev = mucse->netdev;
unregister_netdev(netdev);
err = rnpgbe_send_notify(hw, false, mucse_fw_powerup);
if (err)
dev_warn(&pdev->dev, "Send powerdown to hw failed %d\n", err);
rnpgbe_free_mbx_irq(mucse);
rnpgbe_notify_powerdown(mucse);
rnpgbe_clear_interrupt_scheme(mucse);
free_netdev(netdev);
}
@ -272,7 +428,8 @@ static void rnpgbe_remove(struct pci_dev *pdev)
{
rnpgbe_rm_adapter(pdev);
pci_release_mem_regions(pdev);
pci_disable_device(pdev);
if (pci_is_enabled(pdev))
pci_disable_device(pdev);
}
/**
@ -287,9 +444,14 @@ static void rnpgbe_dev_shutdown(struct pci_dev *pdev)
rtnl_lock();
netif_device_detach(netdev);
if (netif_running(netdev))
rnpgbe_close(netdev);
dev_close(netdev);
rtnl_unlock();
pci_disable_device(pdev);
rnpgbe_free_mbx_irq(mucse);
rnpgbe_notify_powerdown(mucse);
rnpgbe_clear_interrupt_scheme(mucse);
if (pci_is_enabled(pdev))
pci_disable_device(pdev);
}
/**

View File

@ -84,7 +84,7 @@ static u32 mucse_mbx_get_lock_pf(struct mucse_hw *hw)
* @hw: pointer to the HW structure
*
* Pair with mucse_release_mbx_lock_pf()
* This function maybe used in an irq handler.
* All mailbox access runs in process context.
*
* Return: 0 on success, negative errno on failure
**/
@ -93,11 +93,11 @@ static int mucse_obtain_mbx_lock_pf(struct mucse_hw *hw)
struct mucse_mbx_info *mbx = &hw->mbx;
u32 val;
return read_poll_timeout_atomic(mucse_mbx_get_lock_pf,
val, val & MUCSE_MBX_PFU,
mbx->delay_us,
mbx->timeout_us,
false, hw);
return read_poll_timeout(mucse_mbx_get_lock_pf,
val, val & MUCSE_MBX_PFU,
mbx->delay_us,
mbx->timeout_us,
false, hw);
}
/**
@ -197,7 +197,8 @@ static int mucse_read_mbx_pf(struct mucse_hw *hw, __le32 *msg, u16 size)
* mucse_check_for_msg_pf - Check to see if the fw has sent mail
* @hw: pointer to the HW structure
*
* Return: 0 if the fw has set the Status bit or else -EIO
* Return: 0 if firmware has posted a new message, -ENOMSG if there is no
* new message, or -EIO if the mailbox has been reset
**/
static int mucse_check_for_msg_pf(struct mucse_hw *hw)
{
@ -209,8 +210,13 @@ static int mucse_check_for_msg_pf(struct mucse_hw *hw)
* mbx command. Return -EIO if in this state, others
* fw == hw->mbx.fw_req means no new msg.
**/
if (fw_req == 0 || fw_req == hw->mbx.fw_req)
/* Firmware skips zero for valid FW-to-PF requests and wraps the
* counter from U16_MAX to 1.
**/
if (!fw_req)
return -EIO;
if (fw_req == hw->mbx.fw_req)
return -ENOMSG;
return 0;
}
@ -252,6 +258,68 @@ int mucse_poll_and_read_mbx(struct mucse_hw *hw, __le32 *msg, u16 size)
return mucse_read_mbx_pf(hw, msg, size);
}
/**
* mucse_mbx_event_begin - Lock and read a pending firmware event
* @hw: pointer to the HW structure
* @msg: the message buffer
* @size: length of buffer
*
* On success the hardware mailbox remains locked. The caller must process
* the event and call mucse_mbx_event_end() to acknowledge it and release
* the hardware mailbox lock.
*
* Firmware releases the hardware mailbox lock before raising the event
* interrupt and does not access the shared window again until the PF
* acknowledges the event. Therefore, failure to acquire the lock indicates
* a mailbox fault rather than transient contention.
*
* Return: 0 on success, -ENOMSG if no event is pending, or another negative
* errno on failure
**/
int mucse_mbx_event_begin(struct mucse_hw *hw, __le32 *msg, u16 size)
{
const int size_in_words = size / sizeof(__le32);
struct mucse_mbx_info *mbx = &hw->mbx;
int off = MUCSE_MBX_FWPF_SHM;
int err;
err = mucse_check_for_msg_pf(hw);
if (err)
return err;
err = mucse_obtain_mbx_lock_pf(hw);
if (err)
return err;
/* Check again after taking ownership of the shared mailbox. */
err = mucse_check_for_msg_pf(hw);
if (err) {
mucse_release_mbx_lock_pf(hw, false);
return err;
}
for (int i = 0; i < size_in_words; i++)
msg[i] = cpu_to_le32(mbx_data_rd32(mbx, off + 4 * i));
return 0;
}
/**
* mucse_mbx_event_end - Acknowledge a firmware event and unlock the mailbox
* @hw: pointer to the HW structure
*
* Pair with a successful mucse_mbx_event_begin().
**/
void mucse_mbx_event_end(struct mucse_hw *hw)
{
struct mucse_mbx_info *mbx = &hw->mbx;
mbx_data_wr32(mbx, MUCSE_MBX_FWPF_SHM, 0);
hw->mbx.fw_req = mucse_mbx_get_fwreq(mbx);
mucse_mbx_inc_pf_ack(hw);
mucse_release_mbx_lock_pf(hw, false);
}
/**
* mucse_mbx_get_fwack - Read fw ack from reg
* @mbx: pointer to the MBX structure
@ -286,6 +354,36 @@ static void mucse_mbx_inc_pf_req(struct mucse_hw *hw)
mbx_data_wr32(mbx, MUCSE_MBX_PF2FW_CNT, val);
}
/**
* mucse_ack_pending_event - Acknowledge a level-like firmware event
* @hw: pointer to the HW structure
*
* The caller must own the hardware mailbox lock.
*
* Return: 0 on success, negative errno on failure
**/
static int mucse_ack_pending_event(struct mucse_hw *hw)
{
struct mucse_mbx_info *mbx = &hw->mbx;
u16 fw_req;
fw_req = mucse_mbx_get_fwreq(mbx);
if (fw_req == hw->mbx.fw_req)
return 0;
if (!fw_req)
return -EIO;
/* Firmware only posts LINK_CHANGE_EVT asynchronously. It is level-like,
* so leaving the link snapshot unchanged lets firmware report the
* current state again if reporting stays enabled.
*/
mbx_data_wr32(mbx, MUCSE_MBX_FWPF_SHM, 0);
hw->mbx.fw_req = fw_req;
mucse_mbx_inc_pf_ack(hw);
return 0;
}
/**
* mucse_write_mbx_pf - Place a message in the mailbox
* @hw: pointer to the HW structure
@ -320,6 +418,44 @@ static int mucse_write_mbx_pf(struct mucse_hw *hw, const __le32 *msg, u16 size)
return 0;
}
/**
* mucse_write_mbx_pf_coalesce_event - Write around a level-like event
* @hw: pointer to the HW structure
* @msg: the message buffer
* @size: length of buffer
*
* Return: 0 on success, negative errno on failure
**/
static int mucse_write_mbx_pf_coalesce_event(struct mucse_hw *hw,
const __le32 *msg, u16 size)
{
const int size_in_words = size / sizeof(__le32);
struct mucse_mbx_info *mbx = &hw->mbx;
int err;
err = mucse_obtain_mbx_lock_pf(hw);
if (err)
return err;
err = mucse_ack_pending_event(hw);
if (err)
goto release;
for (int i = 0; i < size_in_words; i++)
mbx_data_wr32(mbx, MUCSE_MBX_FWPF_SHM + i * 4,
le32_to_cpu(msg[i]));
hw->mbx.fw_ack = mucse_mbx_get_fwack(mbx);
mucse_mbx_inc_pf_req(hw);
mucse_release_mbx_lock_pf(hw, true);
return 0;
release:
mucse_release_mbx_lock_pf(hw, false);
return err;
}
/**
* mucse_check_for_ack_pf - Check to see if the fw has ACKed
* @hw: pointer to the HW structure
@ -381,6 +517,33 @@ int mucse_write_and_wait_ack_mbx(struct mucse_hw *hw, const __le32 *msg,
return mucse_poll_for_ack(hw);
}
/**
* mucse_write_mbx_coalesce_event - Send a command while coalescing an event
* @hw: pointer to the HW structure
* @msg: the message buffer
* @size: length of buffer
*
* A pending link event is acknowledged without updating its snapshot, then
* the PF command is written while the hardware mailbox remains locked. If
* event reporting remains enabled, firmware reports the event again because
* its snapshot still differs from the hardware state. This function waits
* for firmware to acknowledge the PF command before returning.
*
* Return: 0 after firmware acknowledges the command, negative errno on
* failure
**/
int mucse_write_mbx_coalesce_event(struct mucse_hw *hw,
const __le32 *msg, u16 size)
{
int err;
err = mucse_write_mbx_pf_coalesce_event(hw, msg, size);
if (err)
return err;
return mucse_poll_for_ack(hw);
}
/**
* mucse_mbx_reset - Reset mbx info, sync info from regs
* @hw: pointer to the HW structure

View File

@ -16,6 +16,10 @@
int mucse_write_and_wait_ack_mbx(struct mucse_hw *hw,
const __le32 *msg, u16 size);
int mucse_write_mbx_coalesce_event(struct mucse_hw *hw,
const __le32 *msg, u16 size);
void mucse_init_mbx_params_pf(struct mucse_hw *hw);
int mucse_poll_and_read_mbx(struct mucse_hw *hw, __le32 *msg, u16 size);
int mucse_mbx_event_begin(struct mucse_hw *hw, __le32 *msg, u16 size);
void mucse_mbx_event_end(struct mucse_hw *hw);
#endif /* _RNPGBE_MBX_H */

View File

@ -3,6 +3,7 @@
#include <linux/if_ether.h>
#include <linux/bitfield.h>
#include <linux/pci.h>
#include "rnpgbe.h"
#include "rnpgbe_mbx.h"
@ -197,3 +198,285 @@ int mucse_mbx_get_macaddr(struct mucse_hw *hw, int pfvfnum,
return 0;
}
/**
* mucse_mbx_set_link - Configure firmware link settings
* @hw: pointer to the HW structure
* @advertising: firmware link-mode advertisement mask
* @autoneg: whether to enable autonegotiation
* @speed: forced link speed when autonegotiation is disabled
* @duplex: forced duplex mode when autonegotiation is disabled
* @mdix_ctrl: MDI/MDI-X control mode
*
* Firmware ignores @speed, @duplex when @autoneg is true.
*
* Return: 0 on success, negative errno on failure
**/
int mucse_mbx_set_link(struct mucse_hw *hw, u32 advertising, bool autoneg,
u32 speed, u32 duplex, u32 mdix_ctrl)
{
union mbx_fw_cmd_req_u req = {
.r = {
.datalen = cpu_to_le16(sizeof(req.r.phy_link_set) +
MUCSE_MBX_REQ_HDR_LEN),
.opcode = cpu_to_le16(PHY_LINK_SET),
.phy_link_set = {
.adv_speed_mask = cpu_to_le32(advertising),
.autoneg = cpu_to_le32(autoneg),
.speed = cpu_to_le32(speed),
.duplex = cpu_to_le32(duplex),
.nr_lane = cpu_to_le32(hw->port),
.tp_mdix_ctrl = cpu_to_le32(mdix_ctrl),
},
},
};
int len, err;
len = le16_to_cpu(req.r.datalen);
mutex_lock(&hw->mbx.lock);
err = mucse_write_and_wait_ack_mbx(hw, req.dwords, len);
mutex_unlock(&hw->mbx.lock);
return err;
}
/**
* mucse_mbx_phyup - Request that firmware bring the PHY up or down
* @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)
{
union mbx_fw_cmd_req_u req = {
.r = {
.datalen = cpu_to_le16(sizeof(req.r.phy_status) +
MUCSE_MBX_REQ_HDR_LEN),
.opcode = cpu_to_le16(SET_PHY_UP),
.phy_status = {
.port_mask = cpu_to_le32(BIT(hw->port)),
.status = cpu_to_le32(is_phyup ? 1 : 0),
},
},
};
int len, err;
len = le16_to_cpu(req.r.datalen);
mutex_lock(&hw->mbx.lock);
err = mucse_write_mbx_coalesce_event(hw, req.dwords, len);
mutex_unlock(&hw->mbx.lock);
return err;
}
/**
* mucse_mbx_link_report - Configure firmware link-change event reporting
* @hw: pointer to the HW structure
* @is_report: true for report, false for no
*
* mucse_mbx_link_report 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)
{
union mbx_fw_cmd_req_u req = {
.r = {
.datalen = cpu_to_le16(sizeof(req.r.report_status) +
MUCSE_MBX_REQ_HDR_LEN),
.opcode = cpu_to_le16(LINK_REPORT_EN),
.report_status = {
.port_mask = cpu_to_le16(BIT(hw->port)),
.status = cpu_to_le16(is_report ? 1 : 0),
},
},
};
int len, err;
len = le16_to_cpu(req.r.datalen);
mutex_lock(&hw->mbx.lock);
err = mucse_write_mbx_coalesce_event(hw, req.dwords, len);
mutex_unlock(&hw->mbx.lock);
return err;
}
static bool mucse_link_speed_valid(const struct mbx_fw_cmd_req *req)
{
switch (le16_to_cpu(req->link_stat.st.speed)) {
case 10:
case 100:
case 1000:
return true;
default:
return false;
}
}
static bool mucse_link_is_up(const struct mucse_hw *hw,
const struct mbx_fw_cmd_req *req)
{
return le16_to_cpu(req->link_stat.port_status) & BIT(hw->port);
}
/**
* mucse_update_link_status_reg - update driver speed inf to reg
* @hw: pointer to the HW structure
* @req: pointer to req data
*
* Update the driver's link-state snapshot exported to firmware. Firmware
* sends a new event when this snapshot differs from the hardware state.
* The default snapshot clears the driver-reported fields;
* a valid event then repopulates them, including the LLDP status in bit 6.
*
**/
static void mucse_update_link_status_reg(struct mucse_hw *hw,
struct mbx_fw_cmd_req *req)
{
u16 status = le16_to_cpu(req->link_stat.st.status);
u16 speed = le16_to_cpu(req->link_stat.st.speed);
u32 value;
value = M_DEFAULT_ST;
if (mucse_link_is_up(hw, req)) {
value |= BIT(0);
switch (speed) {
case 10:
value |= (mucse_speed_10 << 8);
break;
case 100:
value |= (mucse_speed_100 << 8);
break;
case 1000:
value |= (mucse_speed_1000 << 8);
break;
default:
break;
}
value |= FIELD_PREP(BIT(4),
!!(req->link_stat.st.flags & DUPLEX_BIT));
value |= FIELD_PREP(GENMASK_U32(25, 24),
status & GENMASK(1, 0));
} else {
value &= ~BIT(0);
}
if (status & ST_STATUS_LLDP_STATUS_MASK)
value |= BIT(6);
else
value &= ~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
*
* mucse_mbx_fw_req_handler handler fw req, such as a link event req.
**/
static void mucse_mbx_fw_req_handler(struct mucse_hw *hw,
struct mbx_fw_cmd_req *req)
{
struct mucse *mucse = container_of(hw, struct mucse, hw);
u32 magic = le32_to_cpu(req->link_stat.port_magic);
unsigned long flags;
if (le16_to_cpu(req->opcode) == LINK_CHANGE_EVT) {
u16 speed = le16_to_cpu(req->link_stat.st.speed);
spin_lock_irqsave(&mucse->link_lock, flags);
if (magic != ST_VALID_MAGIC) {
/* Do not change the cached state for an invalid event.
* Use an invalid speed encoding to make firmware report
* again.
*/
mucse_hw_wr32(hw, RNPGBE_LINK_ST, M_INVALID_ST);
spin_unlock_irqrestore(&mucse->link_lock, flags);
return;
}
if (mucse_link_is_up(hw, req) &&
!mucse_link_speed_valid(req)) {
/* Do not acknowledge an invalid speed as valid.
* Keep the snapshot mismatched so firmware retries it.
* Firmware limits link reports to one per 500 ms.
*/
mucse_hw_wr32(hw, RNPGBE_LINK_ST, M_INVALID_ST);
spin_unlock_irqrestore(&mucse->link_lock, flags);
dev_warn_ratelimited(&hw->pdev->dev,
"unsupported link speed %u Mbps\n",
speed);
return;
}
if (!mucse->link_event_ready) {
rnpgbe_set_link(hw, false);
mucse_update_link_status_reg(hw, req);
spin_unlock_irqrestore(&mucse->link_lock, flags);
return;
}
if (mucse_link_is_up(hw, req))
hw->link = true;
else
hw->link = false;
hw->speed = le16_to_cpu(req->link_stat.st.speed);
hw->duplex = req->link_stat.st.flags & DUPLEX_BIT;
rnpgbe_set_link(hw, mucse_link_is_up(hw, req));
/* update regs to notify link info is received */
mucse_update_link_status_reg(hw, req);
mucse->link_pending = true;
/* Run link handling immediately. */
mod_delayed_work(system_percpu_wq, &mucse->serv_task, 0);
spin_unlock_irqrestore(&mucse->link_lock, flags);
}
}
/**
* mucse_fw_handle_event - Handle one pending firmware event
* @hw: pointer to the hardware structure
**/
static void mucse_fw_handle_event(struct mucse_hw *hw)
{
union mbx_fw_cmd_req_u msg = {};
int err;
/* try to check and read fw req */
mutex_lock(&hw->mbx.lock);
err = mucse_mbx_event_begin(hw, msg.dwords, sizeof(msg));
/* A PF command may have coalesced the event before stale mailbox work
* runs, in which case there is no event left to handle.
*/
if (err) {
mutex_unlock(&hw->mbx.lock);
if (err != -ENOMSG)
dev_warn_ratelimited(&hw->pdev->dev,
"failed to read firmware event: %d\n",
err);
return;
}
mucse_mbx_fw_req_handler(hw, &msg.r);
mucse_mbx_event_end(hw);
mutex_unlock(&hw->mbx.lock);
}
/**
* mucse_fw_irq_handler - Handle one pending firmware mailbox event
* @hw: pointer to the HW structure
*
* Process at most one event per work-item invocation. The caller requeues
* mailbox work when a dedicated mailbox interrupt arrives during handling.
**/
void mucse_fw_irq_handler(struct mucse_hw *hw)
{
mucse_fw_handle_event(hw);
}

View File

@ -14,9 +14,19 @@ enum MUCSE_FW_CMD {
GET_HW_INFO = 0x0601,
GET_MAC_ADDRESS = 0x0602,
RESET_HW = 0x0603,
LINK_CHANGE_EVT = 0x0608,
LINK_REPORT_EN = 0x0613,
PHY_LINK_SET = 0x0630,
SET_PHY_UP = 0x0800,
POWER_UP = 0x0803,
};
#define RNPGBE_FW_ADV_10_FULL BIT(2)
#define RNPGBE_FW_ADV_100_FULL BIT(3)
#define RNPGBE_FW_ADV_1000_FULL BIT(4)
#define RNPGBE_FW_ADV_10_HALF BIT(10)
#define RNPGBE_FW_ADV_100_HALF BIT(11)
struct mucse_hw_info {
u8 link_stat;
u8 port_mask;
@ -36,6 +46,16 @@ struct mucse_hw_info {
__le32 ext_info;
} __packed;
#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,6 +75,32 @@ struct mbx_fw_cmd_req {
__le32 port_mask;
__le32 pfvf_num;
} get_mac_addr;
struct {
__le32 port_mask;
__le32 status;
} phy_status;
struct {
__le32 adv_speed_mask;
__le32 autoneg;
__le32 speed;
__le32 duplex;
__le32 nr_lane;
__le32 tp_mdix_ctrl;
} phy_link_set;
struct {
/* LINK_REPORT_EN uses its own 16-bit payload layout:
* status precedes port_mask.
*/
__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;
@ -94,9 +140,23 @@ union mbx_fw_cmd_reply_u {
__le32 dwords[sizeof(struct mbx_fw_cmd_reply) / sizeof(__le32)];
};
/* Firmware encoding for RNPGBE_LINK_ST[11:8]. Valid link events report
* only these three speeds.
*/
enum mucse_speed {
mucse_speed_10 = 0,
mucse_speed_100 = 1,
mucse_speed_1000 = 2,
};
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_set_link(struct mucse_hw *hw, u32 advertising, bool autoneg,
u32 speed, u32 duplex, u32 mdix_ctrl);
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 */