* [PATCH net-next v9 1/7] r8169: add support for multi irqs
From: javen @ 2026-06-29 7:13 UTC (permalink / raw)
To: hkallweit1, nic_swsd, andrew+netdev, davem, edumazet, kuba,
pabeni, horms
Cc: netdev, linux-kernel, Javen Xu
In-Reply-To: <20260629071339.1605-1-javen_xu@realsil.com.cn>
From: Javen Xu <javen_xu@realsil.com.cn>
RSS uses multi rx queues to receive packets, and each rx queue needs one
irq and napi. So this patch adds support for multi irqs and napi here.
Signed-off-by: Javen Xu <javen_xu@realsil.com.cn>
---
Changes in v2:
- remove some unused definitions, such as index, name in rtl8169_irq
- remove array imr and isr
- remove min_irq_nvecs and max_irq_nvecs, replaced with help function
get_min_irq_nvecs and get_max_irq_nvecs
- alloc irq by flags, instead of PCI_IRQ_ALL_TYPES
Changes in v3:
- add enum rtl_isr_version to replace macro definition
- remove struct rtl8169_napi, use napi_struct array instead and alloc
memory for this array dynamically
- remove struct rtl8169_irq
Changes in v4:
- change retval to ret in rtl8169_set_real_num_queue()
- reverse xmas tree in rtl8169_poll() and rtl8169_interrupt()
- remove tp->hw_supp_isr_ver
Changes in v5:
- rtl8169_request_irq(), when failed, only free irqs which are
allocated
- remove rss_support, simplied napi init, call r8169_init_napi()
directly
- remove rtl_isr_version, INTR_VEC_MAP_MASK, INTR_VEC_MAP_STATUS,
R8169_MAX_MSIX_VEC, rss_enable, recheck_desc_ownbit
- rtl_software_parameter_initialize() this function will be expanded in
next patch, so i want to remain it here.
Changes in v6:
- Fix netpoll crash
- Fix use-after-free during driver unload by registering a devm action
for netif_napi_del()
- remove tp->irq
Changes in v7:
- pass NAPI as arg to rtl_rx()
- use netif_set_real_num_queues to replace rtl8169_set_real_num_queues
- replace rtl_software_parameter_initialize with rtl_setup_rx_params
Changes in v8:
- no changes
Changes in v9:
- no changes
---
drivers/net/ethernet/realtek/r8169_main.c | 150 +++++++++++++++++-----
1 file changed, 121 insertions(+), 29 deletions(-)
diff --git a/drivers/net/ethernet/realtek/r8169_main.c b/drivers/net/ethernet/realtek/r8169_main.c
index ec4fc21fa21f..8f3a5c50299f 100644
--- a/drivers/net/ethernet/realtek/r8169_main.c
+++ b/drivers/net/ethernet/realtek/r8169_main.c
@@ -733,7 +733,6 @@ struct rtl8169_private {
struct pci_dev *pci_dev;
struct net_device *dev;
struct phy_device *phydev;
- struct napi_struct napi;
enum mac_version mac_version;
enum rtl_dash_type dash_type;
u32 cur_rx; /* Index into the Rx descriptor buffer of next Rx pkt. */
@@ -745,10 +744,12 @@ struct rtl8169_private {
dma_addr_t RxPhyAddr;
struct page *Rx_databuff[NUM_RX_DESC]; /* Rx data buffers */
struct ring_info tx_skb[NUM_TX_DESC]; /* Tx data buffers */
+ struct napi_struct *rtl8169_napi;
+ unsigned int num_rx_rings;
u16 cp_cmd;
u16 tx_lpi_timer;
u32 irq_mask;
- int irq;
+ unsigned int irq_nvecs;
struct clk *clk;
struct {
@@ -2680,6 +2681,11 @@ static void rtl_hw_reset(struct rtl8169_private *tp)
rtl_loop_wait_low(tp, &rtl_chipcmd_cond, 100, 100);
}
+static void rtl_setup_rx_params(struct rtl8169_private *tp)
+{
+ tp->num_rx_rings = 1;
+}
+
static void rtl_request_firmware(struct rtl8169_private *tp)
{
struct rtl_fw *rtl_fw;
@@ -4266,9 +4272,21 @@ static void rtl8169_tx_clear(struct rtl8169_private *tp)
netdev_reset_queue(tp->dev);
}
+static void rtl8169_napi_disable(struct rtl8169_private *tp)
+{
+ for (int i = 0; i < tp->irq_nvecs; i++)
+ napi_disable(&tp->rtl8169_napi[i]);
+}
+
+static void rtl8169_napi_enable(struct rtl8169_private *tp)
+{
+ for (int i = 0; i < tp->irq_nvecs; i++)
+ napi_enable(&tp->rtl8169_napi[i]);
+}
+
static void rtl8169_cleanup(struct rtl8169_private *tp)
{
- napi_disable(&tp->napi);
+ rtl8169_napi_disable(tp);
/* Give a racing hard_start_xmit a few cycles to complete. */
synchronize_net();
@@ -4314,7 +4332,7 @@ static void rtl_reset_work(struct rtl8169_private *tp)
for (i = 0; i < NUM_RX_DESC; i++)
rtl8169_mark_to_asic(tp->RxDescArray + i);
- napi_enable(&tp->napi);
+ rtl8169_napi_enable(tp);
rtl_hw_start(tp);
}
@@ -4768,7 +4786,8 @@ static inline void rtl8169_rx_csum(struct sk_buff *skb, u32 opts1)
skb_checksum_none_assert(skb);
}
-static int rtl_rx(struct net_device *dev, struct rtl8169_private *tp, int budget)
+static int rtl_rx(struct net_device *dev, struct rtl8169_private *tp,
+ int budget, struct napi_struct *napi)
{
struct device *d = tp_to_dev(tp);
int count;
@@ -4820,7 +4839,7 @@ static int rtl_rx(struct net_device *dev, struct rtl8169_private *tp, int budget
goto release_descriptor;
}
- skb = napi_alloc_skb(&tp->napi, pkt_size);
+ skb = napi_alloc_skb(napi, pkt_size);
if (unlikely(!skb)) {
dev->stats.rx_dropped++;
goto release_descriptor;
@@ -4844,7 +4863,7 @@ static int rtl_rx(struct net_device *dev, struct rtl8169_private *tp, int budget
if (skb->pkt_type == PACKET_MULTICAST)
dev->stats.multicast++;
- napi_gro_receive(&tp->napi, skb);
+ napi_gro_receive(napi, skb);
dev_sw_netstats_rx_add(dev, pkt_size);
release_descriptor:
@@ -4856,8 +4875,12 @@ static int rtl_rx(struct net_device *dev, struct rtl8169_private *tp, int budget
static irqreturn_t rtl8169_interrupt(int irq, void *dev_instance)
{
- struct rtl8169_private *tp = dev_instance;
- u32 status = rtl_get_events(tp);
+ struct napi_struct *napi = dev_instance;
+ struct rtl8169_private *tp;
+ u32 status;
+
+ tp = netdev_priv(napi->dev);
+ status = rtl_get_events(tp);
if ((status & 0xffff) == 0xffff || !(status & tp->irq_mask))
return IRQ_NONE;
@@ -4873,13 +4896,43 @@ static irqreturn_t rtl8169_interrupt(int irq, void *dev_instance)
phy_mac_interrupt(tp->phydev);
rtl_irq_disable(tp);
- napi_schedule(&tp->napi);
+ napi_schedule(napi);
out:
rtl_ack_events(tp, status);
return IRQ_HANDLED;
}
+static void rtl8169_free_irq(struct rtl8169_private *tp)
+{
+ for (int i = 0; i < tp->irq_nvecs; i++) {
+ struct napi_struct *napi = &tp->rtl8169_napi[i];
+
+ pci_free_irq(tp->pci_dev, i, napi);
+ }
+}
+
+static int rtl8169_request_irq(struct rtl8169_private *tp)
+{
+ struct net_device *dev = tp->dev;
+ struct napi_struct *napi;
+ int i, rc;
+
+ for (i = 0; i < tp->irq_nvecs; i++) {
+ napi = &tp->rtl8169_napi[i];
+ rc = pci_request_irq(tp->pci_dev, i, rtl8169_interrupt,
+ NULL, napi, "%s-%d", dev->name, i);
+ if (rc)
+ goto free_irq;
+ }
+ return 0;
+
+free_irq:
+ while (--i >= 0)
+ pci_free_irq(tp->pci_dev, i, &tp->rtl8169_napi[i]);
+ return rc;
+}
+
static void rtl_task(struct work_struct *work)
{
struct rtl8169_private *tp =
@@ -4914,13 +4967,13 @@ static void rtl_task(struct work_struct *work)
static int rtl8169_poll(struct napi_struct *napi, int budget)
{
- struct rtl8169_private *tp = container_of(napi, struct rtl8169_private, napi);
- struct net_device *dev = tp->dev;
- int work_done;
+ struct rtl8169_private *tp = netdev_priv(napi->dev);
+ struct net_device *dev = napi->dev;
+ int work_done = 0;
rtl_tx(dev, tp, budget);
- work_done = rtl_rx(dev, tp, budget);
+ work_done = rtl_rx(dev, tp, budget, napi);
if (work_done < budget && napi_complete_done(napi, work_done))
rtl_irq_enable(tp);
@@ -5035,7 +5088,7 @@ static void rtl8169_up(struct rtl8169_private *tp)
phy_init_hw(tp->phydev);
phy_resume(tp->phydev);
rtl8169_init_phy(tp);
- napi_enable(&tp->napi);
+ rtl8169_napi_enable(tp);
enable_work(&tp->wk.work);
rtl_reset_work(tp);
@@ -5053,7 +5106,7 @@ static int rtl8169_close(struct net_device *dev)
rtl8169_down(tp);
rtl8169_rx_clear(tp);
- free_irq(tp->irq, tp);
+ rtl8169_free_irq(tp);
phy_disconnect(tp->phydev);
@@ -5074,7 +5127,10 @@ static void rtl8169_netpoll(struct net_device *dev)
{
struct rtl8169_private *tp = netdev_priv(dev);
- rtl8169_interrupt(tp->irq, tp);
+ for (int i = 0; i < tp->irq_nvecs; i++) {
+ rtl8169_interrupt(pci_irq_vector(tp->pci_dev, i),
+ &tp->rtl8169_napi[i]);
+ }
}
#endif
@@ -5082,7 +5138,6 @@ static int rtl_open(struct net_device *dev)
{
struct rtl8169_private *tp = netdev_priv(dev);
struct pci_dev *pdev = tp->pci_dev;
- unsigned long irqflags;
int retval = -ENOMEM;
pm_runtime_get_sync(&pdev->dev);
@@ -5107,8 +5162,7 @@ static int rtl_open(struct net_device *dev)
rtl_request_firmware(tp);
- irqflags = pci_dev_msi_enabled(pdev) ? IRQF_NO_THREAD : IRQF_SHARED;
- retval = request_irq(tp->irq, rtl8169_interrupt, irqflags, dev->name, tp);
+ retval = rtl8169_request_irq(tp);
if (retval < 0)
goto err_release_fw_2;
@@ -5125,7 +5179,7 @@ static int rtl_open(struct net_device *dev)
return retval;
err_free_irq:
- free_irq(tp->irq, tp);
+ rtl8169_free_irq(tp);
err_release_fw_2:
rtl_release_firmware(tp);
rtl8169_rx_clear(tp);
@@ -5275,6 +5329,14 @@ static void rtl_shutdown(struct pci_dev *pdev)
pci_prepare_to_sleep(pdev);
}
+static void r8169_free_napi(struct rtl8169_private *tp)
+{
+ for (int i = 0; i < tp->irq_nvecs; i++)
+ netif_napi_del(&tp->rtl8169_napi[i]);
+
+ kfree(tp->rtl8169_napi);
+}
+
static void rtl_remove_one(struct pci_dev *pdev)
{
struct rtl8169_private *tp = pci_get_drvdata(pdev);
@@ -5289,6 +5351,8 @@ static void rtl_remove_one(struct pci_dev *pdev)
unregister_netdev(tp->dev);
+ r8169_free_napi(tp);
+
if (tp->dash_type != RTL_DASH_NONE)
rtl8168_driver_stop(tp);
@@ -5328,7 +5392,9 @@ static void rtl_set_irq_mask(struct rtl8169_private *tp)
static int rtl_alloc_irq(struct rtl8169_private *tp)
{
+ struct pci_dev *pdev = tp->pci_dev;
unsigned int flags;
+ int nvecs;
switch (tp->mac_version) {
case RTL_GIGA_MAC_VER_02 ... RTL_GIGA_MAC_VER_06:
@@ -5344,7 +5410,14 @@ static int rtl_alloc_irq(struct rtl8169_private *tp)
break;
}
- return pci_alloc_irq_vectors(tp->pci_dev, 1, 1, flags);
+ nvecs = pci_alloc_irq_vectors(pdev, 1, 1, flags);
+
+ if (nvecs < 0)
+ return nvecs;
+
+ tp->irq_nvecs = nvecs;
+
+ return 0;
}
static void rtl_read_mac_address(struct rtl8169_private *tp,
@@ -5599,6 +5672,12 @@ static bool rtl_aspm_is_safe(struct rtl8169_private *tp)
return false;
}
+static void r8169_init_napi(struct rtl8169_private *tp)
+{
+ for (int i = 0; i < tp->irq_nvecs; i++)
+ netif_napi_add(tp->dev, &tp->rtl8169_napi[i], rtl8169_poll);
+}
+
static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
{
const struct rtl_chip_info *chip;
@@ -5703,12 +5782,12 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
rtl_hw_reset(tp);
+ rtl_setup_rx_params(tp);
+
rc = rtl_alloc_irq(tp);
if (rc < 0)
return dev_err_probe(&pdev->dev, rc, "Can't allocate interrupt\n");
- tp->irq = pci_irq_vector(pdev, 0);
-
INIT_WORK(&tp->wk.work, rtl_task);
disable_work(&tp->wk.work);
@@ -5716,8 +5795,6 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
dev->ethtool_ops = &rtl8169_ethtool_ops;
- netif_napi_add(dev, &tp->napi, rtl8169_poll);
-
dev->hw_features = NETIF_F_IP_CSUM | NETIF_F_RXCSUM |
NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX;
dev->vlan_features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_TSO;
@@ -5778,6 +5855,10 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
if (jumbo_max)
dev->max_mtu = jumbo_max;
+ rc = netif_set_real_num_queues(tp->dev, 1, tp->num_rx_rings);
+ if (rc < 0)
+ return dev_err_probe(&pdev->dev, rc, "set tx/rx num failure\n");
+
rtl_set_irq_mask(tp);
tp->counters = dmam_alloc_coherent (&pdev->dev, sizeof(*tp->counters),
@@ -5792,9 +5873,15 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
if (rc)
return rc;
+ tp->rtl8169_napi = kcalloc(tp->irq_nvecs, sizeof(struct napi_struct), GFP_KERNEL);
+ if (!tp->rtl8169_napi)
+ return -ENOMEM;
+
+ r8169_init_napi(tp);
+
rc = register_netdev(dev);
if (rc)
- return rc;
+ goto err_free_napi;
if (IS_ENABLED(CONFIG_R8169_LEDS)) {
if (rtl_is_8125(tp))
@@ -5803,8 +5890,9 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
tp->leds = rtl8168_init_leds(dev);
}
- netdev_info(dev, "%s, %pM, %sXID %x, IRQ %d\n",
- chip->name, dev->dev_addr, ext_xid_str, xid, tp->irq);
+ netdev_info(dev, "%s, %pM, %sXID %x, IRQ %d (%d total)\n",
+ chip->name, dev->dev_addr, ext_xid_str, xid,
+ pci_irq_vector(pdev, 0), tp->irq_nvecs);
if (jumbo_max)
netdev_info(dev, "jumbo features [frames: %d bytes, tx checksumming: %s]\n",
@@ -5821,6 +5909,10 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
pm_runtime_put_sync(&pdev->dev);
return 0;
+
+err_free_napi:
+ r8169_free_napi(tp);
+ return rc;
}
static struct pci_driver rtl8169_pci_driver = {
--
2.43.0
^ permalink raw reply related
* [PATCH net-next v9 3/7] r8169: add support for new interrupt mapping
From: javen @ 2026-06-29 7:13 UTC (permalink / raw)
To: hkallweit1, nic_swsd, andrew+netdev, davem, edumazet, kuba,
pabeni, horms
Cc: netdev, linux-kernel, Javen Xu
In-Reply-To: <20260629071339.1605-1-javen_xu@realsil.com.cn>
From: Javen Xu <javen_xu@realsil.com.cn>
To support RSS, the number of hardware interrupt bits should match the
interrupt of software. So we add support for new interrupt mapping here.
ISR_VEC_MAP_REG is the hardware register to indicate interrupt status.
IMR_SET_VEC_MAP_REG is interrupt mask which is set to enable irq.
Signed-off-by: Javen Xu <javen_xu@realsil.com.cn>
---
Changes in v2:
- no changes
Changes in v3:
- init index in napi_struct and get message_id from index
- move rtl8169_disable_hw_interrupt_msix directly before the call to
napi_schedule()
- change the condition in rtl8169_request_irq when RTL_VEC_MAP_ENABLE
enabled, use rtl8169_interrupt_msix
Changes in v4:
- remove flag tp->feature, replace tp->features & RTL_VEC_MAP_ENABLE
with tp->irq_nvecs > 1, they are equivalent.
- follow reverse xmas tree, in rtl8169_interrupt_msix(),
rtl8169_poll_msix_rx(), rtl8169_poll_msix_tx(),
rtl8169_poll_msix_other()
- use napi->index in rtl8169_poll_msix_other()
- add a comment to describe RTL8127 MSI-X vector layout
- simplify r8169_init_napi()
Changes in v5:
- replace magic number in rtl8169_poll_msix_tx()
Changes in v6:
- when irq_nvecs <= 1, use register IntrMask_8125, else using vec map
- fix irq sequence in rtl8169_interrupt_msix(), disable interrupts
before clean it
- remove dead code in rtl8169_poll_msix_tx()
Changes in v7:
- remove recheck_desc_ownbit
- change return value of rtl_tx
- remove message_id which only used once
Changes in v8:
- fix rtl8169_netpoll()
- remove tx_done
Changes in v9:
- change the way of getting message_id of napi
---
drivers/net/ethernet/realtek/r8169_main.c | 172 +++++++++++++++++++---
1 file changed, 154 insertions(+), 18 deletions(-)
diff --git a/drivers/net/ethernet/realtek/r8169_main.c b/drivers/net/ethernet/realtek/r8169_main.c
index 13c56dbca230..2fb90dc9026c 100644
--- a/drivers/net/ethernet/realtek/r8169_main.c
+++ b/drivers/net/ethernet/realtek/r8169_main.c
@@ -85,6 +85,7 @@
#define R8169_TX_STOP_THRS (MAX_SKB_FRAGS + 1)
#define R8169_TX_START_THRS (2 * R8169_TX_STOP_THRS)
#define R8169_MAX_RX_QUEUES 8
+#define R8127_MAX_TX_QUEUES 8
#define R8169_DEFAULT_RX_QUEUES 1
#define R8169_MAX_TX_QUEUES 1
@@ -455,8 +456,12 @@ enum rtl8125_registers {
RSS_CTRL_8125 = 0x4500,
Q_NUM_CTRL_8125 = 0x4800,
EEE_TXIDLE_TIMER_8125 = 0x6048,
+ IMR_CLEAR_VEC_MAP_REG = 0x0d00,
+ ISR_VEC_MAP_REG = 0x0d04,
+ IMR_SET_VEC_MAP_REG = 0x0d0c,
};
+#define MSIX_ID_VEC_MAP_LINKCHG 29
#define LEDSEL_MASK_8125 0x23f
#define RX_VLAN_INNER_8125 BIT(22)
@@ -587,6 +592,9 @@ enum rtl_register_content {
/* magic enable v2 */
MagicPacket_v2 = (1 << 16), /* Wake up when receives a Magic Packet */
+#define ISRIMR_LINKCHG BIT(29)
+#define ISRIMR_TOK_Q0 BIT(8)
+#define ISRIMR_ROK_Q0 BIT(0)
};
enum rtl_desc_bit {
@@ -1664,26 +1672,38 @@ static u32 rtl_get_events(struct rtl8169_private *tp)
static void rtl_ack_events(struct rtl8169_private *tp, u32 bits)
{
- if (rtl_is_8125(tp))
- RTL_W32(tp, IntrStatus_8125, bits);
- else
+ if (rtl_is_8125(tp)) {
+ if (tp->irq_nvecs > 1)
+ RTL_W32(tp, ISR_VEC_MAP_REG, bits);
+ else
+ RTL_W32(tp, IntrStatus_8125, bits);
+ } else {
RTL_W16(tp, IntrStatus, bits);
+ }
}
static void rtl_irq_disable(struct rtl8169_private *tp)
{
- if (rtl_is_8125(tp))
- RTL_W32(tp, IntrMask_8125, 0);
- else
+ if (rtl_is_8125(tp)) {
+ if (tp->irq_nvecs > 1)
+ RTL_W32(tp, IMR_CLEAR_VEC_MAP_REG, 0xffffffff);
+ else
+ RTL_W32(tp, IntrMask_8125, 0);
+ } else {
RTL_W16(tp, IntrMask, 0);
+ }
}
static void rtl_irq_enable(struct rtl8169_private *tp)
{
- if (rtl_is_8125(tp))
- RTL_W32(tp, IntrMask_8125, tp->irq_mask);
- else
+ if (rtl_is_8125(tp)) {
+ if (tp->irq_nvecs > 1)
+ RTL_W32(tp, IMR_SET_VEC_MAP_REG, tp->irq_mask);
+ else
+ RTL_W32(tp, IntrMask_8125, tp->irq_mask);
+ } else {
RTL_W16(tp, IntrMask, tp->irq_mask);
+ }
}
static void rtl8169_irq_mask_and_ack(struct rtl8169_private *tp)
@@ -5037,6 +5057,45 @@ static void rtl8169_free_irq(struct rtl8169_private *tp)
}
}
+static void rtl8169_disable_hw_interrupt_msix(struct rtl8169_private *tp, int message_id)
+{
+ RTL_W32(tp, IMR_CLEAR_VEC_MAP_REG, BIT(message_id));
+}
+
+static void rtl8169_clear_hw_isr(struct rtl8169_private *tp, int message_id)
+{
+ RTL_W32(tp, ISR_VEC_MAP_REG, BIT(message_id));
+}
+
+static void rtl8169_enable_hw_interrupt_msix(struct rtl8169_private *tp, int message_id)
+{
+ RTL_W32(tp, IMR_SET_VEC_MAP_REG, BIT(message_id));
+}
+
+static irqreturn_t rtl8169_interrupt_msix(int irq, void *dev_instance)
+{
+ struct napi_struct *napi = dev_instance;
+ struct net_device *dev = napi->dev;
+ struct rtl8169_private *tp;
+ int message_id;
+
+ tp = netdev_priv(dev);
+ message_id = napi - tp->rtl8169_napi;
+
+ if (message_id == MSIX_ID_VEC_MAP_LINKCHG) {
+ rtl8169_clear_hw_isr(tp, message_id);
+ phy_mac_interrupt(tp->phydev);
+ return IRQ_HANDLED;
+ }
+
+ rtl8169_disable_hw_interrupt_msix(tp, message_id);
+ rtl8169_clear_hw_isr(tp, message_id);
+
+ napi_schedule(napi);
+
+ return IRQ_HANDLED;
+}
+
static int rtl8169_request_irq(struct rtl8169_private *tp)
{
struct net_device *dev = tp->dev;
@@ -5045,8 +5104,12 @@ static int rtl8169_request_irq(struct rtl8169_private *tp)
for (i = 0; i < tp->irq_nvecs; i++) {
napi = &tp->rtl8169_napi[i];
- rc = pci_request_irq(tp->pci_dev, i, rtl8169_interrupt,
- NULL, napi, "%s-%d", dev->name, i);
+ if (tp->irq_nvecs > 1)
+ rc = pci_request_irq(tp->pci_dev, i, rtl8169_interrupt_msix,
+ NULL, napi, "%s-%d", dev->name, i);
+ else
+ rc = pci_request_irq(tp->pci_dev, i, rtl8169_interrupt,
+ NULL, napi, "%s-%d", dev->name, i);
if (rc)
goto free_irq;
}
@@ -5253,8 +5316,12 @@ static void rtl8169_netpoll(struct net_device *dev)
struct rtl8169_private *tp = netdev_priv(dev);
for (int i = 0; i < tp->irq_nvecs; i++) {
- rtl8169_interrupt(pci_irq_vector(tp->pci_dev, i),
- &tp->rtl8169_napi[i]);
+ if (tp->irq_nvecs > 1)
+ rtl8169_interrupt_msix(pci_irq_vector(tp->pci_dev, i),
+ &tp->rtl8169_napi[i]);
+ else
+ rtl8169_interrupt(pci_irq_vector(tp->pci_dev, i),
+ &tp->rtl8169_napi[i]);
}
}
#endif
@@ -5505,10 +5572,16 @@ static const struct net_device_ops rtl_netdev_ops = {
static void rtl_set_irq_mask(struct rtl8169_private *tp)
{
- tp->irq_mask = RxOK | RxErr | TxOK | TxErr | LinkChg;
+ if (tp->irq_nvecs > 1) {
+ tp->irq_mask = ISRIMR_LINKCHG | ISRIMR_TOK_Q0;
+ for (int i = 0; i < tp->num_rx_rings; i++)
+ tp->irq_mask |= ISRIMR_ROK_Q0 << i;
+ } else {
+ tp->irq_mask = RxOK | RxErr | TxOK | TxErr | LinkChg;
- if (tp->mac_version <= RTL_GIGA_MAC_VER_06)
- tp->irq_mask |= SYSErr | RxFIFOOver;
+ if (tp->mac_version <= RTL_GIGA_MAC_VER_06)
+ tp->irq_mask |= SYSErr | RxFIFOOver;
+ }
}
static int rtl_alloc_irq(struct rtl8169_private *tp)
@@ -5793,10 +5866,73 @@ static bool rtl_aspm_is_safe(struct rtl8169_private *tp)
return false;
}
+static int rtl8169_poll_msix_rx(struct napi_struct *napi, int budget)
+{
+ struct net_device *dev = napi->dev;
+ struct rtl8169_private *tp;
+ int work_done = 0;
+ int message_id;
+
+ tp = netdev_priv(dev);
+ message_id = napi - tp->rtl8169_napi;
+
+ if (message_id < tp->num_rx_rings)
+ work_done += rtl_rx(dev, tp, &tp->rx_ring[message_id], budget, napi);
+
+ if (work_done < budget && napi_complete_done(napi, work_done))
+ rtl8169_enable_hw_interrupt_msix(tp, message_id);
+
+ return work_done;
+}
+
+static int rtl8169_poll_msix_tx(struct napi_struct *napi, int budget)
+{
+ struct net_device *dev = napi->dev;
+ struct rtl8169_private *tp;
+
+ tp = netdev_priv(dev);
+
+ rtl_tx(dev, tp, budget);
+
+ if (napi_complete_done(napi, 0))
+ rtl8169_enable_hw_interrupt_msix(tp, (int)(napi - tp->rtl8169_napi));
+
+ return 0;
+}
+
+static int rtl8169_poll_msix_other(struct napi_struct *napi, int budget)
+{
+ struct net_device *dev = napi->dev;
+ struct rtl8169_private *tp;
+
+ tp = netdev_priv(dev);
+
+ if (napi_complete_done(napi, 0))
+ rtl8169_enable_hw_interrupt_msix(tp, (int)(napi - tp->rtl8169_napi));
+
+ return 0;
+}
+
+/* RTL8127 MSI-X vector layout:
+ * Vectors 0 .. (RxQs - 1) : Rx Queues
+ * Vectors RxQs .. (RxQs + TxQs - 1) : Tx Queues
+ * Vector (RxQs + TxQs) and up : Other events (Link status(29), etc.)
+ */
static void r8169_init_napi(struct rtl8169_private *tp)
{
- for (int i = 0; i < tp->irq_nvecs; i++)
- netif_napi_add(tp->dev, &tp->rtl8169_napi[i], rtl8169_poll);
+ for (int i = 0; i < tp->irq_nvecs; i++) {
+ int (*poll_fn)(struct napi_struct *, int) = rtl8169_poll;
+
+ if (tp->irq_nvecs > 1) {
+ if (i < R8169_MAX_RX_QUEUES)
+ poll_fn = rtl8169_poll_msix_rx;
+ else if (i < R8169_MAX_RX_QUEUES + R8127_MAX_TX_QUEUES)
+ poll_fn = rtl8169_poll_msix_tx;
+ else
+ poll_fn = rtl8169_poll_msix_other;
+ }
+ netif_napi_add(tp->dev, &tp->rtl8169_napi[i], poll_fn);
+ }
}
static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
--
2.43.0
^ permalink raw reply related
* [PATCH net-next v9 7/7] r8169: support setting rx queue numbers via ethtool
From: javen @ 2026-06-29 7:13 UTC (permalink / raw)
To: hkallweit1, nic_swsd, andrew+netdev, davem, edumazet, kuba,
pabeni, horms
Cc: netdev, linux-kernel, Javen Xu
In-Reply-To: <20260629071339.1605-1-javen_xu@realsil.com.cn>
From: Javen Xu <javen_xu@realsil.com.cn>
This patch add support for changing rx queues by ethtool. We can set rx
1, 2, 4, 8 by ethtool -L eth1 rx num.
Signed-off-by: Javen Xu <javen_xu@realsil.com.cn>
---
Changes in v2:
- no changes
Changes in v3:
- no changes
Changes in v4:
- remove rss_support and rss_enable
- remove some zero-initialized
- use kzalloc_objs instead of kcalloc
Changes in v5:
- no changes
Changes in v6:
- change subject of this patch
- defer the assignment of tp->init_rx_desc_type until after
rtl8169_down()
- call netif_set_real_num_rx_queues() to synchronize the new rx queue
number with networking core
Changes in v7:
- no changes
Changes in v8:
- if dev is not running, updating tp->rss_data->rss_indir_tbl and
calling netif_set_real_num_rx_queue when change rx queue number
- if system does not provide enough irq_nvecs, return -EINVAL
- malloc new_rx before device down
Changes in v9:
- pass rx_desc_type to rtl8169_rx_clear
- return netdev_err when netif_set_real_num_rx_queues fail
---
drivers/net/ethernet/realtek/r8169_main.c | 161 ++++++++++++++++++++--
1 file changed, 153 insertions(+), 8 deletions(-)
diff --git a/drivers/net/ethernet/realtek/r8169_main.c b/drivers/net/ethernet/realtek/r8169_main.c
index 06b3782ccbb4..9194c2142f95 100644
--- a/drivers/net/ethernet/realtek/r8169_main.c
+++ b/drivers/net/ethernet/realtek/r8169_main.c
@@ -2789,11 +2789,16 @@ static void rtl_hw_reset(struct rtl8169_private *tp)
rtl_loop_wait_low(tp, &rtl_chipcmd_cond, 100, 100);
}
-static void rtl8169_init_rss(struct rtl8169_private *tp)
+static void rtl8169_set_rss_indir_tbl(struct rtl8169_private *tp,
+ unsigned int num_rx_rings)
{
for (int i = 0; i < tp->rss_data->hw_supp_indir_tbl_entries; i++)
- tp->rss_data->rss_indir_tbl[i] = ethtool_rxfh_indir_default(i, tp->num_rx_rings);
+ tp->rss_data->rss_indir_tbl[i] = ethtool_rxfh_indir_default(i, num_rx_rings);
+}
+static void rtl8169_init_rss(struct rtl8169_private *tp)
+{
+ rtl8169_set_rss_indir_tbl(tp, tp->num_rx_rings);
netdev_rss_key_fill(tp->rss_data->rss_key, RTL_RSS_KEY_SIZE);
}
@@ -4432,7 +4437,9 @@ static struct page *rtl8169_alloc_rx_data(struct rtl8169_private *tp,
return data;
}
-static void rtl8169_rx_clear(struct rtl8169_private *tp, struct rtl8169_rx_ring *ring)
+static void rtl8169_rx_clear(struct rtl8169_private *tp,
+ struct rtl8169_rx_ring *ring,
+ enum rx_desc_type desc_type)
{
int i;
@@ -4443,7 +4450,7 @@ static void rtl8169_rx_clear(struct rtl8169_private *tp, struct rtl8169_rx_ring
__free_pages(ring->rx_databuff[i], get_order(R8169_RX_BUF_SIZE));
ring->rx_databuff[i] = NULL;
ring->rx_desc_phy_addr[i] = 0;
- if (tp->init_rx_desc_type == RX_DESC_TYPE_RSS) {
+ if (desc_type == RX_DESC_TYPE_RSS) {
ring->rx_desc_array[i].rss_addr = 0;
ring->rx_desc_array[i].rss_opts1 = 0;
} else {
@@ -4474,7 +4481,7 @@ static int rtl8169_rx_fill(struct rtl8169_private *tp, struct rtl8169_rx_ring *r
data = rtl8169_alloc_rx_data(tp, ring, i);
if (!data) {
- rtl8169_rx_clear(tp, ring);
+ rtl8169_rx_clear(tp, ring, tp->init_rx_desc_type);
return -ENOMEM;
}
ring->rx_databuff[i] = data;
@@ -4541,7 +4548,7 @@ static int rtl8169_init_ring(struct rtl8169_private *tp)
err_clear:
while (--i >= 0)
- rtl8169_rx_clear(tp, &tp->rx_ring[i]);
+ rtl8169_rx_clear(tp, &tp->rx_ring[i], tp->init_rx_desc_type);
return ret;
}
@@ -5545,7 +5552,7 @@ static int rtl8169_close(struct net_device *dev)
netif_stop_queue(dev);
rtl8169_down(tp);
for (int i = 0; i < tp->num_rx_rings; i++)
- rtl8169_rx_clear(tp, &tp->rx_ring[i]);
+ rtl8169_rx_clear(tp, &tp->rx_ring[i], tp->init_rx_desc_type);
rtl8169_free_irq(tp);
@@ -5624,7 +5631,7 @@ static int rtl_open(struct net_device *dev)
err_release_fw_2:
rtl_release_firmware(tp);
for (int i = 0; i < tp->num_rx_rings; i++)
- rtl8169_rx_clear(tp, &tp->rx_ring[i]);
+ rtl8169_rx_clear(tp, &tp->rx_ring[i], tp->init_rx_desc_type);
err_free_rx_1:
rtl8169_free_rx_desc(tp);
dma_free_coherent(&pdev->dev, R8169_TX_RING_BYTES, tp->TxDescArray,
@@ -6215,6 +6222,142 @@ static void r8169_init_napi(struct rtl8169_private *tp)
}
}
+static void rtl8169_get_channels(struct net_device *dev,
+ struct ethtool_channels *ch)
+{
+ struct rtl8169_private *tp = netdev_priv(dev);
+
+ ch->max_rx = tp->hw_supp_num_rx_queues;
+ ch->max_tx = 1;
+
+ ch->rx_count = tp->num_rx_rings;
+ ch->tx_count = 1;
+}
+
+static int rtl8169_realloc_rx(struct rtl8169_private *tp,
+ struct rtl8169_rx_ring *new_rx,
+ int new_count)
+{
+ int i, ret;
+
+ for (i = 0; i < new_count; i++) {
+ struct rtl8169_rx_ring *ring = &new_rx[i];
+
+ ring->rx_desc_array = dma_alloc_coherent(&tp->pci_dev->dev,
+ R8169_RX_RING_BYTES,
+ &ring->rx_phy_addr,
+ GFP_KERNEL);
+ if (!ring->rx_desc_array) {
+ ret = -ENOMEM;
+ goto err_free;
+ }
+
+ memset(ring->rx_databuff, 0, sizeof(ring->rx_databuff));
+ ret = rtl8169_rx_fill(tp, ring);
+ if (ret) {
+ dma_free_coherent(&tp->pci_dev->dev, R8169_RX_RING_BYTES,
+ ring->rx_desc_array, ring->rx_phy_addr);
+ goto err_free;
+ }
+ }
+ return 0;
+
+err_free:
+ while (--i >= 0) {
+ rtl8169_rx_clear(tp, &new_rx[i], tp->init_rx_desc_type);
+ dma_free_coherent(&tp->pci_dev->dev, R8169_RX_RING_BYTES,
+ new_rx[i].rx_desc_array, new_rx[i].rx_phy_addr);
+ }
+ return ret;
+}
+
+static int rtl8169_set_channels(struct net_device *dev,
+ struct ethtool_channels *ch)
+{
+ struct rtl8169_private *tp = netdev_priv(dev);
+ bool if_running = netif_running(dev);
+ enum rx_desc_type old_rx_desc_type;
+ enum rx_desc_type new_desc_type;
+ struct rtl8169_rx_ring *new_rx;
+ int i, ret;
+
+ if (ch->rx_count == tp->num_rx_rings)
+ return 0;
+
+ old_rx_desc_type = tp->init_rx_desc_type;
+
+ if (!rtl_hw_support_rss(tp)) {
+ netdev_warn(dev, "This chip does not support multiple channels/RSS.\n");
+ return -EOPNOTSUPP;
+ }
+
+ if (ch->rx_count > tp->hw_supp_num_rx_queues || !is_power_of_2(ch->rx_count) ||
+ tp->irq_nvecs < get_min_irq_nvecs(tp))
+ return -EINVAL;
+
+ new_desc_type = ch->rx_count > 1 ? RX_DESC_TYPE_RSS : RX_DESC_TYPE_DEFAULT;
+
+ if (!if_running) {
+ ret = netif_set_real_num_rx_queues(dev, ch->rx_count);
+ if (ret)
+ return ret;
+
+ tp->num_rx_rings = ch->rx_count;
+ tp->init_rx_desc_type = new_desc_type;
+
+ rtl8169_set_rss_indir_tbl(tp, tp->num_rx_rings);
+ rtl_set_irq_mask(tp);
+ return 0;
+ }
+
+ new_rx = kzalloc_objs(*new_rx, R8169_MAX_RX_QUEUES);
+ if (!new_rx)
+ return -ENOMEM;
+
+ netif_stop_queue(dev);
+ rtl8169_down(tp);
+
+ ret = netif_set_real_num_rx_queues(dev, ch->rx_count);
+ if (ret)
+ goto err_up;
+
+ tp->init_rx_desc_type = new_desc_type;
+
+ ret = rtl8169_realloc_rx(tp, new_rx, ch->rx_count);
+ if (ret)
+ goto err_reset;
+
+ for (i = 0; i < tp->num_rx_rings; i++)
+ rtl8169_rx_clear(tp, &tp->rx_ring[i], old_rx_desc_type);
+ rtl8169_free_rx_desc(tp);
+
+ tp->num_rx_rings = ch->rx_count;
+
+ memset(tp->rx_ring, 0, sizeof(tp->rx_ring));
+ memcpy(tp->rx_ring, new_rx, sizeof(*new_rx) * ch->rx_count);
+
+ rtl8169_set_rss_indir_tbl(tp, tp->num_rx_rings);
+ rtl_set_irq_mask(tp);
+
+ rtl8169_up(tp);
+ netif_start_queue(dev);
+
+ kfree(new_rx);
+
+ return 0;
+
+err_reset:
+ if (netif_set_real_num_rx_queues(dev, tp->num_rx_rings))
+ netdev_err(dev, "Failed to revert rx_queues, state might be inconsistent!\n");
+ tp->init_rx_desc_type = old_rx_desc_type;
+err_up:
+ rtl8169_up(tp);
+ netif_start_queue(dev);
+ kfree(new_rx);
+
+ return ret;
+}
+
static const struct ethtool_ops rtl8169_ethtool_ops = {
.supported_coalesce_params = ETHTOOL_COALESCE_USECS |
ETHTOOL_COALESCE_MAX_FRAMES,
@@ -6233,6 +6376,8 @@ static const struct ethtool_ops rtl8169_ethtool_ops = {
.nway_reset = phy_ethtool_nway_reset,
.get_eee = rtl8169_get_eee,
.set_eee = rtl8169_set_eee,
+ .get_channels = rtl8169_get_channels,
+ .set_channels = rtl8169_set_channels,
.get_link_ksettings = phy_ethtool_get_link_ksettings,
.set_link_ksettings = rtl8169_set_link_ksettings,
.get_ringparam = rtl8169_get_ringparam,
--
2.43.0
^ permalink raw reply related
* [PATCH net-next v9 4/7] r8169: enable new interrupt mapping
From: javen @ 2026-06-29 7:13 UTC (permalink / raw)
To: hkallweit1, nic_swsd, andrew+netdev, davem, edumazet, kuba,
pabeni, horms
Cc: netdev, linux-kernel, Javen Xu
In-Reply-To: <20260629071339.1605-1-javen_xu@realsil.com.cn>
From: Javen Xu <javen_xu@realsil.com.cn>
This patch enables new interrupt mapping for RTL8127.
Signed-off-by: Javen Xu <javen_xu@realsil.com.cn>
---
Changes in v2:
- no changes
Changes in v3:
- no changes
Changes in v4:
- no changes
Changes in v5:
- no changes
Changes in v6:
- no changes
Changes in v7:
- no changes
Changes in v8:
- no changes
Changes in v9:
- no changes
---
drivers/net/ethernet/realtek/r8169_main.c | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/drivers/net/ethernet/realtek/r8169_main.c b/drivers/net/ethernet/realtek/r8169_main.c
index 2fb90dc9026c..e1a61cd4070e 100644
--- a/drivers/net/ethernet/realtek/r8169_main.c
+++ b/drivers/net/ethernet/realtek/r8169_main.c
@@ -3939,6 +3939,15 @@ DECLARE_RTL_COND(rtl_mac_ocp_e00e_cond)
return r8168_mac_ocp_read(tp, 0xe00e) & BIT(13);
}
+static void rtl8169_hw_enable_vec_mapping(struct rtl8169_private *tp)
+{
+ u8 tmp;
+
+ tmp = RTL_R8(tp, INT_CFG0_8125);
+ tmp |= INT_CFG0_ENABLE_8125;
+ RTL_W8(tp, INT_CFG0_8125, tmp);
+}
+
static void rtl_hw_start_8125_common(struct rtl8169_private *tp)
{
rtl_pcie_state_l2l3_disable(tp);
@@ -3947,6 +3956,9 @@ static void rtl_hw_start_8125_common(struct rtl8169_private *tp)
RTL_W32(tp, RSS_CTRL_8125, 0);
RTL_W16(tp, Q_NUM_CTRL_8125, 0);
+ if (tp->irq_nvecs > 1)
+ rtl8169_hw_enable_vec_mapping(tp);
+
/* disable UPS */
r8168_mac_ocp_modify(tp, 0xd40a, 0x0010, 0x0000);
--
2.43.0
^ permalink raw reply related
* [PATCH net-next v9 0/7] r8169: add RSS support for RTL8127
From: javen @ 2026-06-29 7:13 UTC (permalink / raw)
To: hkallweit1, nic_swsd, andrew+netdev, davem, edumazet, kuba,
pabeni, horms
Cc: netdev, linux-kernel, Javen Xu
From: Javen Xu <javen_xu@realsil.com.cn>
This patch series adds RSS (Receive Side Scaling) support for the r8169
ethernet driver, specifically for RTL8127 (RTL_GIGA_MAC_VER_80).
RSS enables packet distribution across multiple receive queues, which can
significantly improve network throughput on multi-core systems by allowing
parallel processing of incoming packets.
Key features:
- Multi-queue RX support (up to 8 queues)
- MSI-X interrupt with vector mapping
- Dynamic queue configuration via ethtool (-L)
- RSS hash computation for flow classification
Experiments:
Platform: AMD Ryzen Embedded R2514 with Radeon Graphics(4 Cores/8 Threads)
Arch: x86_64
Test command:
Server: iperf3 -s
Client: iperf3 -c 192.168.2.1 -P 20 -t 3600
Monitor: mpstat -P ALL 1
Before this patch (Without RSS):
Throughput: Unstable, fluctuating between 3.76 Gbits/sec and
8.2 Gbits/sec.
CPU Usage: A single CPU core is fully occupied with softirq reaching
up to 96%.
After this patch (With RSS enabled):
Throughput: Stable at 9.42 Gbits/sec.
CPU Usage: The traffic load is evenly distributed across multiple CPU
cores. The maximum softirq on a single core dropped to 63%.
Other Experiments:
Link: https://lore.kernel.org/netdev/0A5279953D81BB9C+f50c9b49-3e5d-467f-b69a-7e49ed223383@radxa.com/
Javen Xu (7):
r8169: add support for multi irqs
r8169: refactor RX path to prepare for multi-queue
r8169: add support for new interrupt mapping
r8169: enable new interrupt mapping
r8169: add support and enable rss
r8169: move struct ethtool_ops
r8169: support setting rx queue numbers via ethtool
drivers/net/ethernet/realtek/r8169_main.c | 1101 ++++++++++++++++++---
1 file changed, 961 insertions(+), 140 deletions(-)
--
2.43.0
^ permalink raw reply
* [PATCH net-next v9 5/7] r8169: add support and enable rss
From: javen @ 2026-06-29 7:13 UTC (permalink / raw)
To: hkallweit1, nic_swsd, andrew+netdev, davem, edumazet, kuba,
pabeni, horms
Cc: netdev, linux-kernel, Javen Xu
In-Reply-To: <20260629071339.1605-1-javen_xu@realsil.com.cn>
From: Javen Xu <javen_xu@realsil.com.cn>
This patch adds support and enable rss for RTL8127.
Signed-off-by: Javen Xu <javen_xu@realsil.com.cn>
---
Changes in v2:
- some changes moved from Patch 2/7
Changes in v3:
- add struct rtl8169_rss_data. Allocate it dynamically when needed.
- define rss_key as an u32 array
- replace some magic bit numbers in rtl8169_set_rss_hash_opt() and
rtl8125_set_rx_q_num()
- use union to combine different rx descriptor, refactor struct RxDesc
- remove dead code from rtl8169_double_check_rss_support()
Changes in v4:
- rename macro definition, e.g R8127_MAX_IRQ to R8127_MAX_NUM_IRQVEC
- change hw_supp_indir_tbl_entries type to unsigned int
- change init_rx_desc_type type to enum
- remove rtl_check_rss_support(), add helper function
rtl_hw_support_rss()
- remove hw_curr_isr_ver, use irq_nvecs to judge whether we should
enable vector interrupt mapping, use tp->num_rx_ring to judge whether
we should enable rss
- remove function rtl8169_double_check_rss_support(), use
rtl8169_set_rx_ring_num() to set num_rx_ring according to tp->irq_nvecs
Changes in v5:
- no changes
Changes in v6:
- change rss_queue_num type from u8 to unsigned int
- fix rx desc clear in rtl8169_rx_clear() for different desc type
- clamping num_rx_ring with rounddown_pow_of_two()
Changes in v7:
- remove unused macro
- change unfixed type in rtl8169_store_reta
Changes in v8:
- refill desc->addr when rx_desc reset
- rtl8169_set_channels fixed in patch 7/7
Changes in v9:
- remove rtl8169_set_desc_dma_addr, only set desc dma addr for
RX_DESC_TYPE_RSS desc
---
drivers/net/ethernet/realtek/r8169_main.c | 378 ++++++++++++++++++++--
1 file changed, 345 insertions(+), 33 deletions(-)
diff --git a/drivers/net/ethernet/realtek/r8169_main.c b/drivers/net/ethernet/realtek/r8169_main.c
index e1a61cd4070e..9163cb31cebb 100644
--- a/drivers/net/ethernet/realtek/r8169_main.c
+++ b/drivers/net/ethernet/realtek/r8169_main.c
@@ -88,6 +88,19 @@
#define R8127_MAX_TX_QUEUES 8
#define R8169_DEFAULT_RX_QUEUES 1
#define R8169_MAX_TX_QUEUES 1
+#define R8127_MAX_NUM_IRQVEC 32
+#define R8127_MIN_NUM_IRQVEC 30
+#define R8169_IRQ_DEFAULT 1
+#define RTL_RSS_KEY_SIZE 40
+#define RSS_CPU_NUM_MASK GENMASK(18, 16)
+#define RSS_HASH_MASK GENMASK(10, 8)
+#define RTL_MAX_INDIRECTION_TABLE_ENTRIES 128
+#define RXS_RSS_UDP BIT(27)
+#define RXS_RSS_IPV4 BIT(28)
+#define RXS_RSS_IPV6 BIT(29)
+#define RXS_RSS_TCP BIT(30)
+#define RXS_RSS_L3_TYPE_MASK (RXS_RSS_IPV4 | RXS_RSS_IPV6)
+#define RXS_RSS_L4_TYPE_MASK (RXS_RSS_TCP | RXS_RSS_UDP)
#define OCP_STD_PHY_BASE 0xa400
@@ -595,6 +608,20 @@ enum rtl_register_content {
#define ISRIMR_LINKCHG BIT(29)
#define ISRIMR_TOK_Q0 BIT(8)
#define ISRIMR_ROK_Q0 BIT(0)
+#define RTL_DESC_TYPE_CTRL 0xd8
+#define RSS_KEY_REG 0x4600
+#define RSS_INDIRECTION_TBL_REG 0x4700
+#define RSS_CTRL_TCP_IPV4_SUPP BIT(0)
+#define RTL_DESC_TYPE_RSS BIT(1)
+#define RSS_CTRL_IPV4_SUPP BIT(1)
+#define RSS_CTRL_TCP_IPV6_SUPP BIT(2)
+#define RSS_CTRL_IPV6_SUPP BIT(3)
+#define RSS_CTRL_IPV6_EXT_SUPP BIT(4)
+#define RSS_CTRL_TCP_IPV6_EXT_SUPP BIT(5)
+#define RX_RES_RSS BIT(22)
+#define RX_RUNT_RSS BIT(21)
+#define RX_CRC_RSS BIT(20)
+#define RTL_RX_Q_NUM_MASK GENMASK(4, 2)
};
enum rtl_desc_bit {
@@ -652,6 +679,11 @@ enum rtl_rx_desc_bit {
#define RxProtoIP (PID1 | PID0)
#define RxProtoMask RxProtoIP
+#define RX_UDPT_DESC_RSS BIT(19)
+#define RX_TCPT_DESC_RSS BIT(18)
+#define RX_UDPF_DESC_RSS BIT(16) /* UDP/IP checksum failed */
+#define RX_TCPF_DESC_RSS BIT(15) /* TCP/IP checksum failed */
+
IPFail = (1 << 16), /* IP checksum failed */
UDPFail = (1 << 15), /* UDP/IP checksum failed */
TCPFail = (1 << 14), /* TCP/IP checksum failed */
@@ -673,9 +705,27 @@ struct TxDesc {
};
struct RxDesc {
- __le32 opts1;
- __le32 opts2;
- __le64 addr;
+ union {
+ /* RX_DESC_TYPE_DEFAULT */
+ struct {
+ __le32 opts1;
+ __le32 opts2;
+ __le64 addr;
+ };
+
+ /* RX_DESC_TYPE_RSS */
+ struct {
+ union {
+ __le64 rss_addr;
+ struct {
+ __le32 rss_info;
+ __le32 rss_result;
+ } rss_dword;
+ };
+ __le32 rss_opts2;
+ __le32 rss_opts1;
+ };
+ };
};
struct ring_info {
@@ -747,6 +797,11 @@ enum rtl_dash_type {
RTL_DASH_25_BP,
};
+enum rx_desc_type {
+ RX_DESC_TYPE_DEFAULT,
+ RX_DESC_TYPE_RSS,
+};
+
struct rtl8169_rx_ring {
u32 index; /* Rx queue index */
u32 cur_rx; /* Index of next Rx pkt. */
@@ -757,6 +812,12 @@ struct rtl8169_rx_ring {
struct page *rx_databuff[NUM_RX_DESC]; /* Rx data buffers */
};
+struct rtl8169_rss_data {
+ u32 rss_key[RTL_RSS_KEY_SIZE / sizeof(u32)];
+ u8 rss_indir_tbl[RTL_MAX_INDIRECTION_TABLE_ENTRIES];
+ unsigned int hw_supp_indir_tbl_entries;
+};
+
struct rtl8169_private {
void __iomem *mmio_addr; /* memory map physical address */
struct pci_dev *pci_dev;
@@ -776,7 +837,9 @@ struct rtl8169_private {
u16 tx_lpi_timer;
u32 irq_mask;
unsigned int hw_supp_num_rx_queues;
+ struct rtl8169_rss_data *rss_data;
unsigned int irq_nvecs;
+ enum rx_desc_type init_rx_desc_type;
struct clk *clk;
struct {
@@ -1606,6 +1669,11 @@ static bool rtl_dash_is_enabled(struct rtl8169_private *tp)
}
}
+static bool rtl_hw_support_rss(struct rtl8169_private *tp)
+{
+ return tp->mac_version == RTL_GIGA_MAC_VER_80;
+}
+
static enum rtl_dash_type rtl_get_dash_type(struct rtl8169_private *tp)
{
switch (tp->mac_version) {
@@ -1907,9 +1975,20 @@ static inline u32 rtl8169_tx_vlan_tag(struct sk_buff *skb)
TxVlanTag | swab16(skb_vlan_tag_get(skb)) : 0x00;
}
-static void rtl8169_rx_vlan_tag(struct RxDesc *desc, struct sk_buff *skb)
+static void rtl8169_rx_vlan_tag(struct rtl8169_private *tp,
+ struct RxDesc *desc,
+ struct sk_buff *skb)
{
- u32 opts2 = le32_to_cpu(desc->opts2);
+ u32 opts2;
+
+ switch (tp->init_rx_desc_type) {
+ case RX_DESC_TYPE_RSS:
+ opts2 = le32_to_cpu(desc->rss_opts2);
+ break;
+ default:
+ opts2 = le32_to_cpu(desc->opts2);
+ break;
+ }
if (opts2 & RxVlanTag)
__vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), swab16(opts2 & 0xffff));
@@ -2738,17 +2817,27 @@ static void rtl_hw_reset(struct rtl8169_private *tp)
rtl_loop_wait_low(tp, &rtl_chipcmd_cond, 100, 100);
}
+static void rtl8169_init_rss(struct rtl8169_private *tp)
+{
+ for (int i = 0; i < tp->rss_data->hw_supp_indir_tbl_entries; i++)
+ tp->rss_data->rss_indir_tbl[i] = ethtool_rxfh_indir_default(i, tp->num_rx_rings);
+
+ netdev_rss_key_fill(tp->rss_data->rss_key, RTL_RSS_KEY_SIZE);
+}
+
static void rtl_setup_rx_params(struct rtl8169_private *tp)
{
tp->num_rx_rings = 1;
switch (tp->mac_version) {
case RTL_GIGA_MAC_VER_80:
tp->hw_supp_num_rx_queues = R8169_MAX_RX_QUEUES;
+ tp->rss_data->hw_supp_indir_tbl_entries = RTL_MAX_INDIRECTION_TABLE_ENTRIES;
break;
default:
tp->hw_supp_num_rx_queues = R8169_DEFAULT_RX_QUEUES;
break;
}
+ tp->init_rx_desc_type = RX_DESC_TYPE_DEFAULT;
}
static void rtl_request_firmware(struct rtl8169_private *tp)
@@ -2873,6 +2962,59 @@ static void rtl_set_rx_max_size(struct rtl8169_private *tp)
RTL_W16(tp, RxMaxSize, R8169_RX_BUF_SIZE + 1);
}
+static void rtl8169_store_rss_key(struct rtl8169_private *tp)
+{
+ u32 num_entries = RTL_RSS_KEY_SIZE / sizeof(u32);
+ u32 *rss_key = tp->rss_data->rss_key;
+ const u16 rss_key_reg = RSS_KEY_REG;
+
+ /* Write redirection table to HW */
+ for (int i = 0; i < num_entries; i++)
+ RTL_W32(tp, rss_key_reg + (i * 4), rss_key[i]);
+}
+
+static void rtl8169_store_reta(struct rtl8169_private *tp)
+{
+ u8 *indir_tbl = tp->rss_data->rss_indir_tbl;
+ unsigned int i;
+
+ /* Write redirection table to HW */
+ for (i = 0; i < tp->rss_data->hw_supp_indir_tbl_entries; i += 4) {
+ u32 reta = (u32)indir_tbl[i] |
+ (u32)indir_tbl[i + 1] << 8 |
+ (u32)indir_tbl[i + 2] << 16 |
+ (u32)indir_tbl[i + 3] << 24;
+ RTL_W32(tp, RSS_INDIRECTION_TBL_REG + i, reta);
+ }
+}
+
+static void rtl8169_set_rss_hash_opt(struct rtl8169_private *tp)
+{
+ u32 rss_ctrl;
+
+ rss_ctrl = FIELD_PREP(RSS_CPU_NUM_MASK, ilog2(tp->num_rx_rings));
+
+ /* Perform hash on these packet types */
+ rss_ctrl |= RSS_CTRL_TCP_IPV4_SUPP
+ | RSS_CTRL_IPV4_SUPP
+ | RSS_CTRL_IPV6_SUPP
+ | RSS_CTRL_IPV6_EXT_SUPP
+ | RSS_CTRL_TCP_IPV6_SUPP
+ | RSS_CTRL_TCP_IPV6_EXT_SUPP;
+
+ rss_ctrl |= FIELD_PREP(RSS_HASH_MASK,
+ ilog2(tp->rss_data->hw_supp_indir_tbl_entries));
+
+ RTL_W32(tp, RSS_CTRL_8125, rss_ctrl);
+}
+
+static void rtl_set_rss_config(struct rtl8169_private *tp)
+{
+ rtl8169_set_rss_hash_opt(tp);
+ rtl8169_store_reta(tp);
+ rtl8169_store_rss_key(tp);
+}
+
static void rtl_set_rx_tx_desc_registers(struct rtl8169_private *tp)
{
struct rtl8169_rx_ring *ring = &tp->rx_ring[0];
@@ -3939,6 +4081,18 @@ DECLARE_RTL_COND(rtl_mac_ocp_e00e_cond)
return r8168_mac_ocp_read(tp, 0xe00e) & BIT(13);
}
+static void rtl8125_set_rx_q_num(struct rtl8169_private *tp)
+{
+ u16 rx_q_num;
+ u16 q_ctrl;
+
+ rx_q_num = ilog2(tp->num_rx_rings);
+ q_ctrl = RTL_R16(tp, Q_NUM_CTRL_8125);
+ q_ctrl &= ~RTL_RX_Q_NUM_MASK;
+ q_ctrl |= FIELD_PREP(RTL_RX_Q_NUM_MASK, rx_q_num);
+ RTL_W16(tp, Q_NUM_CTRL_8125, q_ctrl);
+}
+
static void rtl8169_hw_enable_vec_mapping(struct rtl8169_private *tp)
{
u8 tmp;
@@ -3978,6 +4132,13 @@ static void rtl_hw_start_8125_common(struct rtl8169_private *tp)
tp->mac_version == RTL_GIGA_MAC_VER_80)
RTL_W8(tp, 0xD8, RTL_R8(tp, 0xD8) & ~0x02);
+ /* enable rx descriptor type v4 and set queue num for rss*/
+ if (tp->num_rx_rings > 1) {
+ rtl8125_set_rx_q_num(tp);
+ RTL_W8(tp, RTL_DESC_TYPE_CTRL,
+ RTL_R8(tp, RTL_DESC_TYPE_CTRL) | RTL_DESC_TYPE_RSS);
+ }
+
if (tp->mac_version == RTL_GIGA_MAC_VER_80)
r8168_mac_ocp_modify(tp, 0xe614, 0x0f00, 0x0f00);
else if (tp->mac_version == RTL_GIGA_MAC_VER_70)
@@ -4214,6 +4375,12 @@ static void rtl_hw_start(struct rtl8169_private *tp)
rtl_hw_aspm_clkreq_enable(tp, true);
rtl_set_rx_max_size(tp);
rtl_set_rx_tx_desc_registers(tp);
+ if (rtl_is_8125(tp)) {
+ if (tp->num_rx_rings > 1)
+ rtl_set_rss_config(tp);
+ else
+ RTL_W32(tp, RSS_CTRL_8125, 0x00);
+ }
rtl_lock_config_regs(tp);
rtl_jumbo_config(tp);
@@ -4241,14 +4408,26 @@ static int rtl8169_change_mtu(struct net_device *dev, int new_mtu)
return 0;
}
-static void rtl8169_mark_to_asic(struct RxDesc *desc)
+static void rtl8169_mark_to_asic(struct rtl8169_private *tp, struct RxDesc *desc)
{
- u32 eor = le32_to_cpu(desc->opts1) & RingEnd;
+ u32 eor;
- desc->opts2 = 0;
- /* Force memory writes to complete before releasing descriptor */
- dma_wmb();
- WRITE_ONCE(desc->opts1, cpu_to_le32(DescOwn | eor | R8169_RX_BUF_SIZE));
+ switch (tp->init_rx_desc_type) {
+ case RX_DESC_TYPE_RSS:
+ eor = le32_to_cpu(desc->rss_opts1) & RingEnd;
+ desc->rss_opts2 = cpu_to_le32(0);
+ /* Force memory writes to complete before releasing descriptor */
+ dma_wmb();
+ WRITE_ONCE(desc->rss_opts1, cpu_to_le32(DescOwn | eor | R8169_RX_BUF_SIZE));
+ break;
+ default:
+ eor = le32_to_cpu(desc->opts1) & RingEnd;
+ desc->opts2 = cpu_to_le32(0);
+ /* Force memory writes to complete before releasing descriptor */
+ dma_wmb();
+ WRITE_ONCE(desc->opts1, cpu_to_le32(DescOwn | eor | R8169_RX_BUF_SIZE));
+ break;
+ }
}
static struct page *rtl8169_alloc_rx_data(struct rtl8169_private *tp,
@@ -4271,9 +4450,12 @@ static struct page *rtl8169_alloc_rx_data(struct rtl8169_private *tp,
return NULL;
}
- desc->addr = cpu_to_le64(mapping);
ring->rx_desc_phy_addr[index] = mapping;
- rtl8169_mark_to_asic(desc);
+ if (tp->init_rx_desc_type == RX_DESC_TYPE_RSS)
+ desc->rss_addr = cpu_to_le64(mapping);
+ else
+ desc->addr = cpu_to_le64(mapping);
+ rtl8169_mark_to_asic(tp, desc);
return data;
}
@@ -4289,8 +4471,25 @@ static void rtl8169_rx_clear(struct rtl8169_private *tp, struct rtl8169_rx_ring
__free_pages(ring->rx_databuff[i], get_order(R8169_RX_BUF_SIZE));
ring->rx_databuff[i] = NULL;
ring->rx_desc_phy_addr[i] = 0;
- ring->rx_desc_array[i].addr = 0;
- ring->rx_desc_array[i].opts1 = 0;
+ if (tp->init_rx_desc_type == RX_DESC_TYPE_RSS) {
+ ring->rx_desc_array[i].rss_addr = 0;
+ ring->rx_desc_array[i].rss_opts1 = 0;
+ } else {
+ ring->rx_desc_array[i].addr = 0;
+ ring->rx_desc_array[i].opts1 = 0;
+ }
+ }
+}
+
+static void rtl8169_mark_as_last_descriptor(struct rtl8169_private *tp, struct RxDesc *desc)
+{
+ switch (tp->init_rx_desc_type) {
+ case RX_DESC_TYPE_RSS:
+ desc->rss_opts1 |= cpu_to_le32(RingEnd);
+ break;
+ default:
+ desc->opts1 |= cpu_to_le32(RingEnd);
+ break;
}
}
@@ -4310,7 +4509,7 @@ static int rtl8169_rx_fill(struct rtl8169_private *tp, struct rtl8169_rx_ring *r
}
/* mark as last descriptor in the ring */
- ring->rx_desc_array[NUM_RX_DESC - 1].opts1 |= cpu_to_le32(RingEnd);
+ rtl8169_mark_as_last_descriptor(tp, &ring->rx_desc_array[NUM_RX_DESC - 1]);
return 0;
}
@@ -4465,8 +4664,13 @@ static void rtl8169_rx_desc_reset(struct rtl8169_private *tp)
for (int i = 0; i < tp->num_rx_rings; i++) {
struct rtl8169_rx_ring *ring = &tp->rx_ring[i];
- for (int j = 0; j < NUM_RX_DESC; j++)
- rtl8169_mark_to_asic(ring->rx_desc_array + j);
+ for (int j = 0; j < NUM_RX_DESC; j++) {
+ dma_addr_t phy_addr = ring->rx_desc_phy_addr[j];
+
+ if (tp->init_rx_desc_type == RX_DESC_TYPE_RSS)
+ ring->rx_desc_array[j].rss_addr = cpu_to_le64(phy_addr);
+ rtl8169_mark_to_asic(tp, ring->rx_desc_array + j);
+ }
}
}
@@ -4922,27 +5126,85 @@ static inline int rtl8169_fragmented_frame(u32 status)
return (status & (FirstFrag | LastFrag)) != (FirstFrag | LastFrag);
}
-static inline void rtl8169_rx_csum(struct sk_buff *skb,
+static inline void rtl8169_rx_hash(struct rtl8169_private *tp,
+ struct RxDesc *desc,
+ struct sk_buff *skb)
+{
+ u32 rss_header_info;
+ u32 hash_val;
+
+ if (!(tp->dev->features & NETIF_F_RXHASH))
+ return;
+
+ rss_header_info = le32_to_cpu(desc->rss_dword.rss_info);
+
+ if (!(rss_header_info & RXS_RSS_L3_TYPE_MASK))
+ return;
+
+ hash_val = le32_to_cpu(desc->rss_dword.rss_result);
+
+ skb_set_hash(skb, hash_val,
+ (RXS_RSS_L4_TYPE_MASK & rss_header_info) ?
+ PKT_HASH_TYPE_L4 : PKT_HASH_TYPE_L3);
+}
+
+static inline void rtl8169_rx_csum(struct rtl8169_private *tp,
+ struct sk_buff *skb,
u32 opts1)
{
- u32 status = opts1 & (RxProtoMask | RxCSFailMask);
+ bool csum_ok = false;
+
+ switch (tp->init_rx_desc_type) {
+ case RX_DESC_TYPE_RSS:
+ if (((opts1 & RX_TCPT_DESC_RSS) && !(opts1 & RX_TCPF_DESC_RSS)) ||
+ ((opts1 & RX_UDPT_DESC_RSS) && !(opts1 & RX_UDPF_DESC_RSS)))
+ csum_ok = true;
+ break;
+ default:
+ if (opts1 == RxProtoTCP || opts1 == RxProtoUDP)
+ csum_ok = true;
+ break;
+ }
- if (status == RxProtoTCP || status == RxProtoUDP)
+ if (csum_ok)
skb->ip_summed = CHECKSUM_UNNECESSARY;
else
skb_checksum_none_assert(skb);
}
+static __le32 rtl8169_rx_desc_opts1(struct rtl8169_private *tp, struct RxDesc *desc)
+{
+ switch (tp->init_rx_desc_type) {
+ case RX_DESC_TYPE_RSS:
+ return READ_ONCE(desc->rss_opts1);
+ default:
+ return READ_ONCE(desc->opts1);
+ }
+}
+
static bool rtl8169_check_rx_desc_error(struct net_device *dev,
struct rtl8169_private *tp,
u32 status)
{
- if (unlikely(status & RxRES)) {
- if (status & (RxRWT | RxRUNT))
- dev->stats.rx_length_errors++;
- if (status & RxCRC)
- dev->stats.rx_crc_errors++;
- return true;
+ switch (tp->init_rx_desc_type) {
+ case RX_DESC_TYPE_RSS:
+ if (unlikely(status & RX_RES_RSS)) {
+ if (status & RX_RUNT_RSS)
+ dev->stats.rx_length_errors++;
+ if (status & RX_CRC_RSS)
+ dev->stats.rx_crc_errors++;
+ return true;
+ }
+ break;
+ default:
+ if (unlikely(status & RxRES)) {
+ if (status & (RxRWT | RxRUNT))
+ dev->stats.rx_length_errors++;
+ if (status & RxCRC)
+ dev->stats.rx_crc_errors++;
+ return true;
+ }
+ break;
}
return false;
}
@@ -4961,7 +5223,7 @@ static int rtl_rx(struct net_device *dev, struct rtl8169_private *tp,
dma_addr_t addr;
u32 status;
- status = le32_to_cpu(READ_ONCE(desc->opts1));
+ status = le32_to_cpu(rtl8169_rx_desc_opts1(tp, desc));
if (status & DescOwn)
break;
@@ -4979,7 +5241,8 @@ static int rtl_rx(struct net_device *dev, struct rtl8169_private *tp,
if (!(dev->features & NETIF_F_RXALL))
goto release_descriptor;
- else if (status & RxRWT || !(status & (RxRUNT | RxCRC)))
+ if ((status & RxRWT || !(status & (RxRUNT | RxCRC))) &&
+ tp->init_rx_desc_type == RX_DESC_TYPE_DEFAULT)
goto release_descriptor;
}
@@ -5012,10 +5275,12 @@ static int rtl_rx(struct net_device *dev, struct rtl8169_private *tp,
skb->len = pkt_size;
dma_sync_single_for_device(d, addr, pkt_size, DMA_FROM_DEVICE);
- rtl8169_rx_csum(skb, status);
+ if (tp->num_rx_rings > 1)
+ rtl8169_rx_hash(tp, desc, skb);
+ rtl8169_rx_csum(tp, skb, status);
skb->protocol = eth_type_trans(skb, dev);
- rtl8169_rx_vlan_tag(desc, skb);
+ rtl8169_rx_vlan_tag(tp, desc, skb);
if (skb->pkt_type == PACKET_MULTICAST)
dev->stats.multicast++;
@@ -5024,7 +5289,9 @@ static int rtl_rx(struct net_device *dev, struct rtl8169_private *tp,
dev_sw_netstats_rx_add(dev, pkt_size);
release_descriptor:
- rtl8169_mark_to_asic(desc);
+ if (tp->init_rx_desc_type == RX_DESC_TYPE_RSS)
+ desc->rss_addr = cpu_to_le64(ring->rx_desc_phy_addr[entry]);
+ rtl8169_mark_to_asic(tp, desc);
}
return count;
@@ -5596,6 +5863,32 @@ static void rtl_set_irq_mask(struct rtl8169_private *tp)
}
}
+static int get_max_irq_nvecs(struct rtl8169_private *tp)
+{
+ if (tp->mac_version == RTL_GIGA_MAC_VER_80)
+ return R8127_MAX_NUM_IRQVEC;
+ return R8169_IRQ_DEFAULT;
+}
+
+static int get_min_irq_nvecs(struct rtl8169_private *tp)
+{
+ if (tp->mac_version == RTL_GIGA_MAC_VER_80)
+ return R8127_MIN_NUM_IRQVEC;
+ return R8169_IRQ_DEFAULT;
+}
+
+static void rtl8169_set_rx_ring_num(struct rtl8169_private *tp)
+{
+ if (tp->irq_nvecs >= get_min_irq_nvecs(tp)) {
+ unsigned int rss_queue_num = netif_get_num_default_rss_queues();
+
+ tp->num_rx_rings = rounddown_pow_of_two(min(rss_queue_num,
+ tp->hw_supp_num_rx_queues));
+ if (tp->num_rx_rings >= 2)
+ tp->init_rx_desc_type = RX_DESC_TYPE_RSS;
+ }
+}
+
static int rtl_alloc_irq(struct rtl8169_private *tp)
{
struct pci_dev *pdev = tp->pci_dev;
@@ -5616,7 +5909,10 @@ static int rtl_alloc_irq(struct rtl8169_private *tp)
break;
}
- nvecs = pci_alloc_irq_vectors(pdev, 1, 1, flags);
+ nvecs = pci_alloc_irq_vectors(pdev, get_min_irq_nvecs(tp), get_max_irq_nvecs(tp), flags);
+
+ if (nvecs < 0)
+ nvecs = pci_alloc_irq_vectors(pdev, 1, 1, flags);
if (nvecs < 0)
return nvecs;
@@ -6040,6 +6336,12 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
tp->dash_type = rtl_get_dash_type(tp);
tp->dash_enabled = rtl_dash_is_enabled(tp);
+ if (rtl_hw_support_rss(tp)) {
+ tp->rss_data = devm_kzalloc(&pdev->dev, sizeof(*tp->rss_data), GFP_KERNEL);
+ if (!tp->rss_data)
+ return -ENOMEM;
+ }
+
tp->cp_cmd = RTL_R16(tp, CPlusCmd) & CPCMD_MASK;
if (sizeof(dma_addr_t) > 4 && tp->mac_version >= RTL_GIGA_MAC_VER_18 &&
@@ -6060,6 +6362,11 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
if (rc < 0)
return dev_err_probe(&pdev->dev, rc, "Can't allocate interrupt\n");
+ rtl8169_set_rx_ring_num(tp);
+
+ if (rtl_hw_support_rss(tp))
+ rtl8169_init_rss(tp);
+
INIT_WORK(&tp->wk.work, rtl_task);
disable_work(&tp->wk.work);
@@ -6072,6 +6379,11 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
dev->vlan_features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_TSO;
dev->priv_flags |= IFF_LIVE_ADDR_CHANGE;
+ if (rtl_hw_support_rss(tp) && tp->num_rx_rings > 1) {
+ dev->hw_features |= NETIF_F_RXHASH;
+ dev->features |= NETIF_F_RXHASH;
+ }
+
/*
* Pretend we are using VLANs; This bypasses a nasty bug where
* Interrupts stop flowing on high load on 8110SCd controllers.
--
2.43.0
^ permalink raw reply related
* Re: [PATCH] dt-bindings: Fix bracket
From: Krzysztof Kozlowski @ 2026-06-29 7:10 UTC (permalink / raw)
To: Manuel Ebner
Cc: Andrew Lunn, David S . Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Oleksij Rempel, open list:NETWORKING DRIVERS,
open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
open list, Randy Dunlap
In-Reply-To: <20260629-crystal-hilarious-trout-ee05ab@quoll>
On 29/06/2026 09:09, Krzysztof Kozlowski wrote:
> On Sat, Jun 27, 2026 at 11:19:36AM +0200, Manuel Ebner wrote:
>> Add "(Alternate" to "ID)"
>
> Why?
>
> I see what you did in the diff, so why repeating it?
>
Heh, and the subject is "fix bracket" - what bracket?
Please use subject prefixes matching the subsystem. You can get them for
example with `git log --oneline -- DIRECTORY_OR_FILE` on the directory
your patch is touching. For bindings, the preferred subjects are
explained here:
https://www.kernel.org/doc/html/latest/devicetree/bindings/submitting-patches.html#i-for-patch-submitters
Best regards,
Krzysztof
^ permalink raw reply
* Re: [PATCH] dt-bindings: Fix bracket
From: Krzysztof Kozlowski @ 2026-06-29 7:09 UTC (permalink / raw)
To: Manuel Ebner
Cc: Andrew Lunn, David S . Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Oleksij Rempel, open list:NETWORKING DRIVERS,
open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
open list, Randy Dunlap
In-Reply-To: <20260627091936.29809-1-manuelebner@mailbox.org>
On Sat, Jun 27, 2026 at 11:19:36AM +0200, Manuel Ebner wrote:
> Add "(Alternate" to "ID)"
Why?
I see what you did in the diff, so why repeating it?
>
> Signed-off-by: Manuel Ebner <manuelebner@mailbox.org>
Best regards,
Krzysztof
^ permalink raw reply
* RE: [External Mail] Re: [PATCH v3 1/7] net: wwan: t9xx: Add PCIe core
From: Wu. JackBB (GSM) @ 2026-06-29 7:05 UTC (permalink / raw)
To: Andrew Lunn
Cc: Loic Poulain, Sergey Ryazanov, Johannes Berg, Andrew Lunn,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Wen-Zhi Huang, Shi-Wei Yeh, Minano Tseng, Matthias Brugger,
AngeloGioacchino Del Regno, Simon Horman, Jonathan Corbet,
Shuah Khan, linux-kernel@vger.kernel.org, netdev@vger.kernel.org,
linux-arm-kernel@lists.infradead.org,
linux-mediatek@lists.infradead.org, linux-doc@vger.kernel.org
In-Reply-To: <b6ee3440-385f-4567-993d-c10db6f10b97@lunn.ch>
Hi Andrew,
> > +#else /* !CONFIG_ACPI */
> > + dev_err((mdev)->dev, "Unsupported, CONFIG ACPI hasn't been set to 'y'\n");
>
> Why not just have the Kconfig depend on ACPI?
Will add "depends on ACPI" and remove the #ifdef blocks in v4.
> > + if (ret) {
> > + dev_err((mdev)->dev, "Failed to register mhccif_irq callback\n");
>
> Why the () around mdev?
Will remove the unnecessary parentheses from all occurrences in v4.
Thanks.
^ permalink raw reply
* Re: [RFC PATCH net-next v8 03/12] net: phylink: add phylink_release_pcs() to externally release a PCS
From: Maxime Chevallier @ 2026-06-29 7:04 UTC (permalink / raw)
To: Christian Marangi
Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Simon Horman, Jonathan Corbet, Shuah Khan, Lorenzo Bianconi,
Heiner Kallweit, Russell King, Saravana Kannan, Philipp Zabel,
Nathan Chancellor, Nick Desaulniers, Bill Wendling, Justin Stitt,
netdev, devicetree, linux-kernel, linux-doc, linux-arm-kernel,
linux-mediatek, llvm
In-Reply-To: <6a3fc312.6161eb1e.3441bb.c0de@mx.google.com>
Hi Christian,
On 6/27/26 14:33, Christian Marangi wrote:
> On Thu, Jun 25, 2026 at 04:13:14PM +0200, Maxime Chevallier wrote:
>> Hello Christian,
>>
>> On 6/18/26 14:57, Christian Marangi wrote:
>>> Add phylink_release_pcs() to externally release a PCS from a phylink
>>> instance. This can be used to handle case when a single PCS needs to be
>>> removed and the phylink instance needs to be refreshed.
>>>
>>> On calling phylink_release_pcs(), the PCS will be removed from the
>>> phylink internal PCS list and the phylink supported_interfaces value is
>>> reparsed with the remaining PCS interfaces.
>>>
>>> Also a phylink resolve is triggered to handle the PCS removal.
>>>
>>> The flag force_major_config is set to make phylink resolve reconfigure
>>> the interface (even if it didn't change).
>>> This is needed to handle the special case when the current PCS used
>>> by phylink is removed and a major_config is needed to propagae the
>>> configuration change. With this option enabled we also force mac_config
>>> even if the PHY link is not up for the in-band case.
>>>
>>> Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
>>> ---
>>> drivers/net/phy/phylink.c | 56 +++++++++++++++++++++++++++++++++++++++
>>> include/linux/phylink.h | 2 ++
>>> 2 files changed, 58 insertions(+)
>>>
>>> diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c
>>> index c38bcd43b8c8..064d6f5a06da 100644
>>> --- a/drivers/net/phy/phylink.c
>>> +++ b/drivers/net/phy/phylink.c
>>> @@ -158,6 +158,8 @@ static const phy_interface_t phylink_sfp_interface_preference[] = {
>>> static DECLARE_PHY_INTERFACE_MASK(phylink_sfp_interfaces);
>>>
>>> static void phylink_run_resolve(struct phylink *pl);
>>> +static void phylink_link_down(struct phylink *pl);
>>> +static void phylink_pcs_disable(struct phylink_pcs *pcs);
>>>
>>> /**
>>> * phylink_set_port_modes() - set the port type modes in the ethtool mask
>>> @@ -918,6 +920,60 @@ static void phylink_resolve_an_pause(struct phylink_link_state *state)
>>> }
>>> }
>>>
>>> +/**
>>> + * phylink_release_pcs - Removes a PCS from the phylink PCS available list
>>> + * @pcs: a pointer to the phylink_pcs struct to be released
>>> + *
>>> + * This function release a PCS from the phylink PCS available list if
>>> + * actually in use. It also refreshes the supported interfaces of the
>>> + * phylink instance by copying the supported interfaces from the phylink
>>> + * conf and merging the supported interfaces of the remaining available PCS
>>> + * in the list and trigger a resolve.
>>> + */
>>> +void phylink_release_pcs(struct phylink_pcs *pcs)
>>> +{
>>> + struct phylink *pl;
>>> +
>>> + ASSERT_RTNL();
>>> +
>>> + pl = pcs->phylink;
>>> + if (!pl)
>>> + return;
>>> +
>>> + mutex_lock(&pl->state_mutex);
>>> +
>>> + list_del(&pcs->list);
>>> + pcs->phylink = NULL;
>>> +
>>> + /*
>>> + * Check if we are removing the PCS currently
>>> + * in use by phylink. If this is the case, tear down
>>> + * the link, force phylink resolve to reconfigure the
>>> + * interface mode, disable the current PCS and set the
>>> + * phylink PCS to NULL.
>>> + */
>>> + if (pl->pcs == pcs) {
>>> + phylink_link_down(pl);
>>> + phylink_pcs_disable(pl->pcs);
>>> +
>>> + pl->force_major_config = true;
>>> + pl->pcs = NULL;
>>> + }
>>> +
>>> + mutex_unlock(&pl->state_mutex);
>>> +
>>> + /* Refresh supported interfaces */
>>> + phy_interface_copy(pl->supported_interfaces,
>>> + pl->config->supported_interfaces);
>>> + list_for_each_entry(pcs, &pl->pcs_list, list)
>>> + phy_interface_or(pl->supported_interfaces,
>>> + pl->supported_interfaces,
>>> + pcs->supported_interfaces);
>>
>> I've given more thought to that 'supported_interfaces' thing. This
>> patchset redefines the meaning of
>>
>> pl->config->supported_interfaces
>>
>> Currently, it's filled by the MAC driver and means "Every interface
>> we can support, including the ones provided by PCSs that we can use
>> with this MAC".
>>
>> It now becomes "Every interface we support without needing a PCS", at
>> least the way I understand that.
>>
>
> Wait but with the current code using the OR logic, it still follows
> "Every interface we can support...". The modes that needs a PCS are
> specificed with the pcs_interfaces mask in phylink_config.
you current code is correct, I was mostly concerned about the doc
that goes along with it :)
So in the end, we'd have something like (simplified):
pl->config.supported_interfaces = RGMII_xx | SGMII | 1000BaseX
pl->config.pcs_interfaces = SGMII | 1000BaseX
pcs->supported_interface = SGMII| 1000BaseX
correct ?
>
> The late add and release operates on the phylink supported_interfaces ONLY
> when the MAC didn't specify support for it (by removing it as only the PCS
> will declare support for it)
>
> The confusion is present because everything is validated later on
> major_config so those supported_interfaces are just an HINT that are later
> verified with get_caps and with the pcs_validate OPs.
>
> Adding the supported_interfaces to phylink is really to keep an original
> reference of the value. This is to address a pattern I have notice where
> the MAC driver always OR the interfaces with the one supported by the PCS.
> (I remember it was pointed out by Russell)
>
> But I'm more than open to discussion as this is something marginal to the
> whole implementation, I'm also questioning if this OR is actually useful to
> anything on the nth tought on this.
>
> One thing that I notice is that parsing this early with AND might be
> problematic at phylink_create, but I still have to evaluate that.
>
> My take is that would be good to have some review also on the other logic
> as I think I reached a point where Sashiko starts to comments on more or
> less unreal problem.
True, TBH all the fwnode part is something I'm a bit less familiar with though
so maybe someone else can browse through that.
FWIW, I've tested that whole series on a board that has "legacy" PCS board
that has mvpp2 and 2 possible PCSs, and it seems to work fine so no regressions
there :)
A side note with the "legacy" naming, I'd rather have it called "built-in" or
something like that, I don't see a clear path to porting the existing code to
fwnode without breaking DT compat, as it's likely we'll have to remove the PCS
register ranges out of the MAC's range.
Thanks for this work anyway, this is great !
Maxime
^ permalink raw reply
* [PATCH net-next v7 4/4] net: phy: realtek: load firmware for RTL8261C_CG
From: javen @ 2026-06-29 6:47 UTC (permalink / raw)
To: andrew, hkallweit1, linux, davem, edumazet, kuba, pabeni,
freddy_gu, nb
Cc: netdev, linux-kernel, daniel, vladimir.oltean, nic_swsd, Javen Xu
In-Reply-To: <20260629064718.1349-1-javen_xu@realsil.com.cn>
From: Javen Xu <javen_xu@realsil.com.cn>
This patch adds support for loading firmware. Download some parameters
for RTL8261C_CG.
Signed-off-by: Javen Xu <javen_xu@realsil.com.cn>
---
Changes in v2:
- remove __pack, struct rtl8261x_fw_header and rtl8261x_fw_entry will not pad
- reverse xmas tree for some definition
- add explanation on rtl_phy_write_mmd_bits()
Changes in v3:
- add struct rtl8261x_priv
Changes in v4:
- add struct device *dev
Changes in v5:
- no changes
Changes in v6:
- replace rtl_phy_write_mmd_bits with phy_modify_mmd, keep mdio lock
- check msb and lsb at the beginning of rtl8261x_fw_execute_entry()
- add comments on rtl8261x_config_init()
Changes in v7:
- no changes
---
drivers/net/phy/realtek/realtek_main.c | 220 +++++++++++++++++++++++++
1 file changed, 220 insertions(+)
diff --git a/drivers/net/phy/realtek/realtek_main.c b/drivers/net/phy/realtek/realtek_main.c
index ef3700894ebf..bf7bc19fb44c 100644
--- a/drivers/net/phy/realtek/realtek_main.c
+++ b/drivers/net/phy/realtek/realtek_main.c
@@ -8,7 +8,9 @@
* Copyright (c) 2004 Freescale Semiconductor, Inc.
*/
#include <linux/bitops.h>
+#include <linux/crc32.h>
#include <linux/ethtool_netlink.h>
+#include <linux/firmware.h>
#include <linux/of.h>
#include <linux/phy.h>
#include <linux/pm_wakeirq.h>
@@ -281,6 +283,42 @@
RTL8261X_INT_ALDPS_CHG | \
RTL8261X_INT_JABBER)
+#define FW_MAIN_MAGIC 0x52544C38
+#define FW_SUB_MAGIC_8261C 0x32363143
+#define RTL8261X_POLL_TIMEOUT_MS 100
+
+#define RTL8261C_CE_FW_NAME "rtl_nic/rtl8261c.bin"
+MODULE_FIRMWARE(RTL8261C_CE_FW_NAME);
+
+enum rtl8261x_fw_op {
+ OP_WRITE = 0x00, /* Write */
+ OP_POLL = 0x02, /* Polling */
+};
+
+struct rtl8261x_fw_header {
+ __le32 main_magic; /* Main magic number 0x52544C38 ("RTL8") */
+ __le32 sub_magic; /* Sub magic number */
+ __le16 version_major; /* Major version */
+ __le16 version_minor; /* Minor version */
+ __le16 num_entries; /* Number of entries */
+ __le16 reserved; /* Reserved */
+ __le32 crc32; /* CRC32 checksum */
+};
+
+struct rtl8261x_fw_entry {
+ __u8 type; /* Operation type (OP_*) */
+ __u8 dev; /* MMD device */
+ __le16 addr; /* Register address */
+ __u8 msb; /* MSB bit position */
+ __u8 lsb; /* LSB bit position */
+ __le16 value; /* Value to write/compare */
+ __le16 timeout_ms; /* Poll timeout in milliseconds */
+ __u8 poll_set; /* Poll for set (1) or clear (0) */
+ __u8 reserved; /* Reserved */
+};
+
+#define FW_HEADER_SIZE sizeof(struct rtl8261x_fw_header)
+#define FW_ENTRY_SIZE sizeof(struct rtl8261x_fw_entry)
/* RTL8211E and RTL8211F support up to three LEDs */
#define RTL8211x_LED_COUNT 3
@@ -300,6 +338,11 @@ struct rtl821x_priv {
u16 iner;
};
+struct rtl8261x_priv {
+ const char *fw_name;
+ bool fw_loaded;
+};
+
static int rtl821x_read_page(struct phy_device *phydev)
{
return __phy_read(phydev, RTL821x_PAGE_SELECT);
@@ -342,8 +385,16 @@ static int rtl821x_modify_ext_page(struct phy_device *phydev, u16 ext_page,
static int rtl8261x_probe(struct phy_device *phydev)
{
+ struct device *dev = &phydev->mdio.dev;
+ struct rtl8261x_priv *priv;
int sub_phy_id, ret;
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ phydev->priv = priv;
+
ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, RTL8261X_EXT_ADDR_REG,
RTL_8261X_SUB_PHY_ID_ADDR);
if (ret < 0)
@@ -357,6 +408,7 @@ static int rtl8261x_probe(struct phy_device *phydev)
switch (sub_phy_id) {
case RTL8261C_CE_MODEL:
+ priv->fw_name = RTL8261C_CE_FW_NAME;
phydev_info(phydev, "RTL8261C detected (sub_id 0x%02x)\n", sub_phy_id);
break;
@@ -407,6 +459,153 @@ static int rtl8261x_read_status(struct phy_device *phydev)
return 0;
}
+static int rtl8261x_verify_firmware(struct phy_device *phydev, const struct firmware *fw)
+{
+ const struct rtl8261x_fw_header *hdr;
+ u32 main_magic, sub_magic;
+ u32 calc_crc, file_crc;
+ size_t data_len;
+ u16 num_entries;
+
+ if (fw->size < FW_HEADER_SIZE) {
+ phydev_err(phydev, "Firmware too small: %zu bytes\n", fw->size);
+ return -EINVAL;
+ }
+
+ hdr = (const struct rtl8261x_fw_header *)fw->data;
+
+ main_magic = le32_to_cpu(hdr->main_magic);
+ if (main_magic != FW_MAIN_MAGIC) {
+ phydev_err(phydev, "Invalid firmware magic: 0x%08x\n", main_magic);
+ return -EINVAL;
+ }
+
+ sub_magic = le32_to_cpu(hdr->sub_magic);
+ if (sub_magic != FW_SUB_MAGIC_8261C) {
+ phydev_err(phydev, "Invalid sub magic: 0x%08x\n", sub_magic);
+ return -EINVAL;
+ }
+
+ num_entries = le16_to_cpu(hdr->num_entries);
+ data_len = num_entries * FW_ENTRY_SIZE;
+
+ if (fw->size != sizeof(*hdr) + data_len) {
+ phydev_err(phydev, "Firmware size mismatch\n");
+ return -EINVAL;
+ }
+
+ calc_crc = crc32(~0, fw->data + FW_HEADER_SIZE, data_len) ^ ~0;
+ file_crc = le32_to_cpu(hdr->crc32);
+
+ if (calc_crc != file_crc) {
+ phydev_err(phydev, "CRC32 mismatch: calculated=0x%08x file=0x%08x\n",
+ calc_crc, file_crc);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int rtl8261x_fw_execute_entry(struct phy_device *phydev,
+ const struct rtl8261x_fw_entry *entry)
+{
+ u16 addr, value, timeout_ms;
+ u8 dev, msb, lsb, poll_set;
+ u32 bits, expect_val;
+ int ret = 0;
+ int val;
+
+ dev = entry->dev;
+ addr = le16_to_cpu(entry->addr);
+ msb = entry->msb;
+ lsb = entry->lsb;
+ value = le16_to_cpu(entry->value);
+ timeout_ms = le16_to_cpu(entry->timeout_ms);
+ poll_set = entry->poll_set;
+
+ if (timeout_ms == 0)
+ timeout_ms = RTL8261X_POLL_TIMEOUT_MS;
+
+ if (msb > 15 || lsb > msb) {
+ phydev_err(phydev, "Invalid firmware bits: msb=%d, lsb=%d\n", msb, lsb);
+ return -EINVAL;
+ }
+
+ switch (entry->type) {
+ case OP_WRITE:
+ ret = phy_modify_mmd(phydev, dev, addr,
+ GENMASK(msb, lsb), (value << lsb) & GENMASK(msb, lsb));
+ if (ret) {
+ phydev_err(phydev, "WRITE failed: dev=%d addr=0x%04x\n", dev, addr);
+ return ret;
+ }
+ break;
+
+ case OP_POLL: {
+ bits = GENMASK(msb, lsb);
+ expect_val = (value << lsb) & bits;
+
+ if (poll_set)
+ ret = phy_read_mmd_poll_timeout(phydev, dev, addr, val,
+ (val & bits) == expect_val,
+ 1000, timeout_ms * 1000, false);
+ else
+ ret = phy_read_mmd_poll_timeout(phydev, dev, addr, val,
+ (val & bits) != expect_val,
+ 1000, timeout_ms * 1000, false);
+ if (ret)
+ phydev_err(phydev, "POLL timeout: dev=%d addr=0x%04x\n",
+ dev, addr);
+ break;
+ }
+ default:
+ phydev_err(phydev, "Unknown firmware operation: %d\n", entry->type);
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static int rtl8261x_fw_load(struct phy_device *phydev)
+{
+ struct rtl8261x_priv *priv = phydev->priv;
+ const struct rtl8261x_fw_entry *entry;
+ const struct rtl8261x_fw_header *hdr;
+ const struct firmware *fw;
+ int ret, i;
+
+ if (!priv->fw_name)
+ return 0;
+
+ ret = request_firmware(&fw, priv->fw_name, &phydev->mdio.dev);
+ if (ret) {
+ phydev_err(phydev, "Failed to load firmware %s: %d\n", priv->fw_name, ret);
+ return ret;
+ }
+
+ ret = rtl8261x_verify_firmware(phydev, fw);
+ if (ret)
+ goto release_fw;
+
+ hdr = (const struct rtl8261x_fw_header *)fw->data;
+
+ entry = (const struct rtl8261x_fw_entry *)(fw->data + FW_HEADER_SIZE);
+ for (i = 0; i < le16_to_cpu(hdr->num_entries); i++, entry++) {
+ ret = rtl8261x_fw_execute_entry(phydev, entry);
+ if (ret) {
+ phydev_err(phydev, "Entry %d failed: %d\n", i, ret);
+ goto release_fw;
+ }
+ }
+
+ priv->fw_loaded = true;
+
+release_fw:
+ release_firmware(fw);
+ return ret;
+}
+
static int rtl8261x_config_intr(struct phy_device *phydev)
{
int ret;
@@ -484,6 +683,26 @@ static int rtl8261x_config_aneg(struct phy_device *phydev)
return 0;
}
+static int rtl8261x_config_init(struct phy_device *phydev)
+{
+ struct rtl8261x_priv *priv = phydev->priv;
+ int ret = 0;
+
+ /* The firmware parameters are preserved across IEEE soft resets and
+ * suspend/resume cycles. Reloading is only necessary after a power
+ * cycle or hard reset.
+ */
+ if (priv->fw_name && !priv->fw_loaded) {
+ ret = rtl8261x_fw_load(phydev);
+ if (ret) {
+ phydev_err(phydev, "Firmware loading failed: %d\n", ret);
+ return ret;
+ }
+ }
+
+ return ret;
+}
+
static int rtl821x_probe(struct phy_device *phydev)
{
struct device *dev = &phydev->mdio.dev;
@@ -3179,6 +3398,7 @@ static struct phy_driver realtek_drvs[] = {
PHY_ID_MATCH_EXACT(RTL_8261C_CG),
.name = "Realtek RTL8261C 10Gbps PHY",
.probe = rtl8261x_probe,
+ .config_init = rtl8261x_config_init,
.get_features = rtl8261x_get_features,
.config_aneg = rtl8261x_config_aneg,
.read_status = rtl8261x_read_status,
--
2.43.0
^ permalink raw reply related
* [PATCH net-next v7 3/4] net: phy: realtek: add support for RTL8261C_CG
From: javen @ 2026-06-29 6:47 UTC (permalink / raw)
To: andrew, hkallweit1, linux, davem, edumazet, kuba, pabeni,
freddy_gu, nb
Cc: netdev, linux-kernel, daniel, vladimir.oltean, nic_swsd, Javen Xu
In-Reply-To: <20260629064718.1349-1-javen_xu@realsil.com.cn>
From: Javen Xu <javen_xu@realsil.com.cn>
This patch adds support for Realtek phy chip RTL8261C_CG. Its PHY ID is
0x001cc898.
This patch introduces a distinct family of handlers (probe, get_features,
config_aneg, read_status, config_intr, handle_interrupt).
Signed-off-by: Javen Xu <javen_xu@realsil.com.cn>
---
Changes in v2:
- no changes, new file
Changes in v3:
- re-order function according to the order in phy-c45.c
- add kernel-doc about return value
- add MASTER_SLAVE_CFG_MASTER_PREFERRED,
MASTER_SLAVE_CFG_SLAVE_PREFERRED, MASTER_SLAVE_CFG_UNKNOWN,
MASTER_SLAVE_CFG_UNSUPPORTED, MASTER_SLAVE_CFG_SLAVE_PREFERRED cfg
Changes in v4:
- no changes
Changes in v5:
- remove genphy_c45_pma_setup_forced() for this is already done when
calling genphy_c45_config_aneg()
Changes in v6:
- when PHY_INTERRUPT_DISABLE, clear IMR and ISR
- if AUTONEG_DISABLE, nothing need to do in rtl8261x_config_aneg
- add rtl8261x_read_status, support 1G speed
Changes in v7:
- remove RTL8261X_IMR and RTL8261X_ISR, duplicated definition
- modify commit message
- continue with default behavior when meet unknown sub_phy_id
- change the internal order of rtl8261x_read_status
- expand RTL8261X_INT_MASK_DEFAULT
- add the handle for ADVERTISE_1000HALF in rtl8261x_config_aneg
---
drivers/net/phy/realtek/realtek_main.c | 186 +++++++++++++++++++++++++
1 file changed, 186 insertions(+)
diff --git a/drivers/net/phy/realtek/realtek_main.c b/drivers/net/phy/realtek/realtek_main.c
index 27268811f564..ef3700894ebf 100644
--- a/drivers/net/phy/realtek/realtek_main.c
+++ b/drivers/net/phy/realtek/realtek_main.c
@@ -141,6 +141,10 @@
#define RTL8211F_PHYSICAL_ADDR_WORD1 17
#define RTL8211F_PHYSICAL_ADDR_WORD2 18
+#define RTL8261X_EXT_ADDR_REG 0xa436
+#define RTL8261X_EXT_DATA_REG 0xa438
+#define RTL_8261X_SUB_PHY_ID_ADDR 0x801d
+
#define RTL822X_VND1_SERDES_OPTION 0x697a
#define RTL822X_VND1_SERDES_OPTION_MODE_MASK GENMASK(5, 0)
#define RTL822X_VND1_SERDES_OPTION_MODE_2500BASEX_SGMII 0
@@ -251,6 +255,32 @@
#define RTL_8221B_VM_CG 0x001cc84a
#define RTL_8251B 0x001cc862
#define RTL_8261C 0x001cc890
+#define RTL_8261C_CG 0x001cc898
+
+#define RTL8261C_CE_MODEL 0x00
+#define RTL8261X_INT_AUTONEG_ERROR BIT(0)
+#define RTL8261X_INT_PAGE_RECV BIT(2)
+#define RTL8261X_INT_AUTONEG_DONE BIT(3)
+#define RTL8261X_INT_LINK_CHG BIT(4)
+#define RTL8261X_INT_PHY_REG_ACCESS BIT(5)
+#define RTL8261X_INT_PME BIT(7)
+#define RTL8261X_INT_ALDPS_CHG BIT(9)
+#define RTL8261X_INT_JABBER BIT(10)
+
+#define RTL8261X_INT_MASK_DEFAULT (RTL8261X_INT_AUTONEG_DONE | \
+ RTL8261X_INT_LINK_CHG | \
+ RTL8261X_INT_AUTONEG_ERROR | \
+ RTL8261X_INT_JABBER)
+
+#define RTL8261X_INT_MASK_ALL (RTL8261X_INT_AUTONEG_ERROR | \
+ RTL8261X_INT_PAGE_RECV | \
+ RTL8261X_INT_AUTONEG_DONE | \
+ RTL8261X_INT_LINK_CHG | \
+ RTL8261X_INT_PHY_REG_ACCESS | \
+ RTL8261X_INT_PME | \
+ RTL8261X_INT_ALDPS_CHG | \
+ RTL8261X_INT_JABBER)
+
/* RTL8211E and RTL8211F support up to three LEDs */
#define RTL8211x_LED_COUNT 3
@@ -310,6 +340,150 @@ static int rtl821x_modify_ext_page(struct phy_device *phydev, u16 ext_page,
return phy_restore_page(phydev, oldpage, ret);
}
+static int rtl8261x_probe(struct phy_device *phydev)
+{
+ int sub_phy_id, ret;
+
+ ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, RTL8261X_EXT_ADDR_REG,
+ RTL_8261X_SUB_PHY_ID_ADDR);
+ if (ret < 0)
+ return ret;
+
+ ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, RTL8261X_EXT_DATA_REG);
+ if (ret < 0)
+ return ret;
+
+ sub_phy_id = (ret >> 8) & 0xff;
+
+ switch (sub_phy_id) {
+ case RTL8261C_CE_MODEL:
+ phydev_info(phydev, "RTL8261C detected (sub_id 0x%02x)\n", sub_phy_id);
+ break;
+
+ default:
+ phydev_warn(phydev, "Unknown sub_id 0x%02x, default behavior\n", sub_phy_id);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int rtl8261x_get_features(struct phy_device *phydev)
+{
+ int ret;
+
+ ret = genphy_c45_pma_read_abilities(phydev);
+ if (ret)
+ return ret;
+ /*
+ * Supplement Multi-Gig speeds that may not be automatically detected
+ * RTL8261X supports 2.5G/5G in addition to standard 10G
+ */
+ linkmode_set_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT,
+ phydev->supported);
+ linkmode_set_bit(ETHTOOL_LINK_MODE_5000baseT_Full_BIT,
+ phydev->supported);
+
+ return 0;
+}
+
+static int rtl8261x_read_status(struct phy_device *phydev)
+{
+ int ret, val;
+
+ if (phydev->autoneg == AUTONEG_ENABLE && phydev->autoneg_complete) {
+ val = phy_read_mmd(phydev, MDIO_MMD_VEND2,
+ RTL822X_VND2_C22_REG(MII_STAT1000));
+ if (val < 0)
+ return val;
+
+ mii_stat1000_mod_linkmode_lpa_t(phydev->lp_advertising, val);
+ }
+
+ ret = genphy_c45_read_status(phydev);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int rtl8261x_config_intr(struct phy_device *phydev)
+{
+ int ret;
+
+ if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
+ ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, RTL8221B_VND2_INSR);
+ if (ret < 0)
+ return ret;
+
+ ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, RTL8221B_VND2_INER,
+ RTL8261X_INT_MASK_DEFAULT);
+ if (ret < 0)
+ return ret;
+ } else {
+ ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, RTL8221B_VND2_INER, 0);
+ if (ret < 0)
+ return ret;
+
+ ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, RTL8221B_VND2_INSR);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static irqreturn_t rtl8261x_handle_interrupt(struct phy_device *phydev)
+{
+ int irq_status;
+
+ irq_status = phy_read_mmd(phydev, MDIO_MMD_VEND2, RTL8221B_VND2_INSR);
+ if (irq_status < 0) {
+ phy_error(phydev);
+ return IRQ_NONE;
+ }
+
+ if (!(irq_status & RTL8261X_INT_MASK_ALL))
+ return IRQ_NONE;
+
+ if (irq_status & (RTL8261X_INT_LINK_CHG | RTL8261X_INT_AUTONEG_DONE |
+ RTL8261X_INT_AUTONEG_ERROR | RTL8261X_INT_JABBER))
+ phy_trigger_machine(phydev);
+
+ return IRQ_HANDLED;
+}
+
+static int rtl8261x_config_aneg(struct phy_device *phydev)
+{
+ u16 adv_1g = 0;
+ int ret;
+
+ ret = genphy_c45_config_aneg(phydev);
+ if (ret < 0)
+ return ret;
+
+ if (phydev->autoneg == AUTONEG_DISABLE)
+ return 0;
+
+ if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
+ phydev->advertising))
+ adv_1g = ADVERTISE_1000FULL;
+ if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT,
+ phydev->advertising))
+ adv_1g |= ADVERTISE_1000HALF;
+
+ ret = phy_modify_mmd_changed(phydev, MDIO_MMD_VEND2,
+ RTL822X_VND2_C22_REG(MII_CTRL1000),
+ ADVERTISE_1000FULL | ADVERTISE_1000HALF,
+ adv_1g);
+ if (ret < 0)
+ return ret;
+ if (ret > 0)
+ return genphy_c45_restart_aneg(phydev);
+
+ return 0;
+}
+
static int rtl821x_probe(struct phy_device *phydev)
{
struct device *dev = &phydev->mdio.dev;
@@ -3001,6 +3175,18 @@ static struct phy_driver realtek_drvs[] = {
.resume = genphy_resume,
.read_mmd = genphy_read_mmd_unsupported,
.write_mmd = genphy_write_mmd_unsupported,
+ }, {
+ PHY_ID_MATCH_EXACT(RTL_8261C_CG),
+ .name = "Realtek RTL8261C 10Gbps PHY",
+ .probe = rtl8261x_probe,
+ .get_features = rtl8261x_get_features,
+ .config_aneg = rtl8261x_config_aneg,
+ .read_status = rtl8261x_read_status,
+ .config_intr = rtl8261x_config_intr,
+ .handle_interrupt = rtl8261x_handle_interrupt,
+ .soft_reset = genphy_c45_soft_reset,
+ .suspend = genphy_c45_pma_suspend,
+ .resume = genphy_c45_pma_resume,
},
};
--
2.43.0
^ permalink raw reply related
* [PATCH net-next v7 2/4] net: phy: c45: add setup and read master/slave helpers
From: javen @ 2026-06-29 6:47 UTC (permalink / raw)
To: andrew, hkallweit1, linux, davem, edumazet, kuba, pabeni,
freddy_gu, nb
Cc: netdev, linux-kernel, daniel, vladimir.oltean, nic_swsd, Javen Xu
In-Reply-To: <20260629064718.1349-1-javen_xu@realsil.com.cn>
From: Javen Xu <javen_xu@realsil.com.cn>
This patch adds two static helpers in drivers/net/phy/phy-c45.c to
configure and read back master-slave roles for non BASE-T1 Clause 45
PHYs via the 10GBASE-T AN control/status registers.
These helpers are wired into genphy_c45_config_aneg() and
genphy_c45_read_status(). This changes the observable ethtool output
for drivers using the generic c45 read path.
Signed-off-by: Javen Xu <javen_xu@realsil.com.cn>
---
Changes in v2:
- no changes, new file
Changes in v3:
- re-order function according to the order in phy-c45.c
- add kernel-doc about return value
- add MASTER_SLAVE_CFG_MASTER_PREFERRED,
MASTER_SLAVE_CFG_SLAVE_PREFERRED, MASTER_SLAVE_CFG_UNKNOWN,
MASTER_SLAVE_CFG_UNSUPPORTED, MASTER_SLAVE_CFG_SLAVE_PREFERRED cfg
Changes in v4:
- no changes
Changes in v5:
- move genphy_c45_an_setup_master_slave() to genphy_c45_config_aneg(),
as that C22 does.
Changes in v6:
- add colon in the function description
- add genphy_c45_read_master_slave in read function
Changes in v7:
- when phydev->link is down, just return UNKNOWN
- modify commit message
---
drivers/net/phy/phy-c45.c | 103 ++++++++++++++++++++++++++++++++++++++
include/uapi/linux/mdio.h | 5 ++
2 files changed, 108 insertions(+)
diff --git a/drivers/net/phy/phy-c45.c b/drivers/net/phy/phy-c45.c
index 60d044156a83..df682d3ebd5a 100644
--- a/drivers/net/phy/phy-c45.c
+++ b/drivers/net/phy/phy-c45.c
@@ -406,6 +406,97 @@ int genphy_c45_soft_reset(struct phy_device *phydev)
}
EXPORT_SYMBOL_GPL(genphy_c45_soft_reset);
+/**
+ * genphy_c45_an_setup_master_slave - Configure Master/Slave setting for C45 PHYs
+ * @phydev: target phy_device struct
+ *
+ * Description: Configure the forced or preferred Master/Slave role
+ * 10GBASE-T control register (MMD 7, Register 0x0020) according to
+ * IEEE 802.3 standards.
+ *
+ * Return: negative errno code on failure, 0 if Master/Slave didn't change,
+ * or 1 if Master/Slave modes changed.
+ */
+static int genphy_c45_an_setup_master_slave(struct phy_device *phydev)
+{
+ u16 ctl = 0;
+
+ switch (phydev->master_slave_set) {
+ case MASTER_SLAVE_CFG_MASTER_PREFERRED:
+ ctl = MDIO_AN_10GBT_CTRL_MS_PORT_TYPE;
+ break;
+ case MASTER_SLAVE_CFG_SLAVE_PREFERRED:
+ break;
+ case MASTER_SLAVE_CFG_MASTER_FORCE:
+ ctl = MDIO_AN_10GBT_CTRL_MS_ENABLE | MDIO_AN_10GBT_CTRL_MS_VALUE;
+ break;
+ case MASTER_SLAVE_CFG_SLAVE_FORCE:
+ ctl = MDIO_AN_10GBT_CTRL_MS_ENABLE;
+ break;
+ case MASTER_SLAVE_CFG_UNKNOWN:
+ case MASTER_SLAVE_CFG_UNSUPPORTED:
+ return 0;
+ default:
+ phydev_warn(phydev, "Unsupported Master/Slave mode\n");
+ return -EOPNOTSUPP;
+ }
+
+ return phy_modify_mmd_changed(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_CTRL,
+ MDIO_AN_10GBT_CTRL_MS_ENABLE |
+ MDIO_AN_10GBT_CTRL_MS_VALUE |
+ MDIO_AN_10GBT_CTRL_MS_PORT_TYPE, ctl);
+}
+
+/**
+ * genphy_c45_read_master_slave - read master/slave status
+ * @phydev: target phy_device struct
+ *
+ * Description: Read the Master/Slave configuration and status
+ * from 10GBASE-T control/status registers (MMD 7, Reg 0x0020 and 0x0021).
+ *
+ * Return: 0 on success, or a negative error code on failure.
+ */
+static int genphy_c45_read_master_slave(struct phy_device *phydev)
+{
+ int val;
+
+ phydev->master_slave_get = MASTER_SLAVE_CFG_UNKNOWN;
+ phydev->master_slave_state = MASTER_SLAVE_STATE_UNKNOWN;
+
+ val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_CTRL);
+ if (val < 0)
+ return val;
+
+ if (val & MDIO_AN_10GBT_CTRL_MS_ENABLE) {
+ if (val & MDIO_AN_10GBT_CTRL_MS_VALUE)
+ phydev->master_slave_get = MASTER_SLAVE_CFG_MASTER_FORCE;
+ else
+ phydev->master_slave_get = MASTER_SLAVE_CFG_SLAVE_FORCE;
+ } else {
+ if (val & MDIO_AN_10GBT_CTRL_MS_PORT_TYPE)
+ phydev->master_slave_get = MASTER_SLAVE_CFG_MASTER_PREFERRED;
+ else
+ phydev->master_slave_get = MASTER_SLAVE_CFG_SLAVE_PREFERRED;
+ }
+
+ val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_STAT);
+ if (val < 0)
+ return val;
+
+ if (val & MDIO_AN_10GBT_STAT_MS_FAULT) {
+ phydev->master_slave_state = MASTER_SLAVE_STATE_ERR;
+ } else if (phydev->link) {
+ if (val & MDIO_AN_10GBT_STAT_MS_RES)
+ phydev->master_slave_state = MASTER_SLAVE_STATE_MASTER;
+ else
+ phydev->master_slave_state = MASTER_SLAVE_STATE_SLAVE;
+ } else {
+ phydev->master_slave_state = MASTER_SLAVE_STATE_UNKNOWN;
+ }
+
+ return 0;
+}
+
/**
* genphy_c45_aneg_done - return auto-negotiation complete status
* @phydev: target phy_device struct
@@ -1214,6 +1305,10 @@ int genphy_c45_read_status(struct phy_device *phydev)
ret = genphy_c45_baset1_read_status(phydev);
if (ret < 0)
return ret;
+ } else {
+ ret = genphy_c45_read_master_slave(phydev);
+ if (ret < 0)
+ return ret;
}
phy_resolve_aneg_linkmode(phydev);
@@ -1247,6 +1342,14 @@ int genphy_c45_config_aneg(struct phy_device *phydev)
if (ret > 0)
changed = true;
+ if (!genphy_c45_baset1_able(phydev)) {
+ ret = genphy_c45_an_setup_master_slave(phydev);
+ if (ret < 0)
+ return ret;
+ if (ret > 0)
+ changed = true;
+ }
+
return genphy_c45_check_and_restart_aneg(phydev, changed);
}
EXPORT_SYMBOL_GPL(genphy_c45_config_aneg);
diff --git a/include/uapi/linux/mdio.h b/include/uapi/linux/mdio.h
index b2541c948fc1..06f4bc3c20c7 100644
--- a/include/uapi/linux/mdio.h
+++ b/include/uapi/linux/mdio.h
@@ -332,8 +332,13 @@
#define MDIO_AN_10GBT_CTRL_ADV2_5G 0x0080 /* Advertise 2.5GBASE-T */
#define MDIO_AN_10GBT_CTRL_ADV5G 0x0100 /* Advertise 5GBASE-T */
#define MDIO_AN_10GBT_CTRL_ADV10G 0x1000 /* Advertise 10GBASE-T */
+#define MDIO_AN_10GBT_CTRL_MS_ENABLE 0x8000 /* Master/slave manual config enable */
+#define MDIO_AN_10GBT_CTRL_MS_VALUE 0x4000 /* Master/slave config value (1=Master) */
+#define MDIO_AN_10GBT_CTRL_MS_PORT_TYPE 0x2000 /* Master Preferred Type */
/* AN 10GBASE-T status register. */
+#define MDIO_AN_10GBT_STAT_MS_FAULT 0x8000 /* Master/slave fault */
+#define MDIO_AN_10GBT_STAT_MS_RES 0x4000 /* Master/slave resolution (1=Master) */
#define MDIO_AN_10GBT_STAT_LP2_5G 0x0020 /* LP is 2.5GBT capable */
#define MDIO_AN_10GBT_STAT_LP5G 0x0040 /* LP is 5GBT capable */
#define MDIO_AN_10GBT_STAT_LPTRR 0x0200 /* LP training reset req. */
--
2.43.0
^ permalink raw reply related
* [PATCH net-next v7 1/4] net: phy: c45: add genphy_c45_soft_reset()
From: javen @ 2026-06-29 6:47 UTC (permalink / raw)
To: andrew, hkallweit1, linux, davem, edumazet, kuba, pabeni,
freddy_gu, nb
Cc: netdev, linux-kernel, daniel, vladimir.oltean, nic_swsd, Javen Xu
In-Reply-To: <20260629064718.1349-1-javen_xu@realsil.com.cn>
From: Javen Xu <javen_xu@realsil.com.cn>
Add a generic Clause 45 software reset helper. The helper sets the reset
bit in the PMA/PMD control register and waits until the bit is cleared by
hardware.
Reviewed-by: Nicolai Buchwitz <nb@tipi-net.de>
Signed-off-by: Javen Xu <javen_xu@realsil.com.cn>
---
Changes in v2:
- no changes, new file
Changes in v3:
- re-order function according to the order in phy-c45.c
Changes in v4:
- no changes
Changes in v5:
- no changes
Changes in v6:
- increase timeout to 600ms
Changes in v7:
- no changes
---
drivers/net/phy/phy-c45.c | 22 ++++++++++++++++++++++
include/linux/phy.h | 1 +
2 files changed, 23 insertions(+)
diff --git a/drivers/net/phy/phy-c45.c b/drivers/net/phy/phy-c45.c
index 126951741428..60d044156a83 100644
--- a/drivers/net/phy/phy-c45.c
+++ b/drivers/net/phy/phy-c45.c
@@ -384,6 +384,28 @@ int genphy_c45_check_and_restart_aneg(struct phy_device *phydev, bool restart)
}
EXPORT_SYMBOL_GPL(genphy_c45_check_and_restart_aneg);
+/**
+ * genphy_c45_soft_reset - software reset the PHY via Clause 45 PMA/PMD control register
+ * @phydev: target phy_device struct
+ *
+ * Return: 0 on success, negative errno on failure.
+ */
+int genphy_c45_soft_reset(struct phy_device *phydev)
+{
+ int ret, val;
+
+ ret = phy_set_bits_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_CTRL1,
+ MDIO_CTRL1_RESET);
+ if (ret < 0)
+ return ret;
+
+ return phy_read_mmd_poll_timeout(phydev, MDIO_MMD_PMAPMD,
+ MDIO_CTRL1, val,
+ !(val & MDIO_CTRL1_RESET),
+ 5000, 600000, true);
+}
+EXPORT_SYMBOL_GPL(genphy_c45_soft_reset);
+
/**
* genphy_c45_aneg_done - return auto-negotiation complete status
* @phydev: target phy_device struct
diff --git a/include/linux/phy.h b/include/linux/phy.h
index 199a7aaa341b..25a66320df56 100644
--- a/include/linux/phy.h
+++ b/include/linux/phy.h
@@ -2309,6 +2309,7 @@ int genphy_c37_read_status(struct phy_device *phydev, bool *changed);
/* Clause 45 PHY */
int genphy_c45_restart_aneg(struct phy_device *phydev);
int genphy_c45_check_and_restart_aneg(struct phy_device *phydev, bool restart);
+int genphy_c45_soft_reset(struct phy_device *phydev);
int genphy_c45_aneg_done(struct phy_device *phydev);
int genphy_c45_read_link(struct phy_device *phydev);
int genphy_c45_read_lpa(struct phy_device *phydev);
--
2.43.0
^ permalink raw reply related
* [PATCH net-next v7 0/4] Add support for RTL8261C_CG
From: javen @ 2026-06-29 6:47 UTC (permalink / raw)
To: andrew, hkallweit1, linux, davem, edumazet, kuba, pabeni,
freddy_gu, nb
Cc: netdev, linux-kernel, daniel, vladimir.oltean, nic_swsd, Javen Xu
From: Javen Xu <javen_xu@realsil.com.cn>
Add support for RTL8261C_CG and add support for loading firmware.
Javen Xu (4):
net: phy: c45: add genphy_c45_soft_reset()
net: phy: c45: add setup and read master/slave helpers
net: phy: realtek: add support for RTL8261C_CG
net: phy: realtek: load firmware for RTL8261C_CG
drivers/net/phy/phy-c45.c | 125 ++++++++
drivers/net/phy/realtek/realtek_main.c | 406 +++++++++++++++++++++++++
include/linux/phy.h | 1 +
include/uapi/linux/mdio.h | 5 +
4 files changed, 537 insertions(+)
--
2.43.0
^ permalink raw reply
* [PATCH net] net/mlx5: HWS, fix matcher leak on resize target setup failure
From: Dawei Feng @ 2026-06-29 6:40 UTC (permalink / raw)
To: saeedm
Cc: leon, tariqt, mbloch, andrew+netdev, davem, edumazet, kuba,
pabeni, kliteyn, vdogaru, horms, kees, stable, netdev, linux-rdma,
linux-kernel, jianhao.xu, zilin, Dawei Feng
hws_bwc_matcher_move() allocates a replacement matcher before setting it
as the resize target. If mlx5hws_matcher_resize_set_target() fails, the
replacement matcher is not attached anywhere and is leaked.
Fix the leak by destroying the replacement matcher before returning from
the resize-target failure path.
The bug was first flagged by an experimental analysis tool we are
developing for kernel memory-management bugs while analyzing
v6.13-rc1. The tool is still under development and is not yet publicly
available. Manual inspection confirms that the bug is still
present in v7.1.1.
An x86_64 allyesconfig build showed no new warnings. As we do not have a
mlx5 HWS-capable device to test with, no runtime testing was able to be
performed.
Fixes: 2111bb970c78 ("net/mlx5: HWS, added backward-compatible API handling")
Cc: stable@vger.kernel.org
Signed-off-by: Dawei Feng <dawei.feng@seu.edu.cn>
---
drivers/net/ethernet/mellanox/mlx5/core/steering/hws/bwc.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/hws/bwc.c b/drivers/net/ethernet/mellanox/mlx5/core/steering/hws/bwc.c
index eae02bc74221..3bcf412a08c4 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/steering/hws/bwc.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/hws/bwc.c
@@ -205,6 +205,7 @@ static int hws_bwc_matcher_move(struct mlx5hws_bwc_matcher *bwc_matcher)
ret = mlx5hws_matcher_resize_set_target(old_matcher, new_matcher);
if (ret) {
mlx5hws_err(ctx, "Rehash error: failed setting resize target\n");
+ mlx5hws_matcher_destroy(new_matcher);
return ret;
}
--
2.34.1
^ permalink raw reply related
* [PATCH net-next v2] ipv6: honor per-interface proxy_ndp in forward and NA paths
From: Chenguang Zhao @ 2026-06-29 6:18 UTC (permalink / raw)
To: dsahern, idosch, davem, edumazet, kuba, pabeni
Cc: horms, netdev, Chenguang Zhao, Chenguang Zhao
In-Reply-To: <20260623085600.396401-1-zhaochenguang@kylinos.cn>
ndisc_recv_ns() has always checked both devconf_all and idev->cnf for
proxy_ndp, but ip6_forward() and ndisc_recv_na() only looked at the
global setting. The original commit left XXX comments in these paths
likely because idev was not available there at the time; ip6_forward()
now obtains idev from IP6CB(skb)->iif.
Honor per-interface proxy_ndp in both places to match the NS path and
allow setups that only enable proxy_ndp on specific interfaces.
In ip6_forward(), idev is looked up via the ingress interface (iif) while
pneigh_lookup() uses skb->dev. For ND packets this is correct because
vrf_ip6_rcv() does not modify skb->dev for neighbour discovery frames,
so both refer to the ingress interface.
Signed-off-by: Chenguang Zhao <zhaochenguang@kylinos.cn>
---
v2:
Per Ido's review, the following changes were made in v2:
- Target net-next instead of net
- Drop Fixes tag
- Expand commit message: XXX comment history, idev vs skb->dev for ND packets
- Fix subject prefix
v1:
- https://lore.kernel.org/all/20260623085600.396401-1-zhaochenguang@kylinos.cn/
net/ipv6/ip6_output.c | 4 ++--
net/ipv6/ndisc.c | 4 ++--
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
index 368e4fa3b43c..c4ca4a813479 100644
--- a/net/ipv6/ip6_output.c
+++ b/net/ipv6/ip6_output.c
@@ -579,8 +579,8 @@ int ip6_forward(struct sk_buff *skb)
return -ETIMEDOUT;
}
- /* XXX: idev->cnf.proxy_ndp? */
- if (READ_ONCE(net->ipv6.devconf_all->proxy_ndp) &&
+ if ((READ_ONCE(net->ipv6.devconf_all->proxy_ndp) ||
+ (idev && READ_ONCE(idev->cnf.proxy_ndp))) &&
pneigh_lookup(&nd_tbl, net, &hdr->daddr, skb->dev)) {
int proxied = ip6_forward_proxy_check(skb);
diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c
index f867ec8d3d90..e03e94681738 100644
--- a/net/ipv6/ndisc.c
+++ b/net/ipv6/ndisc.c
@@ -1096,9 +1096,9 @@ static enum skb_drop_reason ndisc_recv_na(struct sk_buff *skb)
*/
if (lladdr && !memcmp(lladdr, dev->dev_addr, dev->addr_len) &&
READ_ONCE(net->ipv6.devconf_all->forwarding) &&
- READ_ONCE(net->ipv6.devconf_all->proxy_ndp) &&
+ (READ_ONCE(net->ipv6.devconf_all->proxy_ndp) ||
+ (idev && READ_ONCE(idev->cnf.proxy_ndp))) &&
pneigh_lookup(&nd_tbl, net, &msg->target, dev)) {
- /* XXX: idev->cnf.proxy_ndp */
goto out;
}
--
2.25.1
^ permalink raw reply related
* [PATCH net-next v3 6/7] r8169: fix RTL8116af can not enter s0idle and c10
From: javen @ 2026-06-29 6:09 UTC (permalink / raw)
To: hkallweit1, nic_swsd, andrew+netdev, davem, edumazet, kuba,
pabeni, maxime.chevallier, horms
Cc: netdev, linux-kernel, Javen Xu
In-Reply-To: <20260629060931.1006-1-javen_xu@realsil.com.cn>
From: Javen Xu <javen_xu@realsil.com.cn>
RTL8116AF is a multi-function device. Functions 2 to 7 are hidden from
the PCI core and return an all-ones response when their vendor ID is read,
so they are not enumerated as normal PCI functions.
However, these hidden functions can still affect platform power
management. If they are left in D0 or keep ASPM disabled, the platform may
fail to enter the low-power s0ix state and the CPU package may fail to
enter Package C10.
Put functions 2 to 7 into D3hot and enable ASPM on their PCIe link control
register. Since these functions are hidden, access their configuration
space through pci_bus_read_config_dword() / pci_bus_write_config_dword()
using the same slot and the target function numbers.
Ignore functions that return a PCI error response when reading their
configuration space.
Signed-off-by: Javen Xu <javen_xu@realsil.com.cn>
---
Changes in v2:
- no changes
Changes in v3:
- no changes
---
drivers/net/ethernet/realtek/r8169_main.c | 33 +++++++++++++++++++++++
1 file changed, 33 insertions(+)
diff --git a/drivers/net/ethernet/realtek/r8169_main.c b/drivers/net/ethernet/realtek/r8169_main.c
index ba5e16087e03..223501479d20 100644
--- a/drivers/net/ethernet/realtek/r8169_main.c
+++ b/drivers/net/ethernet/realtek/r8169_main.c
@@ -356,6 +356,9 @@ enum rtl_registers {
#define LINK_SPEED_CHANGE_EN BIT(14)
#define LTR_SNOOP_EN GENMASK(15, 14)
#define LTR_MSG_EN BIT(0)
+#define RTL8116AF_FUNC_PM_CSR 0x80
+#define RTL8116AF_FUNC_EXP_LNKCTL 0x44
+#define RTL_PM_D3HOT GENMASK(1, 0)
};
enum rtl8168_8101_registers {
@@ -3783,6 +3786,35 @@ static void rtl_hw_start_8168ep_3(struct rtl8169_private *tp)
r8168_mac_ocp_modify(tp, 0xe860, 0x0000, 0x0080);
}
+static void rtl_disable_hidden_function(struct pci_dev *pdev)
+{
+ unsigned int slot = PCI_SLOT(pdev->devfn);
+ struct pci_bus *bus = pdev->bus;
+ unsigned int devfn;
+ int func;
+ int ret;
+ u32 val;
+
+ for (func = 2; func < 8; func++) {
+ devfn = PCI_DEVFN(slot, func);
+
+ ret = pci_bus_read_config_dword(bus, devfn, RTL8116AF_FUNC_PM_CSR, &val);
+ if (!ret && !PCI_POSSIBLE_ERROR(val)) {
+ val &= ~PCI_PM_CTRL_PME_STATUS;
+ val &= ~(PCI_PM_CTRL_STATE_MASK | PCI_PM_CTRL_PME_ENABLE);
+ val |= (RTL_PM_D3HOT | PCI_PM_CTRL_PME_ENABLE);
+ pci_bus_write_config_dword(bus, devfn, RTL8116AF_FUNC_PM_CSR, val);
+ }
+
+ ret = pci_bus_read_config_dword(bus, devfn, RTL8116AF_FUNC_EXP_LNKCTL, &val);
+ if (!ret && !PCI_POSSIBLE_ERROR(val)) {
+ val &= ~((PCI_EXP_LNKSTA_LBMS | PCI_EXP_LNKSTA_LABS) << 16);
+ val |= PCI_EXP_LNKCTL_ASPMC;
+ pci_bus_write_config_dword(bus, devfn, RTL8116AF_FUNC_EXP_LNKCTL, val);
+ }
+ }
+}
+
static void rtl_hw_start_8117(struct rtl8169_private *tp)
{
static const struct ephy_info e_info_8117[] = {
@@ -3839,6 +3871,7 @@ static void rtl_hw_start_8117(struct rtl8169_private *tp)
r8168_mac_ocp_write(tp, 0xc094, 0x0000);
r8168_mac_ocp_write(tp, 0xc09e, 0x0000);
+ rtl_disable_hidden_function(tp->pci_dev);
/* firmware is for MAC only */
r8169_apply_firmware(tp);
}
--
2.43.0
^ permalink raw reply related
* [PATCH net-next v3 2/7] r8169: create a virtual interrupt for linkchg
From: javen @ 2026-06-29 6:09 UTC (permalink / raw)
To: hkallweit1, nic_swsd, andrew+netdev, davem, edumazet, kuba,
pabeni, maxime.chevallier, horms
Cc: netdev, linux-kernel, Javen Xu
In-Reply-To: <20260629060931.1006-1-javen_xu@realsil.com.cn>
From: Javen Xu <javen_xu@realsil.com.cn>
Create a virtual interrupt for linkchg. To support phylink, we should
try to decouple most of tp->phydev, so we add virtual interrupt for mac
interrupt to inform the change of link status.
generic_handle_domain_irq() will help us to call phylib.
Signed-off-by: Javen Xu <javen_xu@realsil.com.cn>
---
Changes in v2:
- new file
Changes in v3:
- if irq_create_mapping fail, return -EINVAL
---
drivers/net/ethernet/realtek/r8169_main.c | 51 +++++++++++++++++++++--
1 file changed, 48 insertions(+), 3 deletions(-)
diff --git a/drivers/net/ethernet/realtek/r8169_main.c b/drivers/net/ethernet/realtek/r8169_main.c
index 43bc1456bf04..ee33f01af6df 100644
--- a/drivers/net/ethernet/realtek/r8169_main.c
+++ b/drivers/net/ethernet/realtek/r8169_main.c
@@ -21,6 +21,8 @@
#include <linux/in.h>
#include <linux/io.h>
#include <linux/ip.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
#include <linux/tcp.h>
#include <linux/interrupt.h>
#include <linux/dma-mapping.h>
@@ -775,6 +777,7 @@ struct rtl8169_private {
struct r8169_led_classdev *leds;
u32 ocp_base;
+ struct irq_domain *phy_irq_domain;
};
typedef void (*rtl_generic_fct)(struct rtl8169_private *tp);
@@ -4870,7 +4873,7 @@ static irqreturn_t rtl8169_interrupt(int irq, void *dev_instance)
}
if (status & LinkChg)
- phy_mac_interrupt(tp->phydev);
+ generic_handle_domain_irq(tp->phy_irq_domain, 0);
rtl_irq_disable(tp);
napi_schedule(&tp->napi);
@@ -5424,11 +5427,39 @@ static int r8169_mdio_write_reg_c45(struct mii_bus *mii_bus, int addr,
return 0;
}
+static int rtl_phy_irq_map(struct irq_domain *d, unsigned int irq,
+ irq_hw_number_t hwirq)
+{
+ irq_set_chip_and_handler(irq, &dummy_irq_chip, handle_simple_irq);
+ irq_set_chip_data(irq, d->host_data);
+
+ return 0;
+}
+
+static const struct irq_domain_ops rtl_phy_irq_domain_ops = {
+ .map = rtl_phy_irq_map,
+};
+
+static void rtl_phy_irq_cleanup(void *data)
+{
+ struct rtl8169_private *tp = data;
+ int virq;
+
+ if (tp->phy_irq_domain) {
+ virq = irq_find_mapping(tp->phy_irq_domain, 0);
+ if (virq)
+ irq_dispose_mapping(virq);
+
+ irq_domain_remove(tp->phy_irq_domain);
+ tp->phy_irq_domain = NULL;
+ }
+}
+
static int r8169_mdio_register(struct rtl8169_private *tp)
{
struct pci_dev *pdev = tp->pci_dev;
struct mii_bus *new_bus;
- int ret;
+ int ret, virq;
/* On some boards with this chip version the BIOS is buggy and misses
* to reset the PHY page selector. This results in the PHY ID read
@@ -5446,7 +5477,6 @@ static int r8169_mdio_register(struct rtl8169_private *tp)
new_bus->name = "r8169";
new_bus->priv = tp;
new_bus->parent = &pdev->dev;
- new_bus->irq[0] = PHY_MAC_INTERRUPT;
new_bus->phy_mask = GENMASK(31, 1);
snprintf(new_bus->id, MII_BUS_ID_SIZE, "r8169-%x-%x",
pci_domain_nr(pdev->bus), pci_dev_id(pdev));
@@ -5459,6 +5489,21 @@ static int r8169_mdio_register(struct rtl8169_private *tp)
new_bus->write_c45 = r8169_mdio_write_reg_c45;
}
+ tp->phy_irq_domain = irq_domain_add_linear(NULL, 1,
+ &rtl_phy_irq_domain_ops, tp);
+ if (!tp->phy_irq_domain)
+ return -ENOMEM;
+
+ ret = devm_add_action_or_reset(&pdev->dev, rtl_phy_irq_cleanup, tp);
+ if (ret)
+ return ret;
+
+ virq = irq_create_mapping(tp->phy_irq_domain, 0);
+ if (!virq)
+ return -EINVAL;
+
+ new_bus->irq[0] = virq;
+
ret = devm_mdiobus_register(&pdev->dev, new_bus);
if (ret)
return ret;
--
2.43.0
^ permalink raw reply related
* [PATCH net-next v3 5/7] r8169: add ltr support for RTL8116af
From: javen @ 2026-06-29 6:09 UTC (permalink / raw)
To: hkallweit1, nic_swsd, andrew+netdev, davem, edumazet, kuba,
pabeni, maxime.chevallier, horms
Cc: netdev, linux-kernel, Javen Xu
In-Reply-To: <20260629060931.1006-1-javen_xu@realsil.com.cn>
From: Javen Xu <javen_xu@realsil.com.cn>
This patch adds ltr support for RTL8116af, enables RTL8116af enter l1.2
state. This makes sense for the system to enter c10 state.
Signed-off-by: Javen Xu <javen_xu@realsil.com.cn>
---
Changes in v2:
- no changes
Changes in v3:
- no changes
---
drivers/net/ethernet/realtek/r8169_main.c | 21 ++++++++++++++++++++-
1 file changed, 20 insertions(+), 1 deletion(-)
diff --git a/drivers/net/ethernet/realtek/r8169_main.c b/drivers/net/ethernet/realtek/r8169_main.c
index 009095e9c706..ba5e16087e03 100644
--- a/drivers/net/ethernet/realtek/r8169_main.c
+++ b/drivers/net/ethernet/realtek/r8169_main.c
@@ -349,11 +349,13 @@ enum rtl_registers {
ALDPS_LTR = 0xe0a2,
LTR_OBFF_LOCK = 0xe032,
LTR_SNOOP = 0xe034,
+ SEND_LTR_MSG = 0xe038,
#define ALDPS_LTR_EN BIT(0)
#define LTR_OBFF_LOCK_EN BIT(0)
#define LINK_SPEED_CHANGE_EN BIT(14)
#define LTR_SNOOP_EN GENMASK(15, 14)
+#define LTR_MSG_EN BIT(0)
};
enum rtl8168_8101_registers {
@@ -3205,8 +3207,22 @@ static void rtl_enable_ltr(struct rtl8169_private *tp)
r8168_mac_ocp_write(tp, 0xcdf2, 0x9003);
r8168_mac_ocp_modify(tp, LTR_OBFF_LOCK, 0x0000, LINK_SPEED_CHANGE_EN);
break;
- case RTL_GIGA_MAC_VER_46 ... RTL_GIGA_MAC_VER_48:
case RTL_GIGA_MAC_VER_52:
+ r8168_mac_ocp_write(tp, 0xcdd0, 0x9003);
+ r8168_mac_ocp_modify(tp, LTR_SNOOP, 0x0000, LTR_SNOOP_EN);
+ r8168_mac_ocp_write(tp, 0xe02c, 0x1880);
+ r8168_mac_ocp_write(tp, 0xe02e, 0x4880);
+ r8168_mac_ocp_modify(tp, ALDPS_LTR, 0x0000, ALDPS_LTR_EN);
+ r8168_mac_ocp_write(tp, 0xcdd8, 0x9003);
+ r8168_mac_ocp_write(tp, 0xcdda, 0x9003);
+ r8168_mac_ocp_write(tp, 0xcddc, 0x9003);
+ r8168_mac_ocp_write(tp, 0xcdd2, 0x883c);
+ r8168_mac_ocp_write(tp, 0xcdd4, 0x8c12);
+ r8168_mac_ocp_write(tp, 0xcdd6, 0x9003);
+ r8168_mac_ocp_write(tp, 0xe0a6, 0x9003);
+ r8168_mac_ocp_write(tp, 0xe0a8, 0x9003);
+ break;
+ case RTL_GIGA_MAC_VER_46 ... RTL_GIGA_MAC_VER_48:
r8168_mac_ocp_modify(tp, ALDPS_LTR, 0x0000, ALDPS_LTR_EN);
RTL_W8(tp, COMBO_LTR_EXTEND, RTL_R8(tp, COMBO_LTR_EXTEND) | COMBO_LTR_EXTEND_EN);
fallthrough;
@@ -3226,6 +3242,7 @@ static void rtl_enable_ltr(struct rtl8169_private *tp)
}
/* chip can trigger LTR */
r8168_mac_ocp_modify(tp, LTR_OBFF_LOCK, 0x0003, LTR_OBFF_LOCK_EN);
+ r8168_mac_ocp_modify(tp, SEND_LTR_MSG, 0x0000, LTR_MSG_EN);
}
static void rtl_hw_aspm_clkreq_enable(struct rtl8169_private *tp, bool enable)
@@ -3259,6 +3276,7 @@ static void rtl_hw_aspm_clkreq_enable(struct rtl8169_private *tp, bool enable)
rtl_enable_ltr(tp);
switch (tp->mac_version) {
case RTL_GIGA_MAC_VER_46 ... RTL_GIGA_MAC_VER_48:
+ case RTL_GIGA_MAC_VER_52:
case RTL_GIGA_MAC_VER_61 ... RTL_GIGA_MAC_VER_LAST:
/* reset ephy tx/rx disable timer */
r8168_mac_ocp_modify(tp, 0xe094, 0xff00, 0);
@@ -3271,6 +3289,7 @@ static void rtl_hw_aspm_clkreq_enable(struct rtl8169_private *tp, bool enable)
} else {
switch (tp->mac_version) {
case RTL_GIGA_MAC_VER_46 ... RTL_GIGA_MAC_VER_48:
+ case RTL_GIGA_MAC_VER_52:
case RTL_GIGA_MAC_VER_61 ... RTL_GIGA_MAC_VER_LAST:
r8168_mac_ocp_modify(tp, 0xe092, 0x00ff, 0);
break;
--
2.43.0
^ permalink raw reply related
* [PATCH net-next v3 7/7] r8169: add phylink support for RTL8127atf
From: javen @ 2026-06-29 6:09 UTC (permalink / raw)
To: hkallweit1, nic_swsd, andrew+netdev, davem, edumazet, kuba,
pabeni, maxime.chevallier, horms
Cc: netdev, linux-kernel, Javen Xu
In-Reply-To: <20260629060931.1006-1-javen_xu@realsil.com.cn>
From: Javen Xu <javen_xu@realsil.com.cn>
RTL8127 ATF is a 10G SFP mode of RTL8127. Like RTL8116af, it has no
internal phy, so phylink uses a pcs to get the link status from the
serdes registers instead of standard phy registers.
The interface mode is PHY_INTERFACE_MODE_10GBASER. rtl_mac_select_pcs()
returns tp->pcs for it, and the serdes is configured for 10G in
pcs_config() via r8127_sfp_init_10g(). Speed and duplex are hardcoded
to 10Gbps Full-Duplex.
Per the datasheet, the 10G fiber does not support EEE/EEEP, RTD3, MAC
speed down, GPHY speed up, UPS or PFM mode, so EEE and runtime D3 are
disabled for RTL8127 ATF. UPS and PFM are already left disabled for this
chip, and the phy speed up/down paths are not reached because there is no
phydev.
Signed-off-by: Javen Xu <javen_xu@realsil.com.cn>
---
Changes in v3:
- No changes. New file.
---
drivers/net/ethernet/realtek/r8169_main.c | 84 ++++++++++-------------
1 file changed, 37 insertions(+), 47 deletions(-)
diff --git a/drivers/net/ethernet/realtek/r8169_main.c b/drivers/net/ethernet/realtek/r8169_main.c
index 223501479d20..6f4221079e8f 100644
--- a/drivers/net/ethernet/realtek/r8169_main.c
+++ b/drivers/net/ethernet/realtek/r8169_main.c
@@ -104,6 +104,7 @@
#define OCP_SDS_DATA_REG 0xEB14
#define SDS_CMD_READ 0x0001
#define RTL_SDS_C22_BASE 0x40
+#define RTL_SDS_C45_BASE 0x0080
#define RTL_PKG_DETECT 0xdc00
#define RTL_PKG_DETECT_MASK 0x0078
#define RTL_PKG_DETECT_8116AF 0x0030
@@ -1161,10 +1162,6 @@ static int r8168_phy_ocp_read(struct rtl8169_private *tp, u32 reg)
if (rtl_ocp_reg_failure(reg))
return 0;
- /* Return dummy MII_PHYSID2 in SFP mode to match SFP PHY driver */
- if (tp->sfp_mode == RTL_SFP_8127_ATF && reg == (OCP_STD_PHY_BASE + 2 * MII_PHYSID2))
- return PHY_ID_RTL_DUMMY_SFP & 0xffff;
-
RTL_W32(tp, GPHY_OCP, reg << 15);
return rtl_loop_wait_high(tp, &rtl_ocp_gphy_cond, 25, 10) ?
@@ -1250,12 +1247,6 @@ static void r8127_sfp_init_10g(struct rtl8169_private *tp)
r8168_phy_ocp_write(tp, 0xc804, (val & ~0x000f) | 0x000c);
}
-static void rtl_sfp_init(struct rtl8169_private *tp)
-{
- if (tp->mac_version == RTL_GIGA_MAC_VER_80)
- r8127_sfp_init_10g(tp);
-}
-
static void rtl_sfp_reset(struct rtl8169_private *tp)
{
if (tp->mac_version == RTL_GIGA_MAC_VER_80)
@@ -2445,31 +2436,8 @@ static int rtl8169_set_link_ksettings(struct net_device *ndev,
const struct ethtool_link_ksettings *cmd)
{
struct rtl8169_private *tp = netdev_priv(ndev);
- struct phy_device *phydev = tp->phydev;
- int duplex = cmd->base.duplex;
- int speed = cmd->base.speed;
-
- if (tp->sfp_mode != RTL_SFP_8127_ATF)
- return phylink_ethtool_ksettings_set(tp->phylink, cmd);
-
- if (cmd->base.autoneg != AUTONEG_DISABLE)
- return -EINVAL;
- if (!phy_check_valid(speed, duplex, phydev->supported))
- return -EINVAL;
-
- mutex_lock(&phydev->lock);
-
- phydev->autoneg = AUTONEG_DISABLE;
- phydev->speed = speed;
- phydev->duplex = duplex;
- tp->speed = speed;
-
- rtl_sfp_init(tp);
-
- mutex_unlock(&phydev->lock);
-
- return 0;
+ return phylink_ethtool_ksettings_set(tp->phylink, cmd);
}
static int rtl8169_nway_reset(struct net_device *dev)
@@ -2618,9 +2586,6 @@ static void rtl8169_init_phy(struct rtl8169_private *tp)
tp->pci_dev->subsystem_device == 0xe000)
phy_write_paged(tp->phydev, 0x0001, 0x10, 0xf01b);
- if (tp->sfp_mode == RTL_SFP_8127_ATF)
- rtl_sfp_init(tp);
-
/* We may have called phy_speed_down before */
phy_speed_up(tp->phydev);
@@ -5039,7 +5004,7 @@ static irqreturn_t rtl8169_interrupt(int irq, void *dev_instance)
if (status & LinkChg) {
if (tp->phy_irq_domain)
generic_handle_domain_irq(tp->phy_irq_domain, 0);
- else if (tp->sfp_mode == RTL_SFP_8168_AF)
+ else if (tp->sfp_mode)
phylink_mac_change(tp->phylink,
!!(RTL_R8(tp, PHYstatus) & LinkStatus));
}
@@ -5809,7 +5774,9 @@ static struct phylink_pcs *rtl_mac_select_pcs(struct phylink_config *config,
{
struct rtl8169_private *tp = container_of(config, struct rtl8169_private, phylink_config);
- if (interface == PHY_INTERFACE_MODE_1000BASEX || interface == PHY_INTERFACE_MODE_SGMII)
+ if (interface == PHY_INTERFACE_MODE_1000BASEX ||
+ interface == PHY_INTERFACE_MODE_SGMII ||
+ interface == PHY_INTERFACE_MODE_10GBASER)
return &tp->pcs;
return NULL;
}
@@ -5838,12 +5805,28 @@ static void rtl8169_pcs_get_state(struct phylink_pcs *pcs,
struct phylink_link_state *state)
{
struct rtl8169_private *tp = container_of(pcs, struct rtl8169_private, pcs);
- u16 bmsr, lpa;
- bmsr = rtl8169_sds_read(tp, RTL_SDS_C22_BASE + MII_BMSR);
- lpa = rtl8169_sds_read(tp, RTL_SDS_C22_BASE + MII_LPA);
+ if (tp->sfp_mode == RTL_SFP_8127_ATF) {
+ u16 stat1;
+
+ stat1 = rtl8169_sds_read(tp, RTL_SDS_C45_BASE + MDIO_STAT1);
+
+ if (!(stat1 & MDIO_STAT1_LSTATUS))
+ stat1 = rtl8169_sds_read(tp, RTL_SDS_C45_BASE + MDIO_STAT1);
+
+ state->link = !!(stat1 & MDIO_STAT1_LSTATUS);
+ if (!state->link)
+ return;
- phylink_mii_c22_pcs_decode_state(state, neg_mode, bmsr, lpa);
+ state->duplex = DUPLEX_FULL;
+ state->speed = SPEED_10000;
+ } else {
+ u16 bmsr, lpa;
+
+ bmsr = rtl8169_sds_read(tp, RTL_SDS_C22_BASE + MII_BMSR);
+ lpa = rtl8169_sds_read(tp, RTL_SDS_C22_BASE + MII_LPA);
+ phylink_mii_c22_pcs_decode_state(state, neg_mode, bmsr, lpa);
+ }
}
static int rtl8169_pcs_config(struct phylink_pcs *pcs, unsigned int mode,
@@ -5851,6 +5834,11 @@ static int rtl8169_pcs_config(struct phylink_pcs *pcs, unsigned int mode,
const unsigned long *advertising,
bool permit_pause_to_mac)
{
+ struct rtl8169_private *tp = container_of(pcs, struct rtl8169_private, pcs);
+
+ if (tp->sfp_mode == RTL_SFP_8127_ATF)
+ r8127_sfp_init_10g(tp);
+
return 0;
}
@@ -5896,7 +5884,7 @@ static unsigned long rtl8169_get_lpi_caps(struct rtl8169_private *tp)
{
unsigned long caps = 0;
- if (!rtl_supports_eee(tp))
+ if (!rtl_supports_eee(tp) || tp->sfp_mode == RTL_SFP_8127_ATF)
return 0;
caps |= MAC_100FD | MAC_1000FD;
@@ -5940,7 +5928,9 @@ static int rtl_init_phylink(struct rtl8169_private *tp)
tp->phylink_config.mac_capabilities |= MAC_1000FD;
break;
case RTL_SFP_8127_ATF:
- phy_mode = PHY_INTERFACE_MODE_INTERNAL;
+ tp->pcs.ops = &r8169_pcs_ops;
+ phy_mode = PHY_INTERFACE_MODE_10GBASER;
+ tp->phylink_config.default_an_inband = true;
tp->phylink_config.mac_capabilities |= MAC_10000FD;
break;
default:
@@ -6167,7 +6157,7 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
if (rc)
return rc;
- if (tp->sfp_mode != RTL_SFP_8168_AF) {
+ if (tp->sfp_mode == RTL_SFP_NONE) {
rc = r8169_mdio_register(tp);
if (rc) {
phylink_destroy(tp->phylink);
@@ -6202,7 +6192,7 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
rtl8168_driver_start(tp);
}
- if (pci_dev_run_wake(pdev))
+ if (pci_dev_run_wake(pdev) && tp->sfp_mode != RTL_SFP_8127_ATF)
pm_runtime_put_sync(&pdev->dev);
return 0;
--
2.43.0
^ permalink raw reply related
* [PATCH net-next v3 3/7] r8169: add support for phylink
From: javen @ 2026-06-29 6:09 UTC (permalink / raw)
To: hkallweit1, nic_swsd, andrew+netdev, davem, edumazet, kuba,
pabeni, maxime.chevallier, horms
Cc: netdev, linux-kernel, Javen Xu
In-Reply-To: <20260629060931.1006-1-javen_xu@realsil.com.cn>
From: Javen Xu <javen_xu@realsil.com.cn>
Transfer old framework to phylink. Phylink can support fiber mode card
which can not get link status or link speed from standard phy registers.
Signed-off-by: Javen Xu <javen_xu@realsil.com.cn>
---
Changes in v2:
- merge patch v1 3/6 and v1 4/6.
- add helper rtl_mac_enable_tx_lpi(), rtl_mac_disable_tx_lpi()
and rtl8169_get_lpi_caps()
Changes in v3:
- use phylink_ethtool_set_pauseparam to set pause status when change
mtu
- replace phy_do_ioctl_running with rtl8169_ioctl
- recover phy_mode according to tp->supports_gmii for 1G nics
---
drivers/net/ethernet/realtek/Kconfig | 1 +
drivers/net/ethernet/realtek/r8169_main.c | 310 +++++++++++++++++-----
2 files changed, 241 insertions(+), 70 deletions(-)
diff --git a/drivers/net/ethernet/realtek/Kconfig b/drivers/net/ethernet/realtek/Kconfig
index 9b0f4f9631db..49ac72734225 100644
--- a/drivers/net/ethernet/realtek/Kconfig
+++ b/drivers/net/ethernet/realtek/Kconfig
@@ -88,6 +88,7 @@ config R8169
select CRC32
select PHYLIB
select REALTEK_PHY
+ select PHYLINK
help
Say Y here if you have a Realtek Ethernet adapter belonging to
the following families:
diff --git a/drivers/net/ethernet/realtek/r8169_main.c b/drivers/net/ethernet/realtek/r8169_main.c
index ee33f01af6df..1d4a136519ab 100644
--- a/drivers/net/ethernet/realtek/r8169_main.c
+++ b/drivers/net/ethernet/realtek/r8169_main.c
@@ -28,6 +28,7 @@
#include <linux/dma-mapping.h>
#include <linux/pm_runtime.h>
#include <linux/bitfield.h>
+#include <linux/phylink.h>
#include <linux/prefetch.h>
#include <linux/ipv6.h>
#include <linux/unaligned.h>
@@ -777,7 +778,11 @@ struct rtl8169_private {
struct r8169_led_classdev *leds;
u32 ocp_base;
+ struct phylink *phylink;
+ struct phylink_config phylink_config;
struct irq_domain *phy_irq_domain;
+ struct ethtool_pauseparam saved_pause;
+ bool jumbo_pause_saved;
};
typedef void (*rtl_generic_fct)(struct rtl8169_private *tp);
@@ -2256,7 +2261,7 @@ static int rtl8169_get_eee(struct net_device *dev, struct ethtool_keee *data)
if (!rtl_supports_eee(tp))
return -EOPNOTSUPP;
- ret = phy_ethtool_get_eee(tp->phydev, data);
+ ret = phylink_ethtool_get_eee(tp->phylink, data);
if (ret)
return ret;
@@ -2272,7 +2277,7 @@ static int rtl8169_set_eee(struct net_device *dev, struct ethtool_keee *data)
if (!rtl_supports_eee(tp))
return -EOPNOTSUPP;
- return phy_ethtool_set_eee(tp->phydev, data);
+ return phylink_ethtool_set_eee(tp->phylink, data);
}
static void rtl8169_get_ringparam(struct net_device *dev,
@@ -2303,13 +2308,8 @@ static void rtl8169_get_pauseparam(struct net_device *dev,
struct ethtool_pauseparam *data)
{
struct rtl8169_private *tp = netdev_priv(dev);
- bool tx_pause, rx_pause;
- phy_get_pause(tp->phydev, &tx_pause, &rx_pause);
-
- data->autoneg = tp->phydev->autoneg;
- data->tx_pause = tx_pause ? 1 : 0;
- data->rx_pause = rx_pause ? 1 : 0;
+ phylink_ethtool_get_pauseparam(tp->phylink, data);
}
static int rtl8169_set_pauseparam(struct net_device *dev,
@@ -2320,9 +2320,7 @@ static int rtl8169_set_pauseparam(struct net_device *dev,
if (dev->mtu > ETH_DATA_LEN)
return -EOPNOTSUPP;
- phy_set_asym_pause(tp->phydev, data->rx_pause, data->tx_pause);
-
- return 0;
+ return phylink_ethtool_set_pauseparam(tp->phylink, data);
}
static void rtl8169_get_eth_mac_stats(struct net_device *dev,
@@ -2388,6 +2386,14 @@ static void rtl8169_get_eth_ctrl_stats(struct net_device *dev,
le32_to_cpu(tp->counters->rx_unknown_opcode);
}
+static int rtl8169_get_link_ksettings(struct net_device *ndev,
+ struct ethtool_link_ksettings *cmd)
+{
+ struct rtl8169_private *tp = netdev_priv(ndev);
+
+ return phylink_ethtool_ksettings_get(tp->phylink, cmd);
+}
+
static int rtl8169_set_link_ksettings(struct net_device *ndev,
const struct ethtool_link_ksettings *cmd)
{
@@ -2419,6 +2425,13 @@ static int rtl8169_set_link_ksettings(struct net_device *ndev,
return 0;
}
+static int rtl8169_nway_reset(struct net_device *dev)
+{
+ struct rtl8169_private *tp = netdev_priv(dev);
+
+ return phylink_ethtool_nway_reset(tp->phylink);
+}
+
static const struct ethtool_ops rtl8169_ethtool_ops = {
.supported_coalesce_params = ETHTOOL_COALESCE_USECS |
ETHTOOL_COALESCE_MAX_FRAMES,
@@ -2434,10 +2447,10 @@ static const struct ethtool_ops rtl8169_ethtool_ops = {
.get_sset_count = rtl8169_get_sset_count,
.get_ethtool_stats = rtl8169_get_ethtool_stats,
.get_ts_info = ethtool_op_get_ts_info,
- .nway_reset = phy_ethtool_nway_reset,
+ .nway_reset = rtl8169_nway_reset,
.get_eee = rtl8169_get_eee,
.set_eee = rtl8169_set_eee,
- .get_link_ksettings = phy_ethtool_get_link_ksettings,
+ .get_link_ksettings = rtl8169_get_link_ksettings,
.set_link_ksettings = rtl8169_set_link_ksettings,
.get_ringparam = rtl8169_get_ringparam,
.get_pause_stats = rtl8169_get_pause_stats,
@@ -2630,6 +2643,7 @@ static void rtl8169_init_ring_indexes(struct rtl8169_private *tp)
static void rtl_jumbo_config(struct rtl8169_private *tp)
{
bool jumbo = tp->dev->mtu > ETH_DATA_LEN;
+ struct ethtool_pauseparam pause;
int readrq = 4096;
if (jumbo && tp->mac_version >= RTL_GIGA_MAC_VER_17 &&
@@ -2661,13 +2675,48 @@ static void rtl_jumbo_config(struct rtl8169_private *tp)
if (pci_is_pcie(tp->pci_dev) && tp->supports_gmii)
pcie_set_readrq(tp->pci_dev, readrq);
- /* Chip doesn't support pause in jumbo mode */
if (jumbo) {
- linkmode_clear_bit(ETHTOOL_LINK_MODE_Pause_BIT,
- tp->phydev->advertising);
- linkmode_clear_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT,
- tp->phydev->advertising);
- phy_start_aneg(tp->phydev);
+ if (!tp->jumbo_pause_saved) {
+ struct ethtool_link_ksettings cmd;
+ bool adv_pause, adv_asym;
+
+ phylink_ethtool_get_pauseparam(tp->phylink, &tp->saved_pause);
+
+ if (tp->saved_pause.autoneg) {
+ phylink_ethtool_ksettings_get(tp->phylink, &cmd);
+ adv_pause = ethtool_link_ksettings_test_link_mode(&cmd,
+ advertising,
+ Pause);
+ adv_asym = ethtool_link_ksettings_test_link_mode(&cmd,
+ advertising,
+ Asym_Pause);
+
+ if (adv_pause && !adv_asym) {
+ tp->saved_pause.rx_pause = 1;
+ tp->saved_pause.tx_pause = 1;
+ } else if (adv_pause && adv_asym) {
+ tp->saved_pause.rx_pause = 1;
+ tp->saved_pause.tx_pause = 0;
+ } else if (!adv_pause && adv_asym) {
+ tp->saved_pause.rx_pause = 0;
+ tp->saved_pause.tx_pause = 1;
+ } else {
+ tp->saved_pause.rx_pause = 0;
+ tp->saved_pause.tx_pause = 0;
+ }
+ }
+
+ tp->jumbo_pause_saved = true;
+ }
+
+ pause.autoneg = tp->saved_pause.autoneg;
+ pause.rx_pause = 0;
+ pause.tx_pause = 0;
+
+ phylink_ethtool_set_pauseparam(tp->phylink, &pause);
+ } else if (tp->jumbo_pause_saved) {
+ phylink_ethtool_set_pauseparam(tp->phylink, &tp->saved_pause);
+ tp->jumbo_pause_saved = false;
}
}
@@ -2783,7 +2832,7 @@ static void rtl_prepare_power_down(struct rtl8169_private *tp)
rtl_ephy_write(tp, 0x19, 0xff64);
if (device_may_wakeup(tp_to_dev(tp))) {
- phy_speed_down(tp->phydev, false);
+ phylink_speed_down(tp->phylink, false);
rtl_wol_enable_rx(tp);
}
}
@@ -2835,6 +2884,16 @@ static void rtl8169_set_magic_reg(struct rtl8169_private *tp)
RTL_W32(tp, 0x7c, val);
}
+static int rtl8169_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+ struct rtl8169_private *tp = netdev_priv(dev);
+
+ if (!netif_running(dev))
+ return -ENODEV;
+
+ return phylink_mii_ioctl(tp->phylink, ifr, cmd);
+}
+
static void rtl_set_rx_mode(struct net_device *dev)
{
u32 rx_mode = AcceptBroadcast | AcceptMyPhys | AcceptMulticast;
@@ -4143,11 +4202,17 @@ static int rtl8169_change_mtu(struct net_device *dev, int new_mtu)
{
struct rtl8169_private *tp = netdev_priv(dev);
+ if (netif_running(dev))
+ phylink_stop(tp->phylink);
+
WRITE_ONCE(dev->mtu, new_mtu);
netdev_update_features(dev);
rtl_jumbo_config(tp);
rtl_set_eee_txidle_timer(tp);
+ if (netif_running(dev))
+ phylink_start(tp->phylink);
+
return 0;
}
@@ -4933,9 +4998,6 @@ static int rtl8169_poll(struct napi_struct *napi, int budget)
static void rtl_enable_tx_lpi(struct rtl8169_private *tp, bool enable)
{
- if (!rtl_supports_eee(tp))
- return;
-
switch (tp->mac_version) {
case RTL_GIGA_MAC_VER_34 ... RTL_GIGA_MAC_VER_52:
/* Adjust EEE LED frequency */
@@ -4966,41 +5028,15 @@ static void rtl_enable_tx_lpi(struct rtl8169_private *tp, bool enable)
}
}
-static void r8169_phylink_handler(struct net_device *ndev)
-{
- struct rtl8169_private *tp = netdev_priv(ndev);
- struct device *d = tp_to_dev(tp);
-
- tp->speed = tp->phydev->speed;
- if (netif_carrier_ok(ndev)) {
- rtl_link_chg_patch(tp, tp->speed);
- rtl_enable_tx_lpi(tp, tp->phydev->enable_tx_lpi);
- pm_request_resume(d);
- } else {
- pm_runtime_idle(d);
- }
-
- phy_print_status(tp->phydev);
-}
-
static int r8169_phy_connect(struct rtl8169_private *tp)
{
- struct phy_device *phydev = tp->phydev;
- phy_interface_t phy_mode;
int ret;
- phy_mode = tp->supports_gmii ? PHY_INTERFACE_MODE_GMII :
- PHY_INTERFACE_MODE_MII;
-
- ret = phy_connect_direct(tp->dev, phydev, r8169_phylink_handler,
- phy_mode);
- if (ret)
+ ret = phylink_connect_phy(tp->phylink, tp->phydev);
+ if (ret) {
+ netdev_err(tp->dev, "failed to connect phy\n");
return ret;
-
- if (!tp->supports_gmii)
- phy_set_max_speed(phydev, SPEED_100);
-
- phy_attached_info(phydev);
+ }
return 0;
}
@@ -5011,7 +5047,7 @@ static void rtl8169_down(struct rtl8169_private *tp)
/* Clear all task flags */
bitmap_zero(tp->wk.flags, RTL_FLAG_MAX);
- phy_stop(tp->phydev);
+ phylink_stop(tp->phylink);
/* Reset SerDes PHY to bring down fiber link */
if (tp->sfp_mode)
@@ -5043,7 +5079,7 @@ static void rtl8169_up(struct rtl8169_private *tp)
enable_work(&tp->wk.work);
rtl_reset_work(tp);
- phy_start(tp->phydev);
+ phylink_start(tp->phylink);
}
static int rtl8169_close(struct net_device *dev)
@@ -5059,7 +5095,7 @@ static int rtl8169_close(struct net_device *dev)
free_irq(tp->irq, tp);
- phy_disconnect(tp->phydev);
+ phylink_disconnect_phy(tp->phylink);
dma_free_coherent(&pdev->dev, R8169_RX_RING_BYTES, tp->RxDescArray,
tp->RxPhyAddr);
@@ -5194,8 +5230,11 @@ static int rtl8169_runtime_resume(struct device *dev)
rtl_rar_set(tp, tp->dev->dev_addr);
__rtl8169_set_wol(tp, tp->saved_wolopts);
- if (tp->TxDescArray)
+ if (tp->TxDescArray) {
+ rtnl_lock();
rtl8169_up(tp);
+ rtnl_unlock();
+ }
netif_device_attach(tp->dev);
@@ -5292,6 +5331,8 @@ static void rtl_remove_one(struct pci_dev *pdev)
r8169_remove_leds(tp->leds);
unregister_netdev(tp->dev);
+ if (tp->phylink)
+ phylink_destroy(tp->phylink);
if (tp->dash_type != RTL_DASH_NONE)
rtl8168_driver_stop(tp);
@@ -5314,7 +5355,7 @@ static const struct net_device_ops rtl_netdev_ops = {
.ndo_fix_features = rtl8169_fix_features,
.ndo_set_features = rtl8169_set_features,
.ndo_set_mac_address = rtl_set_mac_address,
- .ndo_eth_ioctl = phy_do_ioctl_running,
+ .ndo_eth_ioctl = rtl8169_ioctl,
.ndo_set_rx_mode = rtl_set_rx_mode,
#ifdef CONFIG_NET_POLL_CONTROLLER
.ndo_poll_controller = rtl8169_netpoll,
@@ -5520,16 +5561,6 @@ static int r8169_mdio_register(struct rtl8169_private *tp)
return -EUNATCH;
}
- tp->phydev->mac_managed_pm = true;
- if (rtl_supports_eee(tp))
- phy_support_eee(tp->phydev);
- phy_support_asym_pause(tp->phydev);
-
- /* mimic behavior of r8125/r8126 vendor drivers */
- if (tp->mac_version == RTL_GIGA_MAC_VER_61)
- phy_disable_eee_mode(tp->phydev,
- ETHTOOL_LINK_MODE_2500baseT_Full_BIT);
-
/* PHY will be woken up in rtl_open() */
phy_suspend(tp->phydev);
@@ -5645,6 +5676,137 @@ static bool rtl_aspm_is_safe(struct rtl8169_private *tp)
return false;
}
+static void rtl_mac_link_down(struct phylink_config *config, unsigned int mode,
+ phy_interface_t interface)
+{
+ struct rtl8169_private *tp = container_of(config, struct rtl8169_private, phylink_config);
+
+ tp->speed = SPEED_UNKNOWN;
+ pm_runtime_idle(tp_to_dev(tp));
+}
+
+static void rtl_mac_link_up(struct phylink_config *config, struct phy_device *phydev,
+ unsigned int mode, phy_interface_t interface,
+ int speed, int duplex, bool tx_pause, bool rx_pause)
+{
+ struct rtl8169_private *tp = container_of(config, struct rtl8169_private, phylink_config);
+ struct device *d = tp_to_dev(tp);
+
+ tp->speed = speed;
+ rtl_link_chg_patch(tp, speed);
+
+ pm_request_resume(d);
+}
+
+static struct phylink_pcs *rtl_mac_select_pcs(struct phylink_config *config,
+ phy_interface_t interface)
+{
+ return NULL;
+}
+
+static void rtl_mac_config(struct phylink_config *config, unsigned int mode,
+ const struct phylink_link_state *state)
+{
+}
+
+static void rtl_mac_disable_tx_lpi(struct phylink_config *config)
+{
+ struct rtl8169_private *tp = container_of(config, struct rtl8169_private, phylink_config);
+
+ rtl_enable_tx_lpi(tp, false);
+}
+
+static int rtl_mac_enable_tx_lpi(struct phylink_config *config, u32 timer, bool tx_clk_stop)
+{
+ struct rtl8169_private *tp = container_of(config, struct rtl8169_private, phylink_config);
+
+ if (!rtl_supports_eee(tp))
+ return -EOPNOTSUPP;
+
+ rtl_enable_tx_lpi(tp, true);
+
+ return 0;
+}
+
+static const struct phylink_mac_ops rtl_phylink_mac_ops = {
+ .mac_select_pcs = rtl_mac_select_pcs,
+ .mac_config = rtl_mac_config,
+ .mac_link_down = rtl_mac_link_down,
+ .mac_link_up = rtl_mac_link_up,
+ .mac_disable_tx_lpi = rtl_mac_disable_tx_lpi,
+ .mac_enable_tx_lpi = rtl_mac_enable_tx_lpi,
+};
+
+static unsigned long rtl8169_get_lpi_caps(struct rtl8169_private *tp)
+{
+ unsigned long caps = 0;
+
+ if (!rtl_supports_eee(tp))
+ return 0;
+
+ caps |= MAC_100FD | MAC_1000FD;
+
+ /* mimic behavior of r8125/r8126 vendor drivers
+ * RTL_GIGA_MAC_VER_61 doesn't support 2.5G eee
+ */
+ if (tp->mac_version >= RTL_GIGA_MAC_VER_63)
+ caps |= MAC_2500FD;
+ if (tp->mac_version >= RTL_GIGA_MAC_VER_70)
+ caps |= MAC_5000FD;
+ if (tp->mac_version == RTL_GIGA_MAC_VER_80)
+ caps |= MAC_10000FD;
+
+ return caps;
+}
+
+static int rtl_init_phylink(struct rtl8169_private *tp)
+{
+ struct phylink *pl;
+ phy_interface_t phy_mode;
+
+ tp->phylink_config.dev = &tp->dev->dev;
+ tp->phylink_config.type = PHYLINK_NETDEV;
+ tp->phylink_config.mac_managed_pm = true;
+ tp->phylink_config.lpi_capabilities = rtl8169_get_lpi_caps(tp);
+ tp->phylink_config.mac_capabilities |= MAC_ASYM_PAUSE | MAC_SYM_PAUSE;
+
+ if (tp->sfp_mode) {
+ phy_mode = PHY_INTERFACE_MODE_INTERNAL;
+ tp->phylink_config.mac_capabilities |= MAC_10000FD;
+ } else {
+ phy_mode = PHY_INTERFACE_MODE_INTERNAL;
+ tp->phylink_config.mac_capabilities |= MAC_10 | MAC_100;
+
+ if (tp->mac_version == RTL_GIGA_MAC_VER_80)
+ tp->phylink_config.mac_capabilities |= MAC_1000FD | MAC_2500FD |
+ MAC_5000FD | MAC_10000FD;
+ else if (tp->mac_version == RTL_GIGA_MAC_VER_70)
+ tp->phylink_config.mac_capabilities |= MAC_1000FD |
+ MAC_2500FD | MAC_5000FD;
+ else if (tp->mac_version >= RTL_GIGA_MAC_VER_61)
+ tp->phylink_config.mac_capabilities |= MAC_1000FD | MAC_2500FD;
+ else
+ if (tp->supports_gmii)
+ tp->phylink_config.mac_capabilities |= MAC_1000FD;
+
+ if (tp->mac_version < RTL_GIGA_MAC_VER_61)
+ phy_mode = tp->supports_gmii ? PHY_INTERFACE_MODE_GMII :
+ PHY_INTERFACE_MODE_MII;
+ else
+ phy_mode = PHY_INTERFACE_MODE_INTERNAL;
+ }
+
+ __set_bit(phy_mode, tp->phylink_config.supported_interfaces);
+ pl = phylink_create(&tp->phylink_config, tp_to_dev(tp)->fwnode,
+ phy_mode, &rtl_phylink_mac_ops);
+ if (IS_ERR(pl))
+ return PTR_ERR(pl);
+
+ tp->phylink = pl;
+
+ return 0;
+}
+
static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
{
const struct rtl_chip_info *chip;
@@ -5835,13 +5997,21 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
pci_set_drvdata(pdev, tp);
- rc = r8169_mdio_register(tp);
+ rc = rtl_init_phylink(tp);
if (rc)
return rc;
+ rc = r8169_mdio_register(tp);
+ if (rc) {
+ phylink_destroy(tp->phylink);
+ return rc;
+ }
+
rc = register_netdev(dev);
- if (rc)
+ if (rc) {
+ phylink_destroy(tp->phylink);
return rc;
+ }
if (IS_ENABLED(CONFIG_R8169_LEDS)) {
if (rtl_is_8125(tp))
--
2.43.0
^ permalink raw reply related
* [PATCH net-next v3 4/7] r8169: add support for RTL8116af
From: javen @ 2026-06-29 6:09 UTC (permalink / raw)
To: hkallweit1, nic_swsd, andrew+netdev, davem, edumazet, kuba,
pabeni, maxime.chevallier, horms
Cc: netdev, linux-kernel, Javen Xu
In-Reply-To: <20260629060931.1006-1-javen_xu@realsil.com.cn>
From: Javen Xu <javen_xu@realsil.com.cn>
RTL8116af is sfp mode. Phylink uses pcs to get the link status from its
serdes reg, instead of standard phy reg. Speed and duplex are hardcoded
to 1000Mbps Full-Duplex. Also, RTL8116af doesn't have internal phy, so
we add some checks to ensure that tp->phydev is not empty when we need it.
In rtl_hw_start_8117(), the MAC calibration for register 0xd412 relies
on reading the internal PHY register 0x0c42. Since RTL8116af does not
have an internal PHY, this calibration step is intentionally bypassed.
Signed-off-by: Javen Xu <javen_xu@realsil.com.cn>
---
Changes in v2:
- replace some magic numbers with macro
Changes in v3:
- change commit message
- add lock when we do rtl8169_sds_read
- use phylink_mii_c22_pcs_decode_state to get status
- add phylink_mac_change for RTL8116af for it doesn't have phy
---
drivers/net/ethernet/realtek/r8169_main.c | 184 ++++++++++++++++++----
1 file changed, 150 insertions(+), 34 deletions(-)
diff --git a/drivers/net/ethernet/realtek/r8169_main.c b/drivers/net/ethernet/realtek/r8169_main.c
index 1d4a136519ab..009095e9c706 100644
--- a/drivers/net/ethernet/realtek/r8169_main.c
+++ b/drivers/net/ethernet/realtek/r8169_main.c
@@ -99,6 +99,18 @@
#define JUMBO_9K (9 * SZ_1K - VLAN_ETH_HLEN - ETH_FCS_LEN)
#define JUMBO_16K (SZ_16K - VLAN_ETH_HLEN - ETH_FCS_LEN)
+#define OCP_SDS_ADDR_REG 0xEB10
+#define OCP_SDS_CMD_REG 0xEB0E
+#define OCP_SDS_DATA_REG 0xEB14
+#define SDS_CMD_READ 0x0001
+#define RTL_SDS_C22_BASE 0x40
+#define RTL_PKG_DETECT 0xdc00
+#define RTL_PKG_DETECT_MASK 0x0078
+#define RTL_PKG_DETECT_8116AF 0x0030
+#define RTL_INT_HW_ID 0xd006
+#define RTL_INT_HW_ID_MASK 0x00ff
+#define RTL_INT_HW_ID_8116AF 0x0000
+
static const struct rtl_chip_info {
u32 mask;
u32 val;
@@ -731,6 +743,12 @@ enum rtl_dash_type {
RTL_DASH_25_BP,
};
+enum rtl_sfp_mode {
+ RTL_SFP_NONE,
+ RTL_SFP_8168_AF,
+ RTL_SFP_8127_ATF,
+};
+
struct rtl8169_private {
void __iomem *mmio_addr; /* memory map physical address */
struct pci_dev *pci_dev;
@@ -739,6 +757,7 @@ struct rtl8169_private {
struct napi_struct napi;
enum mac_version mac_version;
enum rtl_dash_type dash_type;
+ enum rtl_sfp_mode sfp_mode;
u32 cur_rx; /* Index into the Rx descriptor buffer of next Rx pkt. */
u32 cur_tx; /* Index into the Tx descriptor buffer of next Rx pkt. */
u32 dirty_tx;
@@ -766,7 +785,6 @@ struct rtl8169_private {
unsigned supports_gmii:1;
unsigned aspm_manageable:1;
unsigned dash_enabled:1;
- bool sfp_mode:1;
dma_addr_t counters_phys_addr;
struct rtl8169_counters *counters;
struct rtl8169_tc_offsets tc_offset;
@@ -780,6 +798,7 @@ struct rtl8169_private {
u32 ocp_base;
struct phylink *phylink;
struct phylink_config phylink_config;
+ struct phylink_pcs pcs;
struct irq_domain *phy_irq_domain;
struct ethtool_pauseparam saved_pause;
bool jumbo_pause_saved;
@@ -1138,7 +1157,7 @@ static int r8168_phy_ocp_read(struct rtl8169_private *tp, u32 reg)
return 0;
/* Return dummy MII_PHYSID2 in SFP mode to match SFP PHY driver */
- if (tp->sfp_mode && reg == (OCP_STD_PHY_BASE + 2 * MII_PHYSID2))
+ if (tp->sfp_mode == RTL_SFP_8127_ATF && reg == (OCP_STD_PHY_BASE + 2 * MII_PHYSID2))
return PHY_ID_RTL_DUMMY_SFP & 0xffff;
RTL_W32(tp, GPHY_OCP, reg << 15);
@@ -1292,6 +1311,15 @@ static void mac_mcu_write(struct rtl8169_private *tp, int reg, int value)
r8168_mac_ocp_write(tp, tp->ocp_base + reg, value);
}
+static bool rtl_is_8116af(struct rtl8169_private *tp)
+{
+ return tp->mac_version == RTL_GIGA_MAC_VER_52 &&
+ (r8168_mac_ocp_read(tp, RTL_PKG_DETECT) & RTL_PKG_DETECT_MASK) ==
+ RTL_PKG_DETECT_8116AF &&
+ (r8168_mac_ocp_read(tp, RTL_INT_HW_ID) & RTL_INT_HW_ID_MASK) ==
+ RTL_INT_HW_ID_8116AF;
+}
+
static int mac_mcu_read(struct rtl8169_private *tp, int reg)
{
return r8168_mac_ocp_read(tp, tp->ocp_base + reg);
@@ -1587,6 +1615,20 @@ static bool rtl_dash_is_enabled(struct rtl8169_private *tp)
}
}
+static enum rtl_sfp_mode rtl_get_sfp_mode(struct rtl8169_private *tp)
+{
+ if (rtl_is_8125(tp)) {
+ u16 data = r8168_mac_ocp_read(tp, RTL_INT_HW_ID);
+
+ if ((data & 0xff) == 0x07)
+ return RTL_SFP_8127_ATF;
+ } else if (rtl_is_8116af(tp)) {
+ return RTL_SFP_8168_AF;
+ }
+
+ return RTL_SFP_NONE;
+}
+
static enum rtl_dash_type rtl_get_dash_type(struct rtl8169_private *tp)
{
switch (tp->mac_version) {
@@ -2402,8 +2444,8 @@ static int rtl8169_set_link_ksettings(struct net_device *ndev,
int duplex = cmd->base.duplex;
int speed = cmd->base.speed;
- if (!tp->sfp_mode)
- return phy_ethtool_ksettings_set(phydev, cmd);
+ if (tp->sfp_mode != RTL_SFP_8127_ATF)
+ return phylink_ethtool_ksettings_set(tp->phylink, cmd);
if (cmd->base.autoneg != AUTONEG_DISABLE)
return -EINVAL;
@@ -2515,9 +2557,10 @@ void r8169_apply_firmware(struct rtl8169_private *tp)
tp->ocp_base = OCP_STD_PHY_BASE;
/* PHY soft reset may still be in progress */
- phy_read_poll_timeout(tp->phydev, MII_BMCR, val,
- !(val & BMCR_RESET),
- 50000, 600000, true);
+ if (tp->phydev)
+ phy_read_poll_timeout(tp->phydev, MII_BMCR, val,
+ !(val & BMCR_RESET),
+ 50000, 600000, true);
}
}
@@ -2554,6 +2597,8 @@ static void rtl_schedule_task(struct rtl8169_private *tp, enum rtl_flag flag)
static void rtl8169_init_phy(struct rtl8169_private *tp)
{
+ phy_init_hw(tp->phydev);
+ phy_resume(tp->phydev);
r8169_hw_phy_config(tp, tp->phydev, tp->mac_version);
if (tp->mac_version <= RTL_GIGA_MAC_VER_06) {
@@ -2568,7 +2613,7 @@ static void rtl8169_init_phy(struct rtl8169_private *tp)
tp->pci_dev->subsystem_device == 0xe000)
phy_write_paged(tp->phydev, 0x0001, 0x10, 0xf01b);
- if (tp->sfp_mode)
+ if (tp->sfp_mode == RTL_SFP_8127_ATF)
rtl_sfp_init(tp);
/* We may have called phy_speed_down before */
@@ -3755,12 +3800,14 @@ static void rtl_hw_start_8117(struct rtl8169_private *tp)
rtl_pcie_state_l2l3_disable(tp);
- rg_saw_cnt = phy_read_paged(tp->phydev, 0x0c42, 0x13) & 0x3fff;
- if (rg_saw_cnt > 0) {
- u16 sw_cnt_1ms_ini;
+ if (tp->phydev) {
+ rg_saw_cnt = phy_read_paged(tp->phydev, 0x0c42, 0x13) & 0x3fff;
+ if (rg_saw_cnt > 0) {
+ u16 sw_cnt_1ms_ini;
- sw_cnt_1ms_ini = (16000000 / rg_saw_cnt) & 0x0fff;
- r8168_mac_ocp_modify(tp, 0xd412, 0x0fff, sw_cnt_1ms_ini);
+ sw_cnt_1ms_ini = (16000000 / rg_saw_cnt) & 0x0fff;
+ r8168_mac_ocp_modify(tp, 0xd412, 0x0fff, sw_cnt_1ms_ini);
+ }
}
r8168_mac_ocp_modify(tp, 0xe056, 0x00f0, 0x0000);
@@ -4937,8 +4984,13 @@ static irqreturn_t rtl8169_interrupt(int irq, void *dev_instance)
goto out;
}
- if (status & LinkChg)
- generic_handle_domain_irq(tp->phy_irq_domain, 0);
+ if (status & LinkChg) {
+ if (tp->phy_irq_domain)
+ generic_handle_domain_irq(tp->phy_irq_domain, 0);
+ else if (tp->sfp_mode == RTL_SFP_8168_AF)
+ phylink_mac_change(tp->phylink,
+ !!(RTL_R8(tp, PHYstatus) & LinkStatus));
+ }
rtl_irq_disable(tp);
napi_schedule(&tp->napi);
@@ -5050,7 +5102,7 @@ static void rtl8169_down(struct rtl8169_private *tp)
phylink_stop(tp->phylink);
/* Reset SerDes PHY to bring down fiber link */
- if (tp->sfp_mode)
+ if (tp->sfp_mode == RTL_SFP_8127_ATF)
rtl_sfp_reset(tp);
rtl8169_update_counters(tp);
@@ -5072,9 +5124,9 @@ static void rtl8169_up(struct rtl8169_private *tp)
rtl8168_driver_start(tp);
pci_set_master(tp->pci_dev);
- phy_init_hw(tp->phydev);
- phy_resume(tp->phydev);
- rtl8169_init_phy(tp);
+ if (tp->phydev)
+ rtl8169_init_phy(tp);
+
napi_enable(&tp->napi);
enable_work(&tp->wk.work);
rtl_reset_work(tp);
@@ -5152,9 +5204,11 @@ static int rtl_open(struct net_device *dev)
if (retval < 0)
goto err_release_fw_2;
- retval = r8169_phy_connect(tp);
- if (retval)
- goto err_free_irq;
+ if (tp->phydev) {
+ retval = r8169_phy_connect(tp);
+ if (retval)
+ goto err_free_irq;
+ }
rtl8169_up(tp);
rtl8169_init_counter_offsets(tp);
@@ -5701,6 +5755,10 @@ static void rtl_mac_link_up(struct phylink_config *config, struct phy_device *ph
static struct phylink_pcs *rtl_mac_select_pcs(struct phylink_config *config,
phy_interface_t interface)
{
+ struct rtl8169_private *tp = container_of(config, struct rtl8169_private, phylink_config);
+
+ if (interface == PHY_INTERFACE_MODE_1000BASEX || interface == PHY_INTERFACE_MODE_SGMII)
+ return &tp->pcs;
return NULL;
}
@@ -5709,6 +5767,51 @@ static void rtl_mac_config(struct phylink_config *config, unsigned int mode,
{
}
+static u16 rtl8169_sds_read(struct rtl8169_private *tp, u16 sds_reg)
+{
+ unsigned long flags;
+ u16 val = 0;
+
+ raw_spin_lock_irqsave(&tp->mac_ocp_lock, flags);
+ __r8168_mac_ocp_write(tp, OCP_SDS_ADDR_REG, sds_reg);
+ __r8168_mac_ocp_write(tp, OCP_SDS_CMD_REG, SDS_CMD_READ);
+ val = __r8168_mac_ocp_read(tp, OCP_SDS_DATA_REG);
+ raw_spin_unlock_irqrestore(&tp->mac_ocp_lock, flags);
+
+ return val;
+}
+
+static void rtl8169_pcs_get_state(struct phylink_pcs *pcs,
+ unsigned int neg_mode,
+ struct phylink_link_state *state)
+{
+ struct rtl8169_private *tp = container_of(pcs, struct rtl8169_private, pcs);
+ u16 bmsr, lpa;
+
+ bmsr = rtl8169_sds_read(tp, RTL_SDS_C22_BASE + MII_BMSR);
+ lpa = rtl8169_sds_read(tp, RTL_SDS_C22_BASE + MII_LPA);
+
+ phylink_mii_c22_pcs_decode_state(state, neg_mode, bmsr, lpa);
+}
+
+static int rtl8169_pcs_config(struct phylink_pcs *pcs, unsigned int mode,
+ phy_interface_t interface,
+ const unsigned long *advertising,
+ bool permit_pause_to_mac)
+{
+ return 0;
+}
+
+static int rtl8169_pcs_validate(struct phylink_pcs *pcs, unsigned long *supported,
+ const struct phylink_link_state *state)
+{
+ return 0;
+}
+
+static void rtl8169_pcs_an_restart(struct phylink_pcs *pcs)
+{
+}
+
static void rtl_mac_disable_tx_lpi(struct phylink_config *config)
{
struct rtl8169_private *tp = container_of(config, struct rtl8169_private, phylink_config);
@@ -5759,6 +5862,13 @@ static unsigned long rtl8169_get_lpi_caps(struct rtl8169_private *tp)
return caps;
}
+static const struct phylink_pcs_ops r8169_pcs_ops = {
+ .pcs_validate = rtl8169_pcs_validate,
+ .pcs_get_state = rtl8169_pcs_get_state,
+ .pcs_config = rtl8169_pcs_config,
+ .pcs_an_restart = rtl8169_pcs_an_restart,
+};
+
static int rtl_init_phylink(struct rtl8169_private *tp)
{
struct phylink *pl;
@@ -5770,10 +5880,18 @@ static int rtl_init_phylink(struct rtl8169_private *tp)
tp->phylink_config.lpi_capabilities = rtl8169_get_lpi_caps(tp);
tp->phylink_config.mac_capabilities |= MAC_ASYM_PAUSE | MAC_SYM_PAUSE;
- if (tp->sfp_mode) {
+ switch (tp->sfp_mode) {
+ case RTL_SFP_8168_AF:
+ tp->pcs.ops = &r8169_pcs_ops;
+ tp->phylink_config.default_an_inband = true;
+ phy_mode = PHY_INTERFACE_MODE_1000BASEX;
+ tp->phylink_config.mac_capabilities |= MAC_1000FD;
+ break;
+ case RTL_SFP_8127_ATF:
phy_mode = PHY_INTERFACE_MODE_INTERNAL;
tp->phylink_config.mac_capabilities |= MAC_10000FD;
- } else {
+ break;
+ default:
phy_mode = PHY_INTERFACE_MODE_INTERNAL;
tp->phylink_config.mac_capabilities |= MAC_10 | MAC_100;
@@ -5794,6 +5912,7 @@ static int rtl_init_phylink(struct rtl8169_private *tp)
PHY_INTERFACE_MODE_MII;
else
phy_mode = PHY_INTERFACE_MODE_INTERNAL;
+ break;
}
__set_bit(phy_mode, tp->phylink_config.supported_interfaces);
@@ -5888,12 +6007,7 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
}
tp->aspm_manageable = !rc;
- if (rtl_is_8125(tp)) {
- u16 data = r8168_mac_ocp_read(tp, 0xd006);
-
- if ((data & 0xff) == 0x07)
- tp->sfp_mode = true;
- }
+ tp->sfp_mode = rtl_get_sfp_mode(tp);
tp->dash_type = rtl_get_dash_type(tp);
tp->dash_enabled = rtl_dash_is_enabled(tp);
@@ -6001,10 +6115,12 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
if (rc)
return rc;
- rc = r8169_mdio_register(tp);
- if (rc) {
- phylink_destroy(tp->phylink);
- return rc;
+ if (tp->sfp_mode != RTL_SFP_8168_AF) {
+ rc = r8169_mdio_register(tp);
+ if (rc) {
+ phylink_destroy(tp->phylink);
+ return rc;
+ }
}
rc = register_netdev(dev);
--
2.43.0
^ permalink raw reply related
* [PATCH net-next v3 1/7] r8169: add speed in private struct
From: javen @ 2026-06-29 6:09 UTC (permalink / raw)
To: hkallweit1, nic_swsd, andrew+netdev, davem, edumazet, kuba,
pabeni, maxime.chevallier, horms
Cc: netdev, linux-kernel, Javen Xu
In-Reply-To: <20260629060931.1006-1-javen_xu@realsil.com.cn>
From: Javen Xu <javen_xu@realsil.com.cn>
This patch adds speed in private struct in order to decouple
from phydev in the following patch supporting for phylink.
Signed-off-by: Javen Xu <javen_xu@realsil.com.cn>
---
Changes in v2:
- repalce current_speed with speed
Changes in v3:
- update tp->speed in rtl8169_set_link_ksettings()
---
drivers/net/ethernet/realtek/r8169_main.c | 24 ++++++++++++-----------
1 file changed, 13 insertions(+), 11 deletions(-)
diff --git a/drivers/net/ethernet/realtek/r8169_main.c b/drivers/net/ethernet/realtek/r8169_main.c
index ec4fc21fa21f..43bc1456bf04 100644
--- a/drivers/net/ethernet/realtek/r8169_main.c
+++ b/drivers/net/ethernet/realtek/r8169_main.c
@@ -750,6 +750,7 @@ struct rtl8169_private {
u32 irq_mask;
int irq;
struct clk *clk;
+ int speed;
struct {
DECLARE_BITMAP(flags, RTL_FLAG_MAX);
@@ -1673,16 +1674,14 @@ static void rtl8169_irq_mask_and_ack(struct rtl8169_private *tp)
rtl_pci_commit(tp);
}
-static void rtl_link_chg_patch(struct rtl8169_private *tp)
+static void rtl_link_chg_patch(struct rtl8169_private *tp, int speed)
{
- struct phy_device *phydev = tp->phydev;
-
if (tp->mac_version == RTL_GIGA_MAC_VER_34 ||
tp->mac_version == RTL_GIGA_MAC_VER_38) {
- if (phydev->speed == SPEED_1000) {
+ if (speed == SPEED_1000) {
rtl_eri_write(tp, 0x1bc, ERIAR_MASK_1111, 0x00000011);
rtl_eri_write(tp, 0x1dc, ERIAR_MASK_1111, 0x00000005);
- } else if (phydev->speed == SPEED_100) {
+ } else if (speed == SPEED_100) {
rtl_eri_write(tp, 0x1bc, ERIAR_MASK_1111, 0x0000001f);
rtl_eri_write(tp, 0x1dc, ERIAR_MASK_1111, 0x00000005);
} else {
@@ -1692,7 +1691,7 @@ static void rtl_link_chg_patch(struct rtl8169_private *tp)
rtl_reset_packet_filter(tp);
} else if (tp->mac_version == RTL_GIGA_MAC_VER_35 ||
tp->mac_version == RTL_GIGA_MAC_VER_36) {
- if (phydev->speed == SPEED_1000) {
+ if (speed == SPEED_1000) {
rtl_eri_write(tp, 0x1bc, ERIAR_MASK_1111, 0x00000011);
rtl_eri_write(tp, 0x1dc, ERIAR_MASK_1111, 0x00000005);
} else {
@@ -1700,7 +1699,7 @@ static void rtl_link_chg_patch(struct rtl8169_private *tp)
rtl_eri_write(tp, 0x1dc, ERIAR_MASK_1111, 0x0000003f);
}
} else if (tp->mac_version == RTL_GIGA_MAC_VER_37) {
- if (phydev->speed == SPEED_10) {
+ if (speed == SPEED_10) {
rtl_eri_write(tp, 0x1d0, ERIAR_MASK_0011, 0x4d02);
rtl_eri_write(tp, 0x1dc, ERIAR_MASK_0011, 0x0060a);
} else {
@@ -2074,11 +2073,11 @@ rtl_coalesce_info(struct rtl8169_private *tp)
ci = rtl_coalesce_info_8168_8136;
/* if speed is unknown assume highest one */
- if (tp->phydev->speed == SPEED_UNKNOWN)
+ if (tp->speed == SPEED_UNKNOWN)
return ci;
for (; ci->speed; ci++) {
- if (tp->phydev->speed == ci->speed)
+ if (tp->speed == ci->speed)
return ci;
}
@@ -2236,7 +2235,7 @@ static void rtl_set_eee_txidle_timer(struct rtl8169_private *tp)
static unsigned int r8169_get_tx_lpi_timer_us(struct rtl8169_private *tp)
{
- unsigned int speed = tp->phydev->speed;
+ unsigned int speed = tp->speed;
unsigned int timer = tp->tx_lpi_timer;
if (!timer || speed == SPEED_UNKNOWN)
@@ -2408,6 +2407,7 @@ static int rtl8169_set_link_ksettings(struct net_device *ndev,
phydev->autoneg = AUTONEG_DISABLE;
phydev->speed = speed;
phydev->duplex = duplex;
+ tp->speed = speed;
rtl_sfp_init(tp);
@@ -4968,8 +4968,9 @@ static void r8169_phylink_handler(struct net_device *ndev)
struct rtl8169_private *tp = netdev_priv(ndev);
struct device *d = tp_to_dev(tp);
+ tp->speed = tp->phydev->speed;
if (netif_carrier_ok(ndev)) {
- rtl_link_chg_patch(tp);
+ rtl_link_chg_patch(tp, tp->speed);
rtl_enable_tx_lpi(tp, tp->phydev->enable_tx_lpi);
pm_request_resume(d);
} else {
@@ -5667,6 +5668,7 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
ext_xid_str, xid);
tp->mac_version = chip->mac_version;
tp->fw_name = chip->fw_name;
+ tp->speed = SPEED_UNKNOWN;
/* Disable ASPM L1 as that cause random device stop working
* problems as well as full system hangs for some PCIe devices users.
--
2.43.0
^ permalink raw reply related
* [PATCH net-next v3 0/7] r8169: add support for phylink
From: javen @ 2026-06-29 6:09 UTC (permalink / raw)
To: hkallweit1, nic_swsd, andrew+netdev, davem, edumazet, kuba,
pabeni, maxime.chevallier, horms
Cc: netdev, linux-kernel, Javen Xu
From: Javen Xu <javen_xu@realsil.com.cn>
This series patch adds support for phylink. RTL8116af is a fiber mode
card, link status and speed can not be read from standard phy reg. So
we read link status and speed from serdes reg by pcs. So as RTL8127atf.
Javen Xu (7):
r8169: add speed in private struct
r8169: create a virtual interrupt for linkchg
r8169: add support for phylink
r8169: add support for RTL8116af
r8169: add ltr support for RTL8116af
r8169: fix RTL8116af can not enter s0idle and c10
r8169: add phylink support for RTL8127atf
drivers/net/ethernet/realtek/Kconfig | 1 +
drivers/net/ethernet/realtek/r8169_main.c | 657 +++++++++++++++++-----
2 files changed, 517 insertions(+), 141 deletions(-)
--
2.43.0
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox