* Re: [PATCH] net: stmmac: Avoid freeing and re-requesting IRQ during XDP set prog
From: Daniel Thompson @ 2026-07-07 9:48 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Maxime Coquelin, Alexandre Torgue,
Alexei Starovoitov, Daniel Borkmann, Jesper Dangaard Brouer,
John Fastabend, Stanislav Fomichev
Cc: Alex Elder, netdev, linux-stm32, linux-arm-kernel, linux-kernel,
bpf
In-Reply-To: <20260706-tc956x-stmmac-no_irq_teardown-v1-1-df009d0272bf@riscstar.com>
On Mon, Jul 06, 2026 at 05:17:58PM +0100, Daniel Thompson wrote:
> Currently stmmac will run a full cycle of IRQ tear down and setup when
> setting up a new XDP program. This makes tuning TSN systems difficult
> because whenever a new XDP program is installed then the irq threads will
> be stopped and restarted which will undo any thread tuning.
>
> The problem is avoided by removing stmmac_free_irq()/stmmac_request_irq()
> from stmmac_xdp_release()/stmmac_xdp_open().
>
> stmmac_free_irq() implicitly synchronizes interrupts and, with that
> removed, I was unable to prove that later actions in
> stmmac_xdp_release() are safe when there are concurrent interrupts. To
> avoid problems let's also move the code to disable DMA interrupts earlier
> in the sequence and explicitly sync the interrupts handler(s).
>
> Signed-off-by: Daniel Thompson <daniel@riscstar.com>
> <snip>
> @@ -7156,10 +7201,8 @@ int stmmac_xdp_open(struct net_device *dev)
> stmmac_reset_queues_param(priv);
>
> /* DMA CSR Channel configuration */
> - for (chan = 0; chan < dma_csr_ch; chan++) {
> + for (chan = 0; chan < dma_csr_ch; chan++)
> stmmac_init_chan(priv, priv->ioaddr, priv->plat->dma_cfg, chan);
> - stmmac_disable_dma_irq(priv, priv->ioaddr, chan, 1, 1);
> - }
Sashiko picked up that stmmac_init_chan() has the effect of enabling DMA
irqs, making this code unsafe.
Given stmmac_xdp_open() can only be called on a running interface (and that
stmmac_xdp_open() explicitly sets sph) then re-initializing with stmmac_init_chan()
should have no effect and we can drop that as well.
I'll double check with a code review across all the dwmac versions
before pushing out a v2!
Daniel.
^ permalink raw reply
* Re: [PATCH bpf-next 2/6] bpf: Add ksock kfuncs
From: Mahe Tardy @ 2026-07-07 9:48 UTC (permalink / raw)
To: Amery Hung
Cc: bpf, andrew+netdev, andrii, ast, daniel, davem, eddyz87, edumazet,
john.fastabend, kuba, liamwisehart, martin.lau, pabeni, song,
netdev
In-Reply-To: <CAMB2axO2cHcKRZzrQyY8Zd2aO56MZd8=bf5po+59qnGj=bXrkw@mail.gmail.com>
On Mon, Jul 06, 2026 at 02:33:43PM -0700, Amery Hung wrote:
> On Mon, Jul 6, 2026 at 3:26 AM Mahe Tardy <mahe.tardy@gmail.com> wrote:
> >
> > Add BPF kfuncs that allow BPF LSM programs to create and use sockets for
> > sending data. This provides a mechanism for BPF programs to emit
> > telemetry. For this first patch set, it's restricted to SOCK_DGRAM
> > socket types with IPPROTO_UDP protocol but could be easily extended to
> > SOCK_STREAM and IPPROTO_TCP in the future.
> >
> > The API consists of six kfuncs:
> >
> > bpf_ksock_create() - Create a socket (sleepable)
> > bpf_ksock_bind() - Bind socket to local address (sleepable)
> > bpf_ksock_connect() - Connect socket to remote address (sleepable)
> > bpf_ksock_send() - Send data through the socket (sleepable)
> > bpf_ksock_acquire() - Acquire a reference to a socket context
> > bpf_ksock_release() - Release a reference (cleanup via
> > queue_rcu_work since sock_release sleeps)
> >
> > The setup kfuncs bpf_ksock_create, bpf_ksock_bind, bpf_ksock_connect,
> > can be called from SYSCALL programs only. While bpf_ksock_acquire,
> > bpf_ksock_release and bpf_ksock_send can be called from SYSCALL and LSM
> > programs.
> >
> > The implementation follows the established kfunc lifecycle pattern
> > (create/acquire/release with refcounting, kptr map storage, dtor
> > registration). The kernel socket is wrapped in a refcounted bpf_ksock
> > struct. Cleanup is deferred via queue_rcu_work() because sock_release()
> > may sleep.
> >
> > The kfuncs are only compiled when CONFIG_INET is enabled, as they
> > specifically support AF_INET and AF_INET6 sockets.
> >
> > The socket operations go through the expected LSM hooks instead of
> > by-passing them like many kernel sockets since those are created by BPF
> > programs and thus system users. Thus bpf_ksock_send() kfunc, which is
> > exposed to LSM progs, has a re-entering protection to avoid recursion.
> > Also, because of the LSM checks, we prevent the use of the kfuncs from
> > asynchronous workqueue as the current value would then be invalid.
>
> Are these the first BPF kfuncs that invoke (attachable) security
> hooks? Some fs kfuncs seem to deliberately skip them. Can we avoid the
> limiting recursion check by skipping the security hook in
> bpf_ksock_send()? There is still security_socket_connect() in
> bpf_ksock_connect() to prevent undesired send.
FYI I found some piece of documentation around kfuncs and LSM hooks[^1],
people tried to avoid the recursion to happen in the first place.
Initially I wrote this with kernel sockets, which are not subject to LSM
hooks on the send path iirc. I migrated away from this thinking that
those "bpf sockets" should still be subject to the LSMs and not bypass
them, but we can change that or also have it partially as you mentioned
with connect but not send? Note that this would maybe change if we go
the sendto route as suggested on the other thread.
[^1]: https://docs.kernel.org/bpf/fs_kfuncs.html
>
> >
> > A bpf_ksock_max sysctl is added to limit the maximum number of BPF
> > kernel sockets that may exist in each network namespace. Out of
> > simplicity for now, the settings is host wide but the counters are per
> > network namespace.
> >
[...]
^ permalink raw reply
* Re: [PATCH bpf-next 2/6] bpf: Add ksock kfuncs
From: Mahe Tardy @ 2026-07-07 9:41 UTC (permalink / raw)
To: Kuniyuki Iwashima
Cc: sdf.kernel, andrew+netdev, andrii, ast, bpf, daniel, davem,
eddyz87, edumazet, john.fastabend, kuba, liamwisehart, martin.lau,
netdev, pabeni, song
In-Reply-To: <20260706225047.1684039-1-kuniyu@google.com>
On Mon, Jul 06, 2026 at 10:50:33PM +0000, Kuniyuki Iwashima wrote:
> From: Stanislav Fomichev <sdf.kernel@gmail.com>
> Date: Mon, 6 Jul 2026 14:02:39 -0700
> > On 07/06, Mahe Tardy wrote:
> > > On Mon, Jul 06, 2026 at 09:58:09AM -0700, Stanislav Fomichev wrote:
> > > > On 07/06, Mahe Tardy wrote:
> > > > > Add BPF kfuncs that allow BPF LSM programs to create and use sockets for
> > > > > sending data. This provides a mechanism for BPF programs to emit
> > > > > telemetry. For this first patch set, it's restricted to SOCK_DGRAM
> > > > > socket types with IPPROTO_UDP protocol but could be easily extended to
> > > > > SOCK_STREAM and IPPROTO_TCP in the future.
> > > > >
> > > > > The API consists of six kfuncs:
> > > > >
> > > > > bpf_ksock_create() - Create a socket (sleepable)
> > > >
> > > > [..]
> > > >
> > > > > bpf_ksock_bind() - Bind socket to local address (sleepable)
> > > > > bpf_ksock_connect() - Connect socket to remote address (sleepable)
> > > >
> > > > Since you're doing only UDP for now, maybe you don't need bind/connect? The
> > > > kernel should autobind (by default) when you sendmsg over UDP socket (IIRC).
> > >
> > > Yep indeed, I kinda overlooked that as I started with UDP & TCP supports
> > > and mostly added the args checks. Another thing is that send is simpler
> > > since only used on connected sockets, so you just pass the struct
> > > bpf_ksock and data. So on one side it would simplify the current
> > > UDP-only API for now by removing the kfuncs but we might need a more
> > > complex send kfunc (something like sendto).
> >
> > Since you were targeting bpf_netpoll_send_udp originally, maybe sendto
> > is a better fit? You get the payload and the destination and you
> > bpf_sendto() it? We can later move to stateful bind/connect if needed.
> >
> > (mostly coming from the pow of minimizing api exposure initially, but
> > not a strong preference)
>
> +1, small start would be better.
Okay I feel at least we can confidently remove bind for now. Indeed for
netpoll it was kinda obvious that it was going to be sendto-style but
now with the sockets I'm unsure what's best:
As Amery Hung wrote in a parallel thread:
> [...] but connect() + send() make sense to me. In the stated use case,
> the dst addr probably doesn't change often and I think avoiding route
> lookup everytime should be a good thing.
Looks like there's benefit to both approaches, I'll experiment.
> > > > > bpf_ksock_send() - Send data through the socket (sleepable)
> > > > > bpf_ksock_acquire() - Acquire a reference to a socket context
> > > > > bpf_ksock_release() - Release a reference (cleanup via
> > > > > queue_rcu_work since sock_release sleeps)
> > > > >
> > > > [..]
> > > >
> > > > > A bpf_ksock_max sysctl is added to limit the maximum number of BPF
> > > > > kernel sockets that may exist in each network namespace. Out of
> > > > > simplicity for now, the settings is host wide but the counters are per
> > > > > network namespace.
> > > >
> > > > What is this guarding against? Rogue bpf programs creating too many sockets?
> > >
> > > Yes. AI review raised this because users are prevented from creating too
> > > many sockets by bumping against the max number of fd and this would
> > > allow them to create way more sockets. I kind of agreed that having "a
> > > limit" on resource creation would make sense but maybe it doesn't and we
> > > can simplify this!
> >
> > I believe even the kernel sockets go via lsm layer, so this enforcement
> > can be done in an lsm bpf program. Seems like that should be enough?
>
> Right, I don't think the per-netns limit is useful for CAP_BPF users.
Ok, agree, let's remove all this then for the next version.
^ permalink raw reply
* Re: [PATCH net-next V10 00/14] devlink and mlx5: Support cross-function rate scheduling
From: patchwork-bot+netdevbpf @ 2026-07-07 9:40 UTC (permalink / raw)
To: Tariq Toukan
Cc: andrew+netdev, davem, edumazet, kuba, netdev, pabeni,
ajayachandra, bobbyeshleman, cjubran, cratiu, daniel, danielj,
daniel.zahka, dw, donald.hunter, dtatulea, jiri, jiri, joe,
corbet, kees, leon, linux-doc, linux-kernel, linux-kselftest,
linux-rdma, mbloch, moshe, ohartoov, parav, petrm, rkannoth,
saeedm, shshitrit, shayd, shuah, skhan, horms, sdf, willemb, gal
In-Reply-To: <20260701073254.754518-1-tariqt@nvidia.com>
Hello:
This series was applied to netdev/net-next.git (main)
by Paolo Abeni <pabeni@redhat.com>:
On Wed, 1 Jul 2026 10:32:40 +0300 you wrote:
> Hi,
>
> This series by Cosmin adds support for cross-function rate scheduling in
> devlink and mlx5.
> See detailed explanation by Cosmin below [0].
>
> Regards,
> Tariq
>
> [...]
Here is the summary with links:
- [net-next,V10,01/14] devlink: Update nested instance locking comment
https://git.kernel.org/netdev/net-next/c/3bf4c5970ca8
- [net-next,V10,02/14] devlink: Add a helper for getting a nested-in instance
https://git.kernel.org/netdev/net-next/c/9f2d908cc34e
- [net-next,V10,03/14] devlink: Migrate from info->user_ptr to info->ctx
https://git.kernel.org/netdev/net-next/c/e48abacd6e83
- [net-next,V10,04/14] devlink: Decouple rate storage from associated devlink object
https://git.kernel.org/netdev/net-next/c/db078bc2b031
- [net-next,V10,05/14] devlink: Add parent dev to devlink API
https://git.kernel.org/netdev/net-next/c/b5f90fd4580c
- [net-next,V10,06/14] devlink: Allow parent dev for rate-set and rate-new
https://git.kernel.org/netdev/net-next/c/58132b6fc4a5
- [net-next,V10,07/14] devlink: Allow rate node parents from other devlinks
https://git.kernel.org/netdev/net-next/c/6bbd1bce3099
- [net-next,V10,08/14] net/mlx5: qos: Use mlx5_lag_query_bond_speed to query LAG speed
https://git.kernel.org/netdev/net-next/c/f8128b13df66
- [net-next,V10,09/14] net/mlx5: qos: Refactor vport QoS cleanup
https://git.kernel.org/netdev/net-next/c/89a0881183d1
- [net-next,V10,10/14] net/mlx5: qos: Model the root node in the scheduling hierarchy
https://git.kernel.org/netdev/net-next/c/22d32def3ced
- [net-next,V10,11/14] net/mlx5: qos: Remove qos domains and use shd
https://git.kernel.org/netdev/net-next/c/450ed6b182de
- [net-next,V10,12/14] net/mlx5: qos: Support cross-device tx scheduling
https://git.kernel.org/netdev/net-next/c/2bc38232047c
- [net-next,V10,13/14] selftests: drv-net: Add test for cross-esw rate scheduling
https://git.kernel.org/netdev/net-next/c/b2aa7390967e
- [net-next,V10,14/14] net/mlx5: Document devlink rates
https://git.kernel.org/netdev/net-next/c/403ac520e893
You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html
^ permalink raw reply
* Re: [PATCH net 1/4] ice: wait for reset completion in ice_resume()
From: Aaron Ma @ 2026-07-07 9:35 UTC (permalink / raw)
To: Paolo Abeni
Cc: Tony Nguyen, davem, kuba, edumazet, andrew+netdev, netdev,
jbrandeb, stable, Kohei Enju, Aleksandr Loktionov,
Przemek Kitszel, Alexander Nowlin
In-Reply-To: <0496c117-0731-4de4-9f5d-7fdacf34bd71@redhat.com>
On Tue, Jul 7, 2026 at 4:28 PM Paolo Abeni <pabeni@redhat.com> wrote:
>
> On 6/30/26 11:43 PM, Tony Nguyen wrote:
> > From: Aaron Ma <aaron.ma@canonical.com>
> >
> > ice_resume() schedules an asynchronous PF reset and returns
> > immediately. The reset runs later in ice_service_task(). If
> > userspace tries to bring up the net device before the reset
> > finishes, ice_open() fails with -EBUSY:
> >
> > ice_resume()
> > ice_schedule_reset() # sets ICE_PFR_REQ, returns
> > ...
> > ice_open()
> > ice_is_reset_in_progress() # ICE_PFR_REQ still set, -EBUSY
> > ...
> > ice_service_task()
> > ice_do_reset()
> > ice_rebuild() # clears ICE_PFR_REQ, too late
> >
> > Reproduced on E800 series NICs during suspend/resume with irdma
> > enabled, where the aux device probe widens the race window.
> >
> > ice 0000:81:00.0: can't open net device while reset is in progress
> >
> > Add a best-effort wait (10s timeout, matching ice_devlink_info_get())
> > for the reset to complete before returning from ice_resume(). In
> > practice the reset completes in ~300ms.
>
> Would not be better to (eventually) wait in ice_open()? Why? AFAICS that
> would be also more consistent with i.e. the current wait in
> ice_devlink_info_get().
>
> Otherwise why don't consolidate all the wait at resume time and remove
> the other one in ice_devlink_info_get()?
>
Hi Paolo,
ice_open() is where the failure is observed, but it is not what creates
this race. ice_resume() schedules a PFR and returns success before that
reset/rebuild has completed, so userspace can call open immediately after
resume and hit -EBUSY.
Waiting in ice_resume() keeps the fix scoped to the PM path and to the
reset initiated by resume. Moving the wait into ice_open() would change
all opens during any reset from immediate -EBUSY to sleeping, and ndo_open
runs under RTNL.
The devlink wait should stay because ice_devlink_info_get() is reached by
DEVLINK_CMD_INFO_GET userspace requests and reads device/FW state. That
path still needs protection from resets unrelated to PM resume.
Aaron
> /P
>
^ permalink raw reply
* Re: [PATCH net v4 0/3] net/smc: bound wire-controlled CDC cursors against the local buffers
From: Dust Li @ 2026-07-07 9:29 UTC (permalink / raw)
To: hexlabsecurity, David S. Miller, Sidraya Jayagond, Eric Dumazet,
D. Wythe, Jakub Kicinski, Simon Horman, Wenjia Zhang, Paolo Abeni
Cc: Stefan Raspl, Wen Gu, linux-kernel, netdev, Mahanta Jambigi,
Tony Lu, Ursula Braun, linux-s390, linux-rdma
In-Reply-To: <20260705-b4-disp-28a1bbca-v4-0-be089b98acc6@proton.me>
On 2026-07-05 02:54:04, Bryam Vargas via B4 Relay wrote:
>A peer's CDC producer/consumer cursors are copied from the wire and used,
>without an upper bound against the local buffers, as (a) a raw index into the
>RMB on the urgent path, (b) the receive length in smc_rx_recvmsg(), and (c) the
>send length in smc_tx_sendmsg() on the SMC-D DMB-merge path. A malicious or
>buggy peer can forge a cursor so each runs past the relevant buffer: an
>out-of-bounds read of adjacent kernel memory (disclosed to the peer) on the
>receive/urgent side, and, on the send side, an out-of-bounds write whose
>length the peer controls and whose overflowing bytes are the local sender's
>own outbound data.
>
>This series bounds each length where it is consumed. The clamp is synchronous
>and race-free against the tasklet that advances the cursor, so it is the minimal
>fix for stable. A separate net-next series adds the wire-boundary validation and
>connection abort that Dust Li suggested; those do not replace these clamps.
>
>The clamp is not subsumed by validating cursors at the input boundary. A peer
>that only increments prod.wrap with count == 0 hits the differing-wrap branch of
>smc_curs_diff(), which returns (len - 0) + 0 == len every CDC, so bytes_to_rcv
>(and sndbuf_space on the send side) accumulates past the buffer while every
>per-cursor bound sees count == 0 and accepts the message. The overflow lives in
>the accumulator, not the cursor; only the consumer-side clamp bounds it. And
>because a queued abort runs asynchronously (queue_work -> smc_conn_kill) while
>smc_rx_recvmsg() reads the accumulator under lock_sock, only the synchronous
>clamp closes that window. So the clamp goes to stable; the abort is net-next.
>
>The nearby readable >= rmb_desc->len / len > sndbuf_desc->len tests only feed
>statistics counters (SMC_STAT_RMB_RX_FULL / SMC_STAT_RMB_TX_SIZE_SMALL) on an
>earlier, separate read; they do not bound the copy.
>
>A/B (in-kernel KASAN replaying the sink arithmetic over a real rmb_desc->len /
>sndbuf_desc->len slab; kasan.fault=report kasan_multi_shot, 2026-07-05):
> - urgent index (1/3): count = len+1 -> slab-out-of-bounds Read; clamped -> clean
> - recv length (2/3): bytes_to_rcv = 5*len via wrap++/count=0 -> OOB Read; clamped -> clean
> - send length (3/3): sndbuf_space inflated -> slab-out-of-bounds Write; clamped -> clean
> - signed overflow: readable = -1 -> v1 ">len" misses -> OOB; "<0 || >len" -> clean
> - concurrent TOCTOU race: a producer-side clamp is racy (OOB in a racing consumer
> kthread on another CPU); the consumer-side clamp is race-free (0 hits / 5,000,000 reads).
> - every in-bounds / honest-peer arm: clean.
>
>Changes since v3:
> - split into this stable-bound clamp series and a separate net-next
> validate/abort series, per Dust Li's review;
> - tightened the commit messages; noted that the nearby SMC_STAT_* tests are not
> bounds; no functional change to the three clamps.
> v3: https://lore.kernel.org/all/20260614-b4-disp-edd64be9-v3-0-551fa514257e@proton.me/
Hi Bryam,
Are you planning to land these clamps first, and then follow up with a
separate validate/abort series?
Looking at your earlier A/B test, it simulates this logic in userspace to
demonstrate the bug, but it doesn't actually trigger the bug in our
current kernel. If that's the case, the security risk here doesn't seem
high to me, since SMC is only meant to be deployed in trusted environments.
On the other hand, once this is actually triggered, it means the data
we've been handing to userspace is already wrong, which is already a
serious problem, and the connection should be terminated. So I don't
really see much value in merging the bound-clamp patches first.
So, I'd expected to see the real bug and validation/abort patch.
Best regards,
Dust
^ permalink raw reply
* Re: [PATCH net 1/2] vsock/virtio: collapse receive queue under memory pressure
From: Stefano Garzarella @ 2026-07-07 9:27 UTC (permalink / raw)
To: Bobby Eshleman
Cc: netdev, Jason Wang, Jakub Kicinski, Paolo Abeni,
Michael S. Tsirkin, kvm, virtualization, Xuan Zhuo, Eric Dumazet,
Simon Horman, linux-kernel, Stefan Hajnoczi, David S. Miller,
Eugenio Pérez, stable, Brien Oberstein
In-Reply-To: <akbFcMHenseQW7mJ@devvm29614.prn0.facebook.com>
On Thu, Jul 02, 2026 at 01:09:20PM -0700, Bobby Eshleman wrote:
>On Thu, Jul 02, 2026 at 10:56:04AM +0200, Stefano Garzarella wrote:
>> On Wed, Jul 01, 2026 at 09:34:35AM -0700, Bobby Eshleman wrote:
>> > On Fri, Jun 26, 2026 at 03:48:22PM +0200, Stefano Garzarella wrote:
>>
>> [...]
>>
>> > > +out:
>> > > + if (new_skb)
>> > > + __skb_queue_tail(&new_queue, new_skb);
>> > > +
>> > > + skb_queue_splice(&new_queue, &vvs->rx_queue);
>> >
>> > I think the new skbs will also need skb_set_owner_sk_safe(skb, sk)
>> > when adding to rx_queue?
>>
>> IIRC we added it in the rx path, mainily for loopback to pass the ownership
>> from the tx socket to the rx socket, but here we are already in the rx path,
>> so the skb will never leave this socket.
>>
>
>Ah that's right, I stand corrected. There is no sender to leak in this
>case.
>
>> Maybe it's necessary for the eBPF path?
>
>Looking through sockmap, I don't think it depends on skb->sk being
>non-null either (it reassigns owner to the redirect socket anyway using
>skb_set_owner_r()).
>
>Sorry for the false alarm. LGTM.
Thanks for checking!
>
>Reviewed-by: Bobby Eshleman <bobbyeshleman@meta.com>
>
I'm going to add a threashold as Paolo suggested. Do you prefer to look
at v2 or should I carry your R-b ?
Thanks,
Stefano
^ permalink raw reply
* [PATCH net-next v5 4/8] r8169: add support for RTL8116af
From: javen @ 2026-07-07 9:16 UTC (permalink / raw)
To: hkallweit1, nic_swsd, andrew+netdev, davem, edumazet, kuba,
pabeni, maxime.chevallier, horms
Cc: netdev, linux-kernel, daniel, linux, enelsonmoore, daniel,
Javen Xu
In-Reply-To: <20260707091637.371-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
Changes in v4:
- if tp->pcs.ops is not initial, just return NULL in rtl_mac_select_pcs
Changes in v5:
- no changes
---
drivers/net/ethernet/realtek/r8169_main.c | 186 ++++++++++++++++++----
1 file changed, 153 insertions(+), 33 deletions(-)
diff --git a/drivers/net/ethernet/realtek/r8169_main.c b/drivers/net/ethernet/realtek/r8169_main.c
index 72e4e0330446..f3609c5671a1 100644
--- a/drivers/net/ethernet/realtek/r8169_main.c
+++ b/drivers/net/ethernet/realtek/r8169_main.c
@@ -97,6 +97,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;
@@ -729,6 +741,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;
@@ -737,6 +755,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;
@@ -764,7 +783,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;
@@ -778,6 +796,7 @@ struct rtl8169_private {
u32 ocp_base;
struct phylink *phylink;
struct phylink_config phylink_config;
+ struct phylink_pcs pcs;
struct ethtool_pauseparam saved_pause;
bool jumbo_pause_saved;
};
@@ -1135,7 +1154,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);
@@ -1289,6 +1308,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);
@@ -1584,6 +1612,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) {
@@ -2399,7 +2441,7 @@ static int rtl8169_set_link_ksettings(struct net_device *ndev,
int duplex = cmd->base.duplex;
int speed = cmd->base.speed;
- if (!tp->sfp_mode)
+ if (tp->sfp_mode != RTL_SFP_8127_ATF)
return phylink_ethtool_ksettings_set(tp->phylink, cmd);
if (cmd->base.autoneg != AUTONEG_DISABLE)
@@ -2511,9 +2553,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);
}
}
@@ -2550,6 +2593,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) {
@@ -2564,7 +2609,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 */
@@ -3706,12 +3751,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);
@@ -4895,8 +4942,13 @@ static irqreturn_t rtl8169_interrupt(int irq, void *dev_instance)
goto out;
}
- if (status & LinkChg)
- phy_mac_interrupt(tp->phydev);
+ if (status & LinkChg) {
+ if (tp->phydev)
+ phy_mac_interrupt(tp->phydev);
+ 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);
@@ -5008,7 +5060,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);
@@ -5030,9 +5082,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);
@@ -5110,9 +5162,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);
@@ -5616,6 +5670,14 @@ 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 (!tp->pcs.ops)
+ return NULL;
+
+ if (interface == PHY_INTERFACE_MODE_1000BASEX)
+ return &tp->pcs;
+
return NULL;
}
@@ -5624,6 +5686,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);
@@ -5671,6 +5778,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;
@@ -5682,10 +5796,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;
@@ -5706,6 +5828,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);
@@ -5803,12 +5926,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);
@@ -5916,10 +6034,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 v5 7/8] r8169: add phylink support for RTL8127atf
From: javen @ 2026-07-07 9:16 UTC (permalink / raw)
To: hkallweit1, nic_swsd, andrew+netdev, davem, edumazet, kuba,
pabeni, maxime.chevallier, horms
Cc: netdev, linux-kernel, daniel, linux, enelsonmoore, daniel,
Javen Xu
In-Reply-To: <20260707091637.371-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.
Signed-off-by: Javen Xu <javen_xu@realsil.com.cn>
---
Changes in v3:
- No changes. New file.
Changes in v4:
- remove DUMMY_PHY in driver/net/phy/realtek/realtek_main.c and related
function
Changes in v5:
- no changes, 1G support will be submitted by a following patch
---
drivers/net/ethernet/realtek/r8169_main.c | 81 ++++++++++-------------
drivers/net/phy/realtek/realtek_main.c | 54 ---------------
include/net/phy/realtek_phy.h | 7 --
3 files changed, 35 insertions(+), 107 deletions(-)
delete mode 100644 include/net/phy/realtek_phy.h
diff --git a/drivers/net/ethernet/realtek/r8169_main.c b/drivers/net/ethernet/realtek/r8169_main.c
index e9b9246380e8..44dd1a5554ff 100644
--- a/drivers/net/ethernet/realtek/r8169_main.c
+++ b/drivers/net/ethernet/realtek/r8169_main.c
@@ -32,7 +32,6 @@
#include <linux/unaligned.h>
#include <net/ip6_checksum.h>
#include <net/netdev_queues.h>
-#include <net/phy/realtek_phy.h>
#include "r8169.h"
#include "r8169_firmware.h"
@@ -102,6 +101,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
@@ -1158,10 +1158,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) ?
@@ -1247,12 +1243,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)
@@ -2442,30 +2432,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;
-
- 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)
@@ -2614,9 +2582,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);
@@ -4999,7 +4964,7 @@ static irqreturn_t rtl8169_interrupt(int irq, void *dev_instance)
if (status & LinkChg) {
if (tp->phydev)
phy_mac_interrupt(tp->phydev);
- else if (tp->sfp_mode == RTL_SFP_8168_AF)
+ else if (tp->sfp_mode)
phylink_mac_change(tp->phylink,
!!(RTL_R8(tp, PHYstatus) & LinkStatus));
}
@@ -5729,7 +5694,8 @@ static struct phylink_pcs *rtl_mac_select_pcs(struct phylink_config *config,
if (!tp->pcs.ops)
return NULL;
- if (interface == PHY_INTERFACE_MODE_1000BASEX)
+ if (interface == PHY_INTERFACE_MODE_1000BASEX ||
+ interface == PHY_INTERFACE_MODE_10GBASER)
return &tp->pcs;
return NULL;
@@ -5759,12 +5725,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,
@@ -5772,6 +5754,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;
}
@@ -5814,7 +5801,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;
@@ -5858,7 +5845,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:
@@ -6088,7 +6077,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);
diff --git a/drivers/net/phy/realtek/realtek_main.c b/drivers/net/phy/realtek/realtek_main.c
index b65d0f5fa1a0..4721ba071cbb 100644
--- a/drivers/net/phy/realtek/realtek_main.c
+++ b/drivers/net/phy/realtek/realtek_main.c
@@ -17,7 +17,6 @@
#include <linux/delay.h>
#include <linux/clk.h>
#include <linux/string_choices.h>
-#include <net/phy/realtek_phy.h>
#include "../phylib.h"
#include "realtek.h"
@@ -2646,45 +2645,6 @@ static irqreturn_t rtl8221b_handle_interrupt(struct phy_device *phydev)
return IRQ_HANDLED;
}
-static int rtlgen_sfp_get_features(struct phy_device *phydev)
-{
- linkmode_set_bit(ETHTOOL_LINK_MODE_10000baseT_Full_BIT,
- phydev->supported);
-
- /* set default mode */
- phydev->speed = SPEED_10000;
- phydev->duplex = DUPLEX_FULL;
-
- phydev->port = PORT_FIBRE;
-
- return 0;
-}
-
-static int rtlgen_sfp_read_status(struct phy_device *phydev)
-{
- int val, err;
-
- err = genphy_update_link(phydev);
- if (err)
- return err;
-
- if (!phydev->link)
- return 0;
-
- val = phy_read(phydev, RTL_PHYSR);
- if (val < 0)
- return val;
-
- rtlgen_decode_physr(phydev, val);
-
- return 0;
-}
-
-static int rtlgen_sfp_config_aneg(struct phy_device *phydev)
-{
- return 0;
-}
-
static struct phy_driver realtek_drvs[] = {
{
PHY_ID_MATCH_EXACT(0x00008201),
@@ -2934,20 +2894,6 @@ static struct phy_driver realtek_drvs[] = {
.write_page = rtl821x_write_page,
.read_mmd = rtl822x_read_mmd,
.write_mmd = rtl822x_write_mmd,
- }, {
- PHY_ID_MATCH_EXACT(PHY_ID_RTL_DUMMY_SFP),
- .name = "Realtek SFP PHY Mode",
- .flags = PHY_IS_INTERNAL,
- .probe = rtl822x_probe,
- .get_features = rtlgen_sfp_get_features,
- .config_aneg = rtlgen_sfp_config_aneg,
- .read_status = rtlgen_sfp_read_status,
- .suspend = genphy_suspend,
- .resume = rtlgen_resume,
- .read_page = rtl821x_read_page,
- .write_page = rtl821x_write_page,
- .read_mmd = rtl822x_read_mmd,
- .write_mmd = rtl822x_write_mmd,
}, {
PHY_ID_MATCH_EXACT(0x001ccad0),
.name = "RTL8224 2.5Gbps PHY",
diff --git a/include/net/phy/realtek_phy.h b/include/net/phy/realtek_phy.h
deleted file mode 100644
index d683bc1b0659..000000000000
--- a/include/net/phy/realtek_phy.h
+++ /dev/null
@@ -1,7 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-#ifndef _REALTEK_PHY_H
-#define _REALTEK_PHY_H
-
-#define PHY_ID_RTL_DUMMY_SFP 0x001ccbff
-
-#endif /* _REALTEK_PHY_H */
--
2.43.0
^ permalink raw reply related
* [PATCH net-next v5 8/8] r8169: add 1g support for RTL8127atf
From: javen @ 2026-07-07 9:16 UTC (permalink / raw)
To: hkallweit1, nic_swsd, andrew+netdev, davem, edumazet, kuba,
pabeni, maxime.chevallier, horms
Cc: netdev, linux-kernel, daniel, linux, enelsonmoore, daniel,
Javen Xu
In-Reply-To: <20260707091637.371-1-javen_xu@realsil.com.cn>
From: Javen Xu <javen_xu@realsil.com.cn>
This patch adds 1g support for RTL8127atf, which is fiber mode.
Signed-off-by: Javen Xu <javen_xu@realsil.com.cn>
---
Changes in v5:
- no changes, new file
---
drivers/net/ethernet/realtek/r8169_main.c | 85 ++++++++++++++++++++++-
1 file changed, 82 insertions(+), 3 deletions(-)
diff --git a/drivers/net/ethernet/realtek/r8169_main.c b/drivers/net/ethernet/realtek/r8169_main.c
index 44dd1a5554ff..12e073b9a614 100644
--- a/drivers/net/ethernet/realtek/r8169_main.c
+++ b/drivers/net/ethernet/realtek/r8169_main.c
@@ -111,6 +111,12 @@
#define RTL8116AF_FUNC_PM_CSR 0x80
#define RTL8116AF_FUNC_EXP_LNKCTL 0x44
#define RTL_PM_D3HOT GENMASK(1, 0)
+#define R8127_SDS_CMD 0x2348
+#define R8127_SDS_ADDR 0x234a
+#define R8127_SDS_DATA_IN 0x234c
+#define R8127_SDS_DATA_OUT 0x234e
+#define R8127_SDS_CMD_EXEC BIT(0)
+#define R8127_SDS_CMD_WE BIT(1)
static const struct rtl_chip_info {
u32 mask;
@@ -1225,6 +1231,67 @@ static void r8127_sfp_sds_phy_reset(struct rtl8169_private *tp)
usleep_range(10, 20);
}
+DECLARE_RTL_COND(r8127_sds_cmd_cond)
+{
+ return RTL_R16(tp, R8127_SDS_CMD) & R8127_SDS_CMD_EXEC;
+}
+
+static u16 r8127_sds_read(struct rtl8169_private *tp, u16 index, u16 page, u16 reg)
+{
+ u16 addr = (index << 11) | (page << 5) | reg;
+
+ RTL_W16(tp, R8127_SDS_ADDR, addr);
+ RTL_W16(tp, R8127_SDS_CMD, R8127_SDS_CMD_EXEC);
+
+ if (rtl_loop_wait_low(tp, &r8127_sds_cmd_cond, 10, 100))
+ return RTL_R16(tp, R8127_SDS_DATA_OUT);
+
+ return 0xffff;
+}
+
+static void r8127_sds_write(struct rtl8169_private *tp, u16 index, u16 page,
+ u16 reg, u16 val)
+{
+ u16 addr = (index << 11) | (page << 5) | reg;
+
+ RTL_W16(tp, R8127_SDS_DATA_IN, val);
+ RTL_W16(tp, R8127_SDS_ADDR, addr);
+ RTL_W16(tp, R8127_SDS_CMD, R8127_SDS_CMD_EXEC | R8127_SDS_CMD_WE);
+
+ rtl_loop_wait_low(tp, &r8127_sds_cmd_cond, 10, 100);
+}
+
+static void r8127_sds_modify(struct rtl8169_private *tp, u16 index, u16 page,
+ u16 reg, u16 clearmask, u16 setmask)
+{
+ u16 val = r8127_sds_read(tp, index, page, reg);
+
+ val = (val & ~clearmask) | setmask;
+ r8127_sds_write(tp, index, page, reg, val);
+}
+
+static void r8127_sfp_init_1g(struct rtl8169_private *tp)
+{
+ int val;
+
+ r8127_sfp_sds_phy_reset(tp);
+
+ r8127_sds_modify(tp, 0, 1, 31, 0, BIT(3));
+ r8127_sds_modify(tp, 0, 2, 0, BIT(13) | BIT(12) | BIT(6), BIT(12) | BIT(6));
+ r8127_sds_modify(tp, 0, 0, 4, 0, BIT(2));
+
+ RTL_W16(tp, 0x233a, 0x8004);
+ RTL_W16(tp, 0x233e, (RTL_R16(tp, 0x233e) & ~0x3003) | 0x0002);
+
+ r8168_phy_ocp_write(tp, 0xc40a, 0x0000);
+ r8168_phy_ocp_write(tp, 0xc466, 0x0000);
+ r8168_phy_ocp_write(tp, 0xc808, 0x0000);
+ r8168_phy_ocp_write(tp, 0xc80a, 0x0000);
+
+ val = r8168_phy_ocp_read(tp, 0xc804);
+ r8168_phy_ocp_write(tp, 0xc804, (val & ~0x000f) | 0x000c);
+}
+
static void r8127_sfp_init_10g(struct rtl8169_private *tp)
{
int val;
@@ -5756,8 +5823,20 @@ static int rtl8169_pcs_config(struct phylink_pcs *pcs, unsigned int mode,
{
struct rtl8169_private *tp = container_of(pcs, struct rtl8169_private, pcs);
- if (tp->sfp_mode == RTL_SFP_8127_ATF)
- r8127_sfp_init_10g(tp);
+ if (tp->sfp_mode == RTL_SFP_8127_ATF) {
+ switch (interface) {
+ case PHY_INTERFACE_MODE_10GBASER:
+ r8127_sfp_init_10g(tp);
+ break;
+ case PHY_INTERFACE_MODE_1000BASEX:
+ r8127_sfp_init_1g(tp);
+ break;
+ default:
+ netdev_err(tp->dev, "Unsupported SFP interface mode: %s\n",
+ phy_modes(interface));
+ return -EOPNOTSUPP;
+ }
+ }
return 0;
}
@@ -5848,7 +5927,7 @@ static int rtl_init_phylink(struct rtl8169_private *tp)
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;
+ tp->phylink_config.mac_capabilities |= MAC_1000FD | MAC_10000FD;
break;
default:
phy_mode = PHY_INTERFACE_MODE_INTERNAL;
--
2.43.0
^ permalink raw reply related
* [PATCH net-next v5 0/8] r8169: add support for phylink
From: javen @ 2026-07-07 9:16 UTC (permalink / raw)
To: hkallweit1, nic_swsd, andrew+netdev, davem, edumazet, kuba,
pabeni, maxime.chevallier, horms
Cc: netdev, linux-kernel, daniel, linux, enelsonmoore, daniel,
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 (8):
r8169: add speed in private struct
net: phy: phylink: add helper to modify pause
r8169: add support for phylink
r8169: add support for RTL8116af
r8169: add ltr support for RTL8117 series
r8169: fix RTL8116af can not enter s0idle and c10
r8169: add phylink support for RTL8127atf
r8169: add 1g support for RTL8127atf
drivers/net/ethernet/realtek/Kconfig | 1 +
drivers/net/ethernet/realtek/r8169_main.c | 658 +++++++++++++++++-----
drivers/net/phy/phylink.c | 66 +++
drivers/net/phy/realtek/realtek_main.c | 54 --
include/linux/phylink.h | 2 +
include/net/phy/realtek_phy.h | 7 -
6 files changed, 585 insertions(+), 203 deletions(-)
delete mode 100644 include/net/phy/realtek_phy.h
--
2.43.0
^ permalink raw reply
* [PATCH net-next v5 3/8] r8169: add support for phylink
From: javen @ 2026-07-07 9:16 UTC (permalink / raw)
To: hkallweit1, nic_swsd, andrew+netdev, davem, edumazet, kuba,
pabeni, maxime.chevallier, horms
Cc: netdev, linux-kernel, daniel, linux, enelsonmoore, daniel,
Javen Xu
In-Reply-To: <20260707091637.371-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
Changes in v4:
- set lpi_interface if support eee
- Only if jumbo changes, set pause
Changes in v5:
- no changes
---
drivers/net/ethernet/realtek/Kconfig | 1 +
drivers/net/ethernet/realtek/r8169_main.c | 279 ++++++++++++++++------
2 files changed, 206 insertions(+), 74 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 c60710f9bd21..72e4e0330446 100644
--- a/drivers/net/ethernet/realtek/r8169_main.c
+++ b/drivers/net/ethernet/realtek/r8169_main.c
@@ -26,6 +26,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>
@@ -775,6 +776,10 @@ struct rtl8169_private {
struct r8169_led_classdev *leds;
u32 ocp_base;
+ struct phylink *phylink;
+ struct phylink_config phylink_config;
+ struct ethtool_pauseparam saved_pause;
+ bool jumbo_pause_saved;
};
typedef void (*rtl_generic_fct)(struct rtl8169_private *tp);
@@ -2253,7 +2258,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;
@@ -2269,7 +2274,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,
@@ -2300,13 +2305,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,
@@ -2317,9 +2317,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,
@@ -2385,6 +2383,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)
{
@@ -2394,7 +2400,7 @@ static int rtl8169_set_link_ksettings(struct net_device *ndev,
int speed = cmd->base.speed;
if (!tp->sfp_mode)
- return phy_ethtool_ksettings_set(phydev, cmd);
+ return phylink_ethtool_ksettings_set(tp->phylink, cmd);
if (cmd->base.autoneg != AUTONEG_DISABLE)
return -EINVAL;
@@ -2415,6 +2421,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,
@@ -2430,10 +2443,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,
@@ -2656,15 +2669,6 @@ 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);
- }
}
DECLARE_RTL_COND(rtl_chipcmd_cond)
@@ -2779,7 +2783,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);
}
}
@@ -2831,6 +2835,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;
@@ -4138,12 +4152,25 @@ static void rtl_hw_start(struct rtl8169_private *tp)
static int rtl8169_change_mtu(struct net_device *dev, int new_mtu)
{
struct rtl8169_private *tp = netdev_priv(dev);
+ bool jumbo_before = dev->mtu > ETH_DATA_LEN;
+ bool jumbo_after = new_mtu > ETH_DATA_LEN;
WRITE_ONCE(dev->mtu, new_mtu);
netdev_update_features(dev);
rtl_jumbo_config(tp);
rtl_set_eee_txidle_timer(tp);
+ if (jumbo_before != jumbo_after) {
+ unsigned long caps = tp->phylink_config.mac_capabilities;
+
+ if (jumbo_after)
+ caps &= ~(MAC_SYM_PAUSE | MAC_ASYM_PAUSE);
+ else
+ caps |= (MAC_SYM_PAUSE | MAC_ASYM_PAUSE);
+
+ phylink_set_mac_capabilities(tp->phylink, caps);
+ }
+
return 0;
}
@@ -4929,9 +4956,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 */
@@ -4962,41 +4986,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;
}
@@ -5007,7 +5005,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)
@@ -5039,7 +5037,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)
@@ -5055,7 +5053,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);
@@ -5190,8 +5188,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);
@@ -5288,6 +5289,7 @@ static void rtl_remove_one(struct pci_dev *pdev)
r8169_remove_leds(tp->leds);
unregister_netdev(tp->dev);
+ phylink_destroy(tp->phylink);
if (tp->dash_type != RTL_DASH_NONE)
rtl8168_driver_stop(tp);
@@ -5310,7 +5312,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,
@@ -5474,16 +5476,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);
@@ -5599,6 +5591,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);
+
+ 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);
+ if (tp->phylink_config.lpi_capabilities)
+ __set_bit(phy_mode, tp->phylink_config.lpi_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;
@@ -5789,13 +5912,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 v5 6/8] r8169: fix RTL8116af can not enter s0idle and c10
From: javen @ 2026-07-07 9:16 UTC (permalink / raw)
To: hkallweit1, nic_swsd, andrew+netdev, davem, edumazet, kuba,
pabeni, maxime.chevallier, horms
Cc: netdev, linux-kernel, daniel, linux, enelsonmoore, daniel,
Javen Xu
In-Reply-To: <20260707091637.371-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
Changes in v4:
- add gate for rtl_lowpower_hidden_functions, only for RTL8116af
Changes in v5:
- no changes
---
drivers/net/ethernet/realtek/r8169_main.c | 57 ++++++++++++++++++-----
1 file changed, 46 insertions(+), 11 deletions(-)
diff --git a/drivers/net/ethernet/realtek/r8169_main.c b/drivers/net/ethernet/realtek/r8169_main.c
index 1548453d484d..e9b9246380e8 100644
--- a/drivers/net/ethernet/realtek/r8169_main.c
+++ b/drivers/net/ethernet/realtek/r8169_main.c
@@ -97,17 +97,20 @@
#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
+#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
+#define RTL8116AF_FUNC_PM_CSR 0x80
+#define RTL8116AF_FUNC_EXP_LNKCTL 0x44
+#define RTL_PM_D3HOT GENMASK(1, 0)
static const struct rtl_chip_info {
u32 mask;
@@ -3734,6 +3737,35 @@ static void rtl_hw_start_8168ep_3(struct rtl8169_private *tp)
r8168_mac_ocp_modify(tp, 0xe860, 0x0000, 0x0080);
}
+static void rtl_lowpower_hidden_functions(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[] = {
@@ -3790,6 +3822,9 @@ static void rtl_hw_start_8117(struct rtl8169_private *tp)
r8168_mac_ocp_write(tp, 0xc094, 0x0000);
r8168_mac_ocp_write(tp, 0xc09e, 0x0000);
+ if (rtl_is_8116af(tp))
+ rtl_lowpower_hidden_functions(tp->pci_dev);
+
/* firmware is for MAC only */
r8169_apply_firmware(tp);
}
--
2.43.0
^ permalink raw reply related
* [PATCH net-next v5 5/8] r8169: add ltr support for RTL8117 series
From: javen @ 2026-07-07 9:16 UTC (permalink / raw)
To: hkallweit1, nic_swsd, andrew+netdev, davem, edumazet, kuba,
pabeni, maxime.chevallier, horms
Cc: netdev, linux-kernel, daniel, linux, enelsonmoore, daniel,
Javen Xu
In-Reply-To: <20260707091637.371-1-javen_xu@realsil.com.cn>
From: Javen Xu <javen_xu@realsil.com.cn>
This patch adds ltr support for RTL8117 series, enables RTL8117 series
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
Changes in v4:
- no changes
Changes in v5:
- 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 f3609c5671a1..1548453d484d 100644
--- a/drivers/net/ethernet/realtek/r8169_main.c
+++ b/drivers/net/ethernet/realtek/r8169_main.c
@@ -347,11 +347,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 {
@@ -3156,8 +3158,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;
@@ -3177,6 +3193,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)
@@ -3210,6 +3227,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);
@@ -3222,6 +3240,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 v5 1/8] r8169: add speed in private struct
From: javen @ 2026-07-07 9:16 UTC (permalink / raw)
To: hkallweit1, nic_swsd, andrew+netdev, davem, edumazet, kuba,
pabeni, maxime.chevallier, horms
Cc: netdev, linux-kernel, daniel, linux, enelsonmoore, daniel,
Javen Xu, Andrew Lunn
In-Reply-To: <20260707091637.371-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.
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
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()
Changes in v4:
- no changes
Changes in v5:
- no changes
---
drivers/net/ethernet/realtek/r8169_main.c | 23 ++++++++++++-----------
1 file changed, 12 insertions(+), 11 deletions(-)
diff --git a/drivers/net/ethernet/realtek/r8169_main.c b/drivers/net/ethernet/realtek/r8169_main.c
index ec4fc21fa21f..c60710f9bd21 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)
@@ -4968,8 +4967,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 +5667,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 v5 2/8] net: phy: phylink: add helper to modify pause
From: javen @ 2026-07-07 9:16 UTC (permalink / raw)
To: hkallweit1, nic_swsd, andrew+netdev, davem, edumazet, kuba,
pabeni, maxime.chevallier, horms
Cc: netdev, linux-kernel, daniel, linux, enelsonmoore, daniel,
Javen Xu
In-Reply-To: <20260707091637.371-1-javen_xu@realsil.com.cn>
From: Javen Xu <javen_xu@realsil.com.cn>
For Realtek nics, when we enable jumbo, pause are not supported. So we
must check the pause capabilities from ourself and lp.
Signed-off-by: Javen Xu <javen_xu@realsil.com.cn>
---
Changes in v5:
- no changes, new file
---
drivers/net/phy/phylink.c | 66 +++++++++++++++++++++++++++++++++++++++
include/linux/phylink.h | 2 ++
2 files changed, 68 insertions(+)
diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c
index 59dfe35afa54..c450ee33b75c 100644
--- a/drivers/net/phy/phylink.c
+++ b/drivers/net/phy/phylink.c
@@ -1828,6 +1828,72 @@ int phylink_set_fixed_link(struct phylink *pl,
}
EXPORT_SYMBOL_GPL(phylink_set_fixed_link);
+/**
+ * phylink_set_mac_capabilities() - Dynamically update MAC capabilities
+ * @pl: a pointer to a &struct phylink returned from phylink_create()
+ * @mac_capabilities: the new MAC capabilities mask
+ *
+ * This function allows a MAC driver to dynamically change its capabilities,
+ * such as losing/gaining Pause frame support based on MTU size.
+ * It recalculates supported link modes and triggers renegotiation if needed.
+ */
+void phylink_set_mac_capabilities(struct phylink *pl, unsigned long mac_capabilities)
+{
+ struct phylink_link_state *config = &pl->link_config;
+ unsigned long caps_added, caps_removed;
+
+ ASSERT_RTNL();
+
+ caps_added = mac_capabilities & ~pl->config->mac_capabilities;
+ caps_removed = pl->config->mac_capabilities & ~mac_capabilities;
+
+ if (!caps_added && !caps_removed)
+ return;
+
+ mutex_lock(&pl->state_mutex);
+
+ pl->config->mac_capabilities = mac_capabilities;
+
+ if (caps_removed & MAC_SYM_PAUSE)
+ linkmode_clear_bit(ETHTOOL_LINK_MODE_Pause_BIT, pl->supported);
+ if (caps_removed & MAC_ASYM_PAUSE)
+ linkmode_clear_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, pl->supported);
+
+ linkmode_and(config->advertising, config->advertising, pl->supported);
+
+ if (caps_added & MAC_SYM_PAUSE) {
+ linkmode_set_bit(ETHTOOL_LINK_MODE_Pause_BIT, pl->supported);
+ if (pl->phydev && !phylink_test(pl->phydev->supported, Pause))
+ linkmode_clear_bit(ETHTOOL_LINK_MODE_Pause_BIT, pl->supported);
+ }
+ if (caps_added & MAC_ASYM_PAUSE) {
+ linkmode_set_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, pl->supported);
+ if (pl->phydev && !phylink_test(pl->phydev->supported, Asym_Pause))
+ linkmode_clear_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, pl->supported);
+ }
+
+ linkmode_and(config->advertising, config->advertising, pl->supported);
+
+ if (config->pause & MLO_PAUSE_AN) {
+ if (phylink_test(pl->supported, Pause))
+ linkmode_set_bit(ETHTOOL_LINK_MODE_Pause_BIT, config->advertising);
+
+ if (phylink_test(pl->supported, Asym_Pause))
+ linkmode_set_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, config->advertising);
+ }
+
+ if (!pl->phydev)
+ phylink_change_inband_advert(pl);
+
+ mutex_unlock(&pl->state_mutex);
+
+ if (pl->phydev) {
+ linkmode_copy(pl->phydev->advertising, config->advertising);
+ phy_start_aneg(pl->phydev);
+ }
+}
+EXPORT_SYMBOL_GPL(phylink_set_mac_capabilities);
+
/**
* phylink_create() - create a phylink instance
* @config: a pointer to the target &struct phylink_config
diff --git a/include/linux/phylink.h b/include/linux/phylink.h
index 2bc0db3d52ac..56642ab961a7 100644
--- a/include/linux/phylink.h
+++ b/include/linux/phylink.h
@@ -842,4 +842,6 @@ void phylink_replay_link_begin(struct phylink *pl);
void phylink_replay_link_end(struct phylink *pl);
+void phylink_set_mac_capabilities(struct phylink *pl, unsigned long mac_capabilities);
+
#endif
--
2.43.0
^ permalink raw reply related
* Re: [PATCH v3] ptp: ocp: add CPLD ISP support for ADVA TimeCard
From: Paolo Abeni @ 2026-07-07 9:19 UTC (permalink / raw)
To: maimon.sagi
Cc: jonathan.lemon, vadim.fedorenko, richardcochran, andrew+netdev,
davem, edumazet, kuba, pabeni, linux-kernel, netdev
In-Reply-To: <20260701072203.4321-1-maimon.sagi@gmail.com>
From: AI Reviewer <ai@example.com>
This is an AI-generated review of your patch. The human sending this
email has considered the AI review valid, or at least plausible.
Full review at: https://netdev-ai.bots.linux.dev/sashiko/
---
ptp: ocp: add CPLD ISP support for ADVA TimeCard
This change adds a sysfs interface on the adva_x1 ptp_ocp variant to
own a secondary I2C bus and forward arbitrary I2C transactions to a
Lattice MachXO3 CPLD for in-system programming. A new attribute
i2c_bus_ctrl controls bus ownership, and a bin_attr cpld_i2c_xfer
implements a write-request/read-response RPC guarded by an
allowlisted address set.
A few questions on the implementation below.
> diff --git a/drivers/ptp/ptp_ocp.c b/drivers/ptp/ptp_ocp.c
> index 35e911f1ad78..86c341ea4062 100644
> --- a/drivers/ptp/ptp_ocp.c
> +++ b/drivers/ptp/ptp_ocp.c
> @@ -416,6 +416,10 @@ struct ptp_ocp {
> dpll_tracker tracker;
> int signals_nr;
> int freq_in_nr;
> + /* cpld_i2c_xfer sysfs (adva_x1) */
> + struct mutex tap_i2c_lock;
> + u8 tap_i2c_rsp[21]; /* [status, read_data...] */
> + size_t tap_i2c_rsp_len;
> };
[Low]
Should tap_i2c_lock be paired with mutex_destroy() on the
ptp_ocp_detach()/ptp_ocp_dev_release() paths? The mutex is initialized
here but never destroyed, which skips the lockdep/DEBUG_MUTEXES
validation that the lock is not held at teardown and breaks the usual
init/destroy symmetry for driver-owned mutexes.
> @@ -3188,6 +3192,8 @@ ptp_ocp_adva_board_init(struct ptp_ocp *bp, struct ocp_resource *r)
> ptp_ocp_nmea_out_init(bp);
> ptp_ocp_signal_init(bp);
>
> + mutex_init(&bp->tap_i2c_lock);
> +
> err = ptp_ocp_attr_group_add(bp, info->attr_groups);
> if (err)
> return err;
[Low]
The commit message says the interface is available on "ADVA TimeCard
boards", but only adva_timecard_x1_group gets the new attributes;
adva_timecard_group is unchanged. However mutex_init() lives in
ptp_ocp_adva_board_init(), which is called for both the adva and
adva_x1 variants, so the mutex is initialized on non-x1 hardware where
tap_i2c_rsp is never consumed. Should the commit message clarify that
this is x1-specific, or should the feature be exposed on the base
adva variant as well?
> @@ -4224,6 +4230,171 @@ static const struct ocp_attr_group art_timecard_groups[] = {
> { },
> };
>
> +static ssize_t
> +i2c_bus_ctrl_show(struct device *dev, struct device_attribute *attr, char *buf)
> +{
> + struct ptp_ocp *bp = dev_get_drvdata(dev);
> +
> + if (!bp->pps_select)
> + return -ENODEV;
> + return sysfs_emit(buf, "0x%08x\n",
> + ioread32(&bp->pps_select->__pad1));
> +}
> +
> +static ssize_t
> +i2c_bus_ctrl_store(struct device *dev, struct device_attribute *attr,
> + const char *buf, size_t count)
> +{
> + struct ptp_ocp *bp = dev_get_drvdata(dev);
> + u32 val;
> +
> + if (!bp->pps_select)
> + return -ENODEV;
> + if (kstrtou32(buf, 0, &val))
> + return -EINVAL;
> + iowrite32(val, &bp->pps_select->__pad1);
> + return count;
> +}
[Medium]
Is it safe to repurpose struct gpio_reg's __pad1 field as a live control
register? The struct declares that slot as u32 __pad1, a padding/reserved
member, and pps_select is registered for every board variant via
OCP_MEM_RESOURCE(pps_select). Could a future rearrangement of gpio_reg
padding silently break this, and does offset 0xC of pps_select do
something different (or truly reserved) on non-adva-x1 boards where an
accidental write could produce undefined FPGA behavior? Would renaming
the field (for example to i2c_bus_ctrl) and documenting its per-variant
meaning make the intent clearer?
[Low]
Since this write hands ownership of a shared I2C bus between the host
and the on-board MicroBlaze, does anything guarantee the write has
been posted to the FPGA before userspace triggers the next
cpld_i2c_xfer transaction? There is no read-back, barrier, or
completion polling here, so on architectures with posted PCIe writes
the first ISP transaction after a hand-off could see the old owner
and NACK or produce corrupted data.
> +static DEVICE_ATTR_RW(i2c_bus_ctrl);
> +
> +/*
> + * cpld_i2c_xfer - sysfs binary I2C passthrough for adva_x1 TAP CPLD.
> + *
> + * write: [addr][write_len][read_len][flags][write_data...]
> + * flags bit 0: I2C_M_NOSTART on the read segment
> + * read: [status][read_data...]
> + * status 0 = success, else positive errno
> + *
> + * Only addresses 0x40 (CPLD) and 0x74 (mux) are permitted.
> + */
[Medium]
Both i2c_bus_ctrl and cpld_i2c_xfer become permanent userspace ABI
including a binary encoded request/response protocol (4-byte header,
status byte, allowlist, NOSTART flag bit), but no
Documentation/ABI/testing/sysfs-driver-ptp-ocp entries are added.
Would documenting the byte layout there rather than only in an inline
C source comment help lock it down as UAPI?
[Low]
As a broader design question rather than a bug: since /dev/i2c-N
(i2c-dev) already exposes arbitrary I2C transactions with the
I2C_RDWR ioctl, is a stateful sysfs bin_attr the right vehicle for
this? Related observations for maintainer consideration:
- i2c_bus_ctrl exposes a raw MMIO register with no semantic model
(any u32 accepted, unspecified bit meanings).
- The CPLD/mux allowlist is hard-coded in ptp_ocp.c rather than
instantiated as proper i2c_client/i2c_mux child devices whose
lifetime is managed by the I2C core.
- The driver does not hold a persistent handle to the adapter and
rediscovers it on every write via device_for_each_child(), which
is fragile if bind order changes or the parent hosts multiple
adapter children.
Once this lands as UAPI it is hard to undo, so it seems worth an
explicit look.
> +#define TAP_I2C_ALLOWED_ADDRS_NUM 2
> +static const u8 tap_i2c_allowed_addrs[TAP_I2C_ALLOWED_ADDRS_NUM] = {
> + 0x40, /* CPLD */
> + 0x74, /* mux */
> +};
> +
> +#define TAP_I2C_REQ_HDR_LEN 4
> +#define TAP_I2C_MAX_WRITE_LEN 67
> +#define TAP_I2C_MAX_READ_LEN 20
> +#define TAP_I2C_FLAG_NOSTART BIT(0)
> +
> +static int
> +ptp_ocp_tap_i2c_find_adapter(struct device *dev, void *data)
> +{
> + struct i2c_adapter **adap = data;
> +
> + *adap = i2c_verify_adapter(dev);
> + return (*adap) ? 1 : 0;
> +}
> +
> +static ssize_t
> +ptp_ocp_cpld_i2c_write(struct file *file, struct kobject *kobj,
> + const struct bin_attribute *attr,
> + char *buf, loff_t off, size_t count)
> +{
> + struct ptp_ocp *bp = dev_get_drvdata(kobj_to_dev(kobj));
> + const u8 *req = (const u8 *)buf;
> + u8 addr, write_len, read_len, flags;
> + struct i2c_adapter *adap = NULL;
> + struct i2c_msg msgs[2];
> + u8 rdbuf[TAP_I2C_MAX_READ_LEN];
> + int nmsgs, ret, i;
> +
> + if (count < TAP_I2C_REQ_HDR_LEN || count > TAP_I2C_REQ_HDR_LEN + TAP_I2C_MAX_WRITE_LEN)
> + return -EINVAL;
[Medium]
Is off ignored on purpose here? If sysfs delivers a large write split
into multiple chunks, or if userspace uses pwrite() at a non-zero
offset, this handler will interpret the fragment starting at off as a
fresh [addr][write_len][read_len][flags] request and generate
arbitrary I2C traffic. The read side already rejects off > 0, so the
two ends are inconsistent.
> + addr = req[0];
> + write_len = req[1];
> + read_len = req[2];
> + flags = req[3];
> +
> + /* Validate */
> + for (i = 0; i < TAP_I2C_ALLOWED_ADDRS_NUM; i++)
> + if (addr == tap_i2c_allowed_addrs[i])
> + break;
> + if (i == TAP_I2C_ALLOWED_ADDRS_NUM)
> + return -EPERM;
> +
> + if (write_len > TAP_I2C_MAX_WRITE_LEN)
> + return -EINVAL;
> + if (read_len > TAP_I2C_MAX_READ_LEN)
> + return -EINVAL;
> + if (write_len + TAP_I2C_REQ_HDR_LEN > count)
> + return -EINVAL;
> + if (write_len == 0 && read_len == 0)
> + return -EINVAL;
> +
> + if (!bp->i2c_ctrl)
> + return -ENODEV;
> + device_for_each_child(&bp->i2c_ctrl->dev, &adap,
> + ptp_ocp_tap_i2c_find_adapter);
> + if (!adap)
> + return -ENODEV;
[High]
Can the adapter go away between this lookup and i2c_transfer()?
device_for_each_child() only holds a klist iterator reference for the
duration of the callback and drops it via klist_iter_exit() before
returning, and i2c_verify_adapter() is just a type check that takes no
reference:
static int
ptp_ocp_tap_i2c_find_adapter(struct device *dev, void *data)
{
struct i2c_adapter **adap = data;
*adap = i2c_verify_adapter(dev);
return (*adap) ? 1 : 0;
}
bp->i2c_ctrl is a platform_device, and the actual I2C adapter is a
child bound by a separate I2C controller driver that can be unbound
via /sys/bus/platform/drivers/<name>/unbind. If that happens between
device_for_each_child() returning and i2c_transfer(adap, ...), the
struct i2c_adapter may already be freed. Would taking an explicit
reference via i2c_get_adapter()/i2c_put_adapter() around the transfer
close this window?
> +
> + nmsgs = 0;
> + if (write_len > 0) {
> + msgs[nmsgs].addr = addr;
> + msgs[nmsgs].flags = 0;
> + msgs[nmsgs].len = write_len;
> + msgs[nmsgs].buf = (u8 *)req + TAP_I2C_REQ_HDR_LEN;
> + nmsgs++;
> + }
> + if (read_len > 0) {
> + u16 rd_flags = I2C_M_RD;
> +
> + if (flags & TAP_I2C_FLAG_NOSTART)
> + rd_flags |= I2C_M_NOSTART;
> + msgs[nmsgs].addr = addr;
> + msgs[nmsgs].flags = rd_flags;
> + msgs[nmsgs].len = read_len;
> + msgs[nmsgs].buf = rdbuf;
> + nmsgs++;
> + }
[Medium]
When userspace supplies flags = TAP_I2C_FLAG_NOSTART together with
write_len = 0 and read_len > 0, I2C_M_NOSTART ends up on the first (and
only) message. Documentation/i2c/i2c-protocol.rst says:
If you set the I2C_M_NOSTART variable for the first partial
message, we do not generate Addr, but we do generate the start
condition S. This will probably confuse all other clients on your
bus, so don't try this.
The CPLD sits on a mux-shared secondary bus (0x74), so should this
combination be rejected outright rather than passed through?
[Medium]
rdbuf lives on the kernel stack and is handed directly to
i2c_transfer(). Documentation/i2c/dma-considerations.rst requires
i2c_msg.buf to be DMA-safe when the underlying adapter uses DMA, and
the DMA characteristics of the adapter behind bp->i2c_ctrl are not
known to this code. Would i2c_get_dma_safe_msg_buf() /
i2c_put_dma_safe_msg_buf() be more appropriate here?
> +
> + ret = i2c_transfer(adap, msgs, nmsgs);
> +
> + mutex_lock(&bp->tap_i2c_lock);
> + if (ret == nmsgs) {
> + bp->tap_i2c_rsp[0] = 0;
> + if (read_len > 0)
> + memcpy(&bp->tap_i2c_rsp[1], rdbuf, read_len);
> + bp->tap_i2c_rsp_len = 1 + read_len;
> + ret = count;
> + } else {
> + bp->tap_i2c_rsp[0] = (u8)(ret < 0 ? -ret : EIO);
> + bp->tap_i2c_rsp_len = 1;
> + ret = (ret < 0) ? ret : -EIO;
> + }
> + mutex_unlock(&bp->tap_i2c_lock);
> +
> + return ret;
> +}
[High]
Can two concurrent users of cpld_i2c_xfer observe each other's
responses via bp->tap_i2c_rsp?
The mutex is only held while publishing the result and is released
before the corresponding read() runs. Consider:
Writer A: i2c_transfer() completes with data X, preempted before
taking tap_i2c_lock
Writer B: i2c_transfer() completes with data Y, publishes Y under
the lock, releases lock
Writer A: resumes, publishes X under the lock
Reader B: reads and gets Writer A's X
Even a single well-behaved userspace process performs write() and
read() as separate syscalls with no session identity, so nothing ties
a read() back to the write() that produced it. For a stateful ISP
session with the CPLD, is there a mechanism (open-exclusive, per-fd
buffer, single write/read syscall pair, ioctl-style) that would keep
transactions correlated?
[Medium]
Is the "positive errno" contract in the header comment held by this
encoding?
bp->tap_i2c_rsp[0] = (u8)(ret < 0 ? -ret : EIO);
Any errno whose low 8 bits are zero (for example -ENOTSUPP is 524 and
truncates to 12, which happens to be ENOMEM; values like 256 or 512
would truncate to 0 and encode "success") ends up in the shadow buffer
as a completely different or false-success value. For userspace ISP
tools that consult this status byte to decide whether to continue
programming, would returning the errno as a wider field (or capping
the values that can appear) be safer?
> +
> +static ssize_t
> +ptp_ocp_cpld_i2c_read(struct file *file, struct kobject *kobj,
> + const struct bin_attribute *attr,
> + char *buf, loff_t off, size_t count)
> +{
> + struct ptp_ocp *bp = dev_get_drvdata(kobj_to_dev(kobj));
> + ssize_t ret;
> +
> + if (off > 0)
> + return 0;
> +
> + mutex_lock(&bp->tap_i2c_lock);
> + ret = min(count, bp->tap_i2c_rsp_len);
> + memcpy(buf, bp->tap_i2c_rsp, ret);
> + mutex_unlock(&bp->tap_i2c_lock);
> + return ret;
> +}
[Medium]
Does this drop response bytes on short reads? off is not honored: if
userspace reads with count < tap_i2c_rsp_len (up to 21 bytes), the
first read returns the leading fragment and advances the file
position, then the next read arrives with off > 0 and this handler
returns 0 (EOF), so the tail of the response is lost. Would the usual
bin_attribute pattern
memcpy(buf, bp->tap_i2c_rsp + off,
min(count, bp->tap_i2c_rsp_len - off));
be more appropriate here?
> +
> +static const struct bin_attribute tap_i2c_bin_attr = {
> + .attr = { .name = "cpld_i2c_xfer", .mode = 0600 },
> + .write = ptp_ocp_cpld_i2c_write,
> + .read = ptp_ocp_cpld_i2c_read,
> +};
> +
[ ... ]
--
This is an AI-generated review.
^ permalink raw reply
* Re: [RFC PATCH] net/iucv: Descend into net/iucv when AFIUCV is enabled
From: Alexandra Winter @ 2026-07-07 9:19 UTC (permalink / raw)
To: Pengpeng Hou, Thorsten Winkler, David Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni
Cc: Simon Horman, Heiko Carstens, linux-s390, netdev, linux-kernel
In-Reply-To: <20260625061303.36326-1-pengpeng@iscas.ac.cn>
On 25.06.26 08:13, Pengpeng Hou wrote:
> AFIUCV can be enabled by the QETH_L3/HiperSockets path even when IUCV
> itself is not enabled. However, the top-level net Makefile only descends
> into net/iucv/ under CONFIG_IUCV.
>
> That creates a Kconfig/Kbuild carrier mismatch: CONFIG_AFIUCV=m can be
> selected, but af_iucv.o is never considered because the containing
> directory is skipped.
>
> This RFC uses an always-descend model for net/iucv/. The subdirectory
> Makefile already gates iucv.o and af_iucv.o on their own Kconfig symbols,
> so entering the directory does not force either provider object on.
>
> This is intentionally RFC because s390 maintainers should confirm whether
> the QETH_L3-only AF_IUCV configuration is intended to build af_iucv.o
> without the base IUCV object.
>
> Signed-off-by: Pengpeng Hou <pengpeng@iscas.ac.cn>
> ---
> net/Makefile | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/net/Makefile b/net/Makefile
> --- a/net/Makefile
> +++ b/net/Makefile
> @@ -45,7 +45,7 @@
> obj-$(CONFIG_MAC80211) += mac80211/
> obj-$(CONFIG_TIPC) += tipc/
> obj-$(CONFIG_NETLABEL) += netlabel/
> -obj-$(CONFIG_IUCV) += iucv/
> +obj-y += iucv/
> obj-$(CONFIG_SMC) += smc/
> obj-$(CONFIG_RFKILL) += rfkill/
> obj-$(CONFIG_NET_9P) += 9p/
I apologize for the late reply, I was on vacation.
I'm looking into this now.
May I ask whether you are using IUCV? Or even AFIUCV over HiperSockets?
^ permalink raw reply
* Re: [PATCH net v2] cxgb4: flower: fix 802.1ad VLAN TPID matching in tc flower filters
From: Harsha Mahadeva @ 2026-07-07 9:15 UTC (permalink / raw)
To: Jagielski, Jedrzej, netdev@vger.kernel.org
Cc: davem@davemloft.net, kuba@kernel.org, edumazet@google.com,
pabeni@redhat.com, andrew+netdev@lunn.ch, Potnuri Bharat Teja
In-Reply-To: <PH0PR11MB590242E0F82125E42DB3F820F0F52@PH0PR11MB5902.namprd11.prod.outlook.com>
On Thursday, July 07/02/26, 2026 at 13:05:42 +0530, Jagielski, Jedrzej wrote:
> From: Harsha M <h.mahadeva@chelsio.com>
> Sent: Thursday, July 2, 2026 6:49 AM
>
> >The cxgb4 driver does not correctly handle tc flower filters that
> >match on an 802.1ad (Q-in-Q) outer VLAN tag. While the VLAN VID is
> >processed, the specified 802.1ad TPID is not programmed into the
> >adapter's outer VLAN matching configuration, resulting in incorrect
> >filter matches.
> >
> >Fix this by configuring the port-specific OVLAN register with the
> >requested TPID value, enabling OVLAN matching through the RX control
> >register, and populating the filter specification with the outer VLAN
> >VID and valid-bit match fields when an 802.1ad TPID is requested.This
> >restores correct matching for tc flower filters that specify an 802.1ad
> >outer VLAN tag
> >
>
> Hi
Hi Jedrzej,
Thanks for reviewing the patch
>
> would be nice to have fixes tag
Sure, I'll add the appropriate Fixes: tag in the next revision
>
> >Signed-off-by: Harsha M <h.mahadeva@chelsio.com>
> >Signed-off-by: Potnuri Bharat Teja <bharat@chelsio.com>
> >---
> >v2:
> >- Compare vlan_tpid with 'cpu_to_be16(ETH_P_8021AD)'
> >- Program the OVLAN register using the ETH_P_8021AD constant
> > and TPID mask as 0xffff instead of deriving the value from the
> > filter key and mask
> >v1:
> >https://lore.kernel.org/all/20260629184417.31b21223@kernel.org/
> >---
> > .../ethernet/chelsio/cxgb4/cxgb4_tc_flower.c | 61 +++++++++++++------
> > drivers/net/ethernet/chelsio/cxgb4/t4_regs.h | 3 +
> > 2 files changed, 46 insertions(+), 18 deletions(-)
> >
> >diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_flower.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_flower.c
> >index 3307e5042681..8c5cfa6982e7 100644
> >--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_flower.c
> >+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_flower.c
> >@@ -40,6 +40,7 @@
> > #include "cxgb4.h"
> > #include "cxgb4_filter.h"
> > #include "cxgb4_tc_flower.h"
> >+#include "t4_regs.h"
> >
> > #define STATS_CHECK_PERIOD (HZ / 2)
> >
> >@@ -266,24 +267,48 @@ static void cxgb4_process_flow_match(struct net_device *dev,
> > VLAN_PRIO_SHIFT);
> > vlan_tci_mask = match.mask->vlan_id | (match.mask->vlan_priority <<
> > VLAN_PRIO_SHIFT);
> >- fs->val.ivlan = vlan_tci;
> >- fs->mask.ivlan = vlan_tci_mask;
> >-
> >- fs->val.ivlan_vld = 1;
> >- fs->mask.ivlan_vld = 1;
> >-
> >- /* Chelsio adapters use ivlan_vld bit to match vlan packets
> >- * as 802.1Q. Also, when vlan tag is present in packets,
> >- * ethtype match is used then to match on ethtype of inner
> >- * header ie. the header following the vlan header.
> >- * So, set the ivlan_vld based on ethtype info supplied by
> >- * TC for vlan packets if its 802.1Q. And then reset the
> >- * ethtype value else, hw will try to match the supplied
> >- * ethtype value with ethtype of inner header.
> >- */
> >- if (fs->val.ethtype == ETH_P_8021Q) {
> >- fs->val.ethtype = 0;
> >- fs->mask.ethtype = 0;
> >+
> >+ if (match.key->vlan_tpid == cpu_to_be16(ETH_P_8021AD)) {
> >+ struct adapter *adap = netdev2adap(dev);
> >+ u32 ovlan_reg, ctl_reg, val, port_id;
> >+
> >+ if (!adap) {
>
> is such scenario really even possible?
You're right. netdev2adap(dev) should always return a valid adapter in this path, so I'll remove the unnecessary NULL check
>
> >+ netdev_err(dev, "%s: adap not found\n", __func__);
> >+ return;
> >+ }
> >+
> >+ val = (0xffff << 16) | ETH_P_8021AD;
> >+ port_id = netdev2pinfo(dev)->port_id;
> >+ fs->val.ovlan = vlan_tci;
> >+ fs->mask.ovlan = vlan_tci_mask;
> >+ fs->val.ovlan_vld = 1;
> >+ fs->mask.ovlan_vld = 1;
> >+ ovlan_reg = PORT_REG(port_id, MPS_PORT_RX_OVLAN0_A);
> >+ ctl_reg = PORT_REG(port_id, MPS_PORT_RX_CTL_A);
> >+ t4_write_reg(adap, ovlan_reg, val);
> >+ val = t4_read_reg(adap, ctl_reg);
> >+ t4_write_reg(adap, ctl_reg, val | 1);
> >+ t4_tp_wr_bits_indirect(adap, TP_INGRESS_CONFIG_A, 1U << 9, 0);
> >+ } else {
> >+ fs->val.ivlan = vlan_tci;
> >+ fs->mask.ivlan = vlan_tci_mask;
> >+ fs->val.ivlan_vld = 1;
> >+ fs->mask.ivlan_vld = 1;
> >+
> >+ /* Chelsio adapters use ivlan_vld bit to match vlan packets
> >+ * as 802.1Q. Also, when vlan tag is present in packets,
> >+ * ethtype match is used then to match on ethtype of inner
> >+ * header ie. the header following the vlan header.
> >+ * So, set the ivlan_vld based on ethtype info supplied by
> >+ * TC for vlan packets if its 802.1Q. And then reset the
> >+ * ethtype value else, hw will try to match the supplied
> >+ * ethtype value with ethtype of inner header.
> >+ */
> >+
>
> isn't this already fs->val.ethtype != ETH_P_8021Q branch and fs has not
> been modified since entering?
This branch is based on match.key->vlan_tpid != ETH_P_8021AD, not on fs->val.ethtype
>
> >+ if (fs->val.ethtype == ETH_P_8021Q) {
> >+ fs->val.ethtype = 0;
> >+ fs->mask.ethtype = 0;
> >+ }
> > }
> > }
> >
> >diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h b/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h
> >index 695916ba0405..38c585f3b1ad 100644
> >--- a/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h
> >+++ b/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h
> >@@ -1921,6 +1921,9 @@
> > #define MAC_PORT_PTP_SUM_LO_A 0x990
> > #define MAC_PORT_PTP_SUM_HI_A 0x994
> >
> >+#define MPS_PORT_RX_OVLAN0_A 0x120
> >+#define MPS_PORT_RX_CTL_A 0X100
> >+
> > #define MPS_CMN_CTL_A 0x9000
> >
> > #define COUNTPAUSEMCRX_S 5
> >--
> >2.43.5
>
>
^ permalink raw reply
* Re: [PATCH net 1/2] net: macb: reprogram TBQP after shuffling the TX ring on link-up
From: Kevin Hao @ 2026-07-07 9:13 UTC (permalink / raw)
To: christian.taedcke
Cc: christian.taedcke-oss, Théo Lebrun, Conor Dooley,
Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Sebastian Andrzej Siewior,
Clark Williams, Steven Rostedt, Robert Hancock, netdev,
linux-kernel, linux-rt-devel, stable
In-Reply-To: <20260706-upstreaming-macb-irq-storm-v1-1-ab3115b5a13a@weidmueller.com>
[-- Attachment #1: Type: text/plain, Size: 3787 bytes --]
On Mon, Jul 06, 2026 at 04:02:14PM +0200, Christian Taedcke via B4 Relay wrote:
> From: Christian Taedcke <christian.taedcke@weidmueller.com>
>
> gem_shuffle_tx_one_ring() rotates the software TX ring so that the
> tail sits at index 0 and resets queue->tx_tail to 0, but it never
> reprograms the hardware transmit buffer queue pointer (TBQP). Other
> paths that reset tx_tail to the ring base (macb_init_buffers() and
> macb_tx_error_task()) also reprogram TBQP to queue->tx_ring_dma; this
> path does not, leaving TBQP pointing at a stale descriptor.
>
> gem_shuffle_tx_rings() runs on every link-up from
> macb_mac_link_up(). After a few link up/down flaps that leave
> un-completed descriptors in the ring, the stale TBQP keeps pointing at
> a descriptor whose used bit is set. When TX is re-enabled on link-up,
> the GEM reads that used descriptor and raises TXUBR. macb_interrupt()
> schedules the TX NAPI, macb_tx_poll() makes no progress (work_done ==
> 0) and macb_tx_restart() re-issues TSTART, which makes the controller
> read the same used descriptor again and re-assert TXUBR. As the MAC
> interrupt is level-triggered, it never deasserts and one CPU is pegged
> at 100% in the threaded handler, eventually triggering "sched: RT
> throttling activated" and a dead network interface.
>
> Fix it by reprogramming TBQP to the ring base on every path of
> gem_shuffle_tx_one_ring() that resets tx_tail to 0, mirroring
> macb_tx_error_task(). The early return for an already-aligned tail is
> left untouched as TBQP is already consistent there. This is safe
> because the shuffle runs from macb_mac_link_up() while TE is still
> disabled, so the transmitter is halted.
>
> Fixes: 881a0263d502 ("net: macb: Shuffle the tx ring before enabling tx")
> Cc: stable@vger.kernel.org
> Assisted-by: Claude:claude-opus-4-8
> Signed-off-by: Christian Taedcke <christian.taedcke@weidmueller.com>
> ---
> drivers/net/ethernet/cadence/macb_main.c | 9 ++++++++-
> 1 file changed, 8 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/net/ethernet/cadence/macb_main.c b/drivers/net/ethernet/cadence/macb_main.c
> index fd282a1700fb..b11cb8f068b7 100644
> --- a/drivers/net/ethernet/cadence/macb_main.c
> +++ b/drivers/net/ethernet/cadence/macb_main.c
> @@ -820,7 +820,7 @@ static void gem_shuffle_tx_one_ring(struct macb_queue *queue)
> if (!count) {
> queue->tx_head = 0;
> queue->tx_tail = 0;
> - goto unlock;
> + goto reset_hw_ptr;
> }
>
> shift = tail % ring_size;
> @@ -869,6 +869,13 @@ static void gem_shuffle_tx_one_ring(struct macb_queue *queue)
> /* Make descriptor updates visible to hardware */
> wmb();
>
> +reset_hw_ptr:
> + /* tx_tail was reset to the ring base, so TBQP must be reprogrammed
> + * to match; otherwise it keeps pointing at a stale descriptor. Safe
> + * to write directly here as TX is still disabled (called from
> + * macb_mac_link_up() before TE is set).
> + */
Could you elaborate on why we need to reprogram the TBQP here? Based on my
understanding, the transmit-buffer queue pointer automatically resets to the
value of TBQP when TX is disabled. The following is quoted from the Zynq
UltraScale TRM [1]:
While transmit is disabled, bit [3] of the network control is
set Low, the transmit-buffer queue pointer resets to point to the address indicated by the
transmit-buffer queue base address register. Disabling receive does not have the same
effect on the receive-buffer queue pointer.
[1] https://docs.amd.com/v/u/en-US/ug1085-zynq-ultrascale-trm
Thanks,
Kevin
> + queue_writel(queue, TBQP, lower_32_bits(queue->tx_ring_dma));
> unlock:
> spin_unlock_irqrestore(&queue->tx_ptr_lock, flags);
> }
>
> --
> 2.54.0
>
>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply
* Re: [PATCH net-next 5/6] net: stmmac: mediatek: add support for TX deallocation adjustment feature
From: Maxime Chevallier @ 2026-07-07 9:11 UTC (permalink / raw)
To: Louis-Alexis Eyraud, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Richard Cochran, Matthias Brugger,
AngeloGioacchino Del Regno, Biao Huang, Maxime Coquelin,
Alexandre Torgue
Cc: rmk+kernel, kernel, netdev, devicetree, linux-kernel,
linux-arm-kernel, linux-mediatek, linux-stm32
In-Reply-To: <20260707-dwmac-mediatek-mt8189-v1-5-17f345eaaca3@collabora.com>
Hi,
On 7/7/26 10:21, Louis-Alexis Eyraud wrote:
> The MT8189 SoC has in the Ethernet control 0 register from the
> peripheral configuration (pericfg) additional bits to adjust the TX
> deallocation.
>
> In preparation of MT8189 SoC support, add its definition, use in the
> set_delay_v2 callback, and a support flag in the platform data.
Can you elaborate a bit on this ? I don't quite get what you mean by
"tx deallocation", this seems to have to do with RGMII timings from
the register access pattern, but the local boolean flag for the feature
is named "use_stage_fine", I'm failing to connect all the dots here
with the different terminology in use :(
>
> Signed-off-by: Louis-Alexis Eyraud <louisalexis.eyraud@collabora.com>
> ---
> .../net/ethernet/stmicro/stmmac/dwmac-mediatek.c | 25 ++++++++++++++++------
> 1 file changed, 19 insertions(+), 6 deletions(-)
>
> diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-mediatek.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-mediatek.c
> index bcc0baef3f71..6b0a42b5839f 100644
> --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-mediatek.c
> +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-mediatek.c
> @@ -37,7 +37,8 @@
> #define ETH_FINE_DLY_RXC BIT(0)
>
> /* Peri Configuration register for mt8189 */
> -#define MT8189_CTRL0_TXC_OUT_OP BIT(20)
> +#define MT8189_CTRL0_TXC_OUT_OP BIT(20)
Extra whitespace inserted here :)
Thanks,
Maxime
^ permalink raw reply
* Re: [PATCH 2/9] ax88179_178a: Add HW support for AX179A-based chips
From: Paolo Abeni @ 2026-07-07 9:11 UTC (permalink / raw)
To: Birger Koblitz, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski
Cc: linux-usb, netdev, linux-kernel
In-Reply-To: <20260701-ax88179a-v1-2-13685df67515@birger-koblitz.de>
On 7/1/26 7:42 AM, Birger Koblitz wrote:
> static int ax88179_reset(struct usbnet *dev)
> {
> - u8 buf[5];
> - u16 *tmp16;
> - u8 *tmp;
> struct ax88179_data *ax179_data = dev->driver_priv;
> struct ethtool_keee eee_data;
> + u16 *tmp16;
> + u8 buf[5];
> + u8 *tmp;
>
> tmp16 = (u16 *)buf;
> tmp = (u8 *)buf;
>
> /* Power up ethernet PHY */
> - *tmp16 = 0;
> - ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL, 2, 2, tmp16);
> + if (ax179_data->chip_version < AX_VERSION_AX88179A) {
> + *tmp16 = 0;
> + ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL, 2, 2, tmp16);
>
> - *tmp16 = AX_PHYPWR_RSTCTL_IPRL;
> - ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL, 2, 2, tmp16);
> - msleep(500);
> + *tmp16 = AX_PHYPWR_RSTCTL_IPRL;
> + ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL, 2, 2, tmp16);
> + msleep(500);
>
> - *tmp = AX_CLK_SELECT_ACS | AX_CLK_SELECT_BCS;
> - ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_CLK_SELECT, 1, 1, tmp);
> - msleep(200);
> + *tmp = AX_CLK_SELECT_ACS | AX_CLK_SELECT_BCS;
> + ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_CLK_SELECT, 1, 1, tmp);
> + msleep(200);
> + } else {
> + *tmp = AX_PHY_POWER;
> + ax88179_write_cmd(dev, AX88179A_PHY_POWER, 0, 0, 1, tmp);
> + msleep(250);
> + }
> +
> + if (ax179_data->chip_version == AX_VERSION_AX88279) {
> + *tmp16 = ax88179_mdio_read(dev->net, dev->mii.phy_id, MII_ADVERTISE);
> + *tmp16 &= ~(ADVERTISE_10FULL | ADVERTISE_10HALF);
> + *tmp16 |= ADVERTISE_RESV; /* Advertise 2.5GBit link */
> + ax88179_mdio_write(dev->net, dev->mii.phy_id, MII_ADVERTISE, *tmp16);
> + }
>
> /* Ethernet PHY Auto Detach*/
> ax88179_auto_detach(dev);
>
> + if (ax179_data->chip_version >= AX_VERSION_AX88179A) {
> + *tmp = AX_MAC_EFF_EN;
> + ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_MAC_BULK_OUT_CTRL, 1, 1, tmp);
> +
> + *tmp16 = 0;
> + ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RX_CTL, 2, 2, tmp16);
> +
> + *tmp = 0x04;
> + ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PAUSE_WATERLVL_LOW, 1, 1, tmp);
> + *tmp = 0x10;
> + ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PAUSE_WATERLVL_HIGH, 1, 1, tmp);
> +
> + *tmp = 0;
> + if (dev->net->features & NETIF_F_HW_VLAN_CTAG_FILTER)
> + *tmp |= AX_VLAN_CONTROL_VFE;
Does not build successfully:
../drivers/net/usb/ax88179_178a.c: In function ‘ax88179_reset’:
../drivers/net/usb/ax88179_178a.c:2376:33: error: ‘AX_VLAN_CONTROL_VFE’
undeclared (first use in this function)
2376 | *tmp |= AX_VLAN_CONTROL_VFE;
Full log here:
https://netdev-ctrl.bots.linux.dev/logs/build/1119419/14655119/build_32bit/stderr
Side process note: the patch series shoudl include the target tree
('net-next' in this case).
/P
> + if (dev->net->features & NETIF_F_HW_VLAN_CTAG_RX)
> + *tmp |= AX_VLAN_CONTROL_VSO;
> + ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_CONTROL, 1, 1, tmp);
> +
> + *tmp = 0xff;
> + ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_MAC_BM_INT_MASK, 1, 1, tmp);
> +
> + *tmp = 0;
> + ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_MAC_BM_RX_DMA_CTL, 1, 1, tmp);
> + ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_MAC_BM_TX_DMA_CTL, 1, 1, tmp);
> + ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_MAC_ARC_CTRL, 1, 1, tmp);
> + ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_MAC_SWP_CTRL, 1, 1, tmp);
> + ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_MAC_TX_HDR_CKSUM, 1, 1, tmp);
> + }
> +
> /* Read MAC address from DTB or asix chip */
> ax88179_get_mac_addr(dev);
> memcpy(dev->net->perm_addr, dev->net->dev_addr, ETH_ALEN);
>
> /* RX bulk configuration */
> - memcpy(tmp, &AX88179_BULKIN_SIZE[0], 5);
> - ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RX_BULKIN_QCTRL, 5, 5, tmp);
> -
> - dev->rx_urb_size = 1024 * 20;
> -
> - *tmp = 0x34;
> - ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PAUSE_WATERLVL_HIGH, 1, 1, tmp);
> -
> - *tmp = 0x52;
> - ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PAUSE_WATERLVL_LOW, 1, 1, tmp);
> + if (ax179_data->chip_version < AX_VERSION_AX88179A) {
> + memcpy(tmp, &AX88179_BULKIN_SIZE[0], 5);
> + ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RX_BULKIN_QCTRL, 5, 5, tmp);
> + *tmp = 0x34;
> + ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PAUSE_WATERLVL_LOW, 1, 1, tmp);
> +
> + *tmp = 0x52;
> + ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PAUSE_WATERLVL_HIGH,
> + 1, 1, tmp);
> + dev->rx_urb_size = 1024 * 20;
> + } else {
> + /* The Bulk-Register configuration for the AX88179A is done in
> + * ax88179a_link_reset(), once the link is up for a given link and USB-speed.
> + */
> + if (ax179_data->is_ax88772d)
> + dev->rx_urb_size = 1024 * 24;
> + else
> + dev->rx_urb_size = 1024 * 48;
> + }
>
> /* Enable checksum offload */
> *tmp = AX_RXCOE_IP | AX_RXCOE_TCP | AX_RXCOE_UDP |
> AX_RXCOE_TCPV6 | AX_RXCOE_UDPV6;
> ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RXCOE_CTL, 1, 1, tmp);
> + ax179_data->rx_checksum = 1;
>
> *tmp = AX_TXCOE_IP | AX_TXCOE_TCP | AX_TXCOE_UDP |
> AX_TXCOE_TCPV6 | AX_TXCOE_UDPV6;
> ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_TXCOE_CTL, 1, 1, tmp);
>
> /* Configure RX control register => start operation */
> - *tmp16 = AX_RX_CTL_DROPCRCERR | AX_RX_CTL_IPE | AX_RX_CTL_START |
> - AX_RX_CTL_AP | AX_RX_CTL_AMALL | AX_RX_CTL_AB;
> - ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RX_CTL, 2, 2, tmp16);
> -
> - *tmp = AX_MONITOR_MODE_PMETYPE | AX_MONITOR_MODE_PMEPOL |
> - AX_MONITOR_MODE_RWMP;
> + ax179_data->rxctl = AX_RX_CTL_DROPCRCERR | AX_RX_CTL_START |
> + AX_RX_CTL_AP | AX_RX_CTL_AMALL | AX_RX_CTL_AB;
> + if (ax179_data->ip_align)
> + ax179_data->rxctl |= AX_RX_CTL_IPE;
> + ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RX_CTL, 2, 2, &ax179_data->rxctl);
> +
> + if (ax179_data->chip_version < AX_VERSION_AX88179A)
> + *tmp = AX_MONITOR_MODE_PMETYPE | AX_MONITOR_MODE_PMEPOL | AX_MONITOR_MODE_RWMP;
> + else
> + *tmp = AX_MONITOR_MODE_RWMP;
> ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MONITOR_MOD, 1, 1, tmp);
>
> /* Configure default medium type => giga */
> *tmp16 = AX_MEDIUM_RECEIVE_EN | AX_MEDIUM_TXFLOW_CTRLEN |
> - AX_MEDIUM_RXFLOW_CTRLEN | AX_MEDIUM_FULL_DUPLEX |
> - AX_MEDIUM_GIGAMODE;
> - ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE,
> - 2, 2, tmp16);
> + AX_MEDIUM_RXFLOW_CTRLEN | AX_MEDIUM_FULL_DUPLEX;
> + if (!ax179_data->is_ax88772d)
> + *tmp16 |= AX_MEDIUM_GIGAMODE;
> + ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE, 2, 2, tmp16);
>
> /* Check if WoL is supported */
> ax179_data->wol_supported = 0;
> @@ -1653,7 +2450,11 @@ static int ax88179_reset(struct usbnet *dev)
> 1, 1, &tmp) > 0)
> ax179_data->wol_supported = WAKE_MAGIC | WAKE_PHY;
>
> - ax88179_led_setting(dev);
> + /* For chips starting with AX88179A, LEDS are configured by the adapter
> + * firmware directly from EEPROM/EFUSE values
> + */
> + if (ax179_data->chip_version < AX_VERSION_AX88179A)
> + ax88179_led_setting(dev);
>
> ax179_data->eee_enabled = 0;
> ax179_data->eee_active = 0;
> @@ -1706,6 +2507,24 @@ static int ax88179_stop(struct usbnet *dev)
> return 0;
> }
>
> +static int ax88179a_stop(struct usbnet *dev)
> +{
> + u16 reg16;
> + u8 reg8;
> +
> + ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE, 2, 2, ®16);
> + reg16 &= ~AX_MEDIUM_RECEIVE_EN;
> + ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE, 2, 2, ®16);
> +
> + reg16 = 0;
> + ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RX_CTL, 2, 2, ®16);
> +
> + reg8 = 0;
> + ax88179_read_cmd(dev, AX88179A_PHY_POWER, 0, 0, 1, ®8);
> +
> + return 0;
> +}
> +
> static const struct driver_info ax88179_info = {
> .description = "ASIX AX88179 USB 3.0 Gigabit Ethernet",
> .bind = ax88179_bind,
> @@ -1732,6 +2551,45 @@ static const struct driver_info ax88178a_info = {
> .tx_fixup = ax88179_tx_fixup,
> };
>
> +static const struct driver_info ax88179a_info = {
> + .description = "ASIX AX88179A USB 3.2 Gigabit Ethernet",
> + .bind = ax88179a_bind,
> + .unbind = ax88179a_unbind,
> + .status = ax88179_status,
> + .link_reset = ax88179a_link_reset,
> + .reset = ax88179_reset,
> + .stop = ax88179a_stop,
> + .flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_MULTI_PACKET | FLAG_AVOID_UNLINK_URBS,
> + .rx_fixup = ax88179a_rx_fixup,
> + .tx_fixup = ax88179a_tx_fixup,
> +};
> +
> +static const struct driver_info ax88772d_info = {
> + .description = "ASIX AX88772D/E USB 2.0 Fast Ethernet",
> + .bind = ax88179a_bind,
> + .unbind = ax88179a_unbind,
> + .status = ax88179_status,
> + .link_reset = ax88179a_link_reset,
> + .reset = ax88179_reset,
> + .stop = ax88179a_stop,
> + .flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_MULTI_PACKET | FLAG_AVOID_UNLINK_URBS,
> + .rx_fixup = ax88179a_rx_fixup,
> + .tx_fixup = ax88179a_tx_fixup,
> +};
> +
> +static const struct driver_info ax88279_info = {
> + .description = "ASIX AX88279 USB 3.2 2.5Gigabit Ethernet",
> + .bind = ax88179a_bind,
> + .unbind = ax88179a_unbind,
> + .status = ax88179_status,
> + .link_reset = ax88179a_link_reset,
> + .reset = ax88179_reset,
> + .stop = ax88179a_stop,
> + .flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_MULTI_PACKET | FLAG_AVOID_UNLINK_URBS,
> + .rx_fixup = ax88179a_rx_fixup,
> + .tx_fixup = ax88179a_tx_fixup,
> +};
> +
> static const struct driver_info cypress_GX3_info = {
> .description = "Cypress GX3 SuperSpeed to Gigabit Ethernet Controller",
> .bind = ax88179_bind,
> @@ -1877,6 +2735,18 @@ static const struct driver_info at_umc2000sp_info = {
>
> static const struct usb_device_id products[] = {
> {
> + /* ASIX AX88179A/B USB 3.2 Gigabit Ethernet */
> + USB_DEVICE_VER(0x0b95, 0x1790, 0x0200, 0x0200),
> + .driver_info = (unsigned long)&ax88179a_info,
> +}, {
> + /* ASIX AX88772D USB 2.0 100Mbit Ethernet */
> + USB_DEVICE_VER(0x0b95, 0x1790, 0x0300, 0x0300),
> + .driver_info = (unsigned long)&ax88772d_info,
> +}, {
> + /* ASIX AX88279 USB 3.2 2.5GBit Ethernet */
> + USB_DEVICE_VER(0x0b95, 0x1790, 0x0400, 0x0400),
> + .driver_info = (unsigned long)&ax88279_info,
> +}, {
> /* ASIX AX88179 10/100/1000 */
> USB_DEVICE_AND_INTERFACE_INFO(0x0b95, 0x1790, 0xff, 0xff, 0),
> .driver_info = (unsigned long)&ax88179_info,
>
^ permalink raw reply
* [PATCH v12 nf-next 7/7] netfilter: nft_flow_offload: Add bridgeflow to nft_flow_offload_eval()
From: Eric Woudstra @ 2026-07-07 9:10 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Pablo Neira Ayuso, Florian Westphal,
Phil Sutter, Nikolay Aleksandrov, Ido Schimmel, Kuniyuki Iwashima,
Stanislav Fomichev, Samiullah Khawaja, Hangbin Liu, Krishna Kumar,
Martin Karsten
Cc: netdev, netfilter-devel, bridge, Eric Woudstra
In-Reply-To: <20260707091045.967678-1-ericwouds@gmail.com>
Edit nft_flow_offload_eval() to make it possible to handle a flowtable of
the nft bridge family.
Use nft_flow_offload_bridge_init() to fill the flow tuples. It uses
nft_dev_fill_bridge_path() in each direction.
Signed-off-by: Eric Woudstra <ericwouds@gmail.com>
---
include/net/netfilter/nf_flow_table.h | 5 +
net/netfilter/nf_flow_table_path.c | 126 ++++++++++++++++++++++++++
net/netfilter/nft_flow_offload.c | 20 +++-
3 files changed, 146 insertions(+), 5 deletions(-)
diff --git a/include/net/netfilter/nf_flow_table.h b/include/net/netfilter/nf_flow_table.h
index 5c6e3b65ae85b..a109eda5250c7 100644
--- a/include/net/netfilter/nf_flow_table.h
+++ b/include/net/netfilter/nf_flow_table.h
@@ -305,6 +305,11 @@ nf_flow_table_offload_del_cb(struct nf_flowtable *flow_table,
void flow_offload_route_init(struct flow_offload *flow,
struct nf_flow_route *route);
+int flow_offload_bridge_init(struct flow_offload *flow,
+ const struct nft_pktinfo *pkt,
+ enum ip_conntrack_dir dir,
+ struct nft_flowtable *ft);
+
int flow_offload_add(struct nf_flowtable *flow_table, struct flow_offload *flow);
void flow_offload_refresh(struct nf_flowtable *flow_table,
struct flow_offload *flow, bool force);
diff --git a/net/netfilter/nf_flow_table_path.c b/net/netfilter/nf_flow_table_path.c
index 2b6ebb594a9ee..cdd6a822cb811 100644
--- a/net/netfilter/nf_flow_table_path.c
+++ b/net/netfilter/nf_flow_table_path.c
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0-only
#include <linux/kernel.h>
#include <linux/module.h>
+#include <linux/if_vlan.h>
#include <linux/init.h>
#include <linux/etherdevice.h>
#include <linux/netlink.h>
@@ -365,3 +366,128 @@ int nft_flow_route(const struct nft_pktinfo *pkt, const struct nf_conn *ct,
return -ENOENT;
}
EXPORT_SYMBOL_GPL(nft_flow_route);
+
+static int nft_dev_fill_bridge_path(struct flow_offload *flow,
+ struct nft_flowtable *ft,
+ enum ip_conntrack_dir dir,
+ const struct net_device *src_dev,
+ const struct net_device *dst_dev,
+ unsigned char *src_ha,
+ unsigned char *dst_ha)
+{
+ struct flow_offload_tuple_rhash *th = flow->tuplehash;
+ struct net_device_path_ctx ctx = {};
+ struct net_device_path_stack stack;
+ struct nft_forward_info info = {};
+ int i, j = 0;
+
+ for (i = th[dir].tuple.encap_num - 1; i >= 0 ; i--) {
+ if (info.num_encaps >= NF_FLOW_TABLE_ENCAP_MAX)
+ return -1;
+
+ if (th[dir].tuple.in_vlan_ingress & BIT(i))
+ continue;
+
+ info.encap[info.num_encaps].id = th[dir].tuple.encap[i].id;
+ info.encap[info.num_encaps].proto = th[dir].tuple.encap[i].proto;
+ info.num_encaps++;
+
+ if (th[dir].tuple.encap[i].proto == htons(ETH_P_PPP_SES))
+ continue;
+
+ if (ctx.num_vlans >= NET_DEVICE_PATH_VLAN_MAX)
+ return -1;
+ ctx.vlan[ctx.num_vlans].id = th[dir].tuple.encap[i].id;
+ ctx.vlan[ctx.num_vlans].proto = th[dir].tuple.encap[i].proto;
+ ctx.num_vlans++;
+ }
+ ctx.dev = src_dev;
+ ether_addr_copy(ctx.daddr, dst_ha);
+
+ if (dev_fill_bridge_path(&ctx, &stack) < 0)
+ return -1;
+
+ nft_dev_path_info(&stack, &info, dst_ha, &ft->data);
+
+ if (!info.indev || info.indev != dst_dev)
+ return -1;
+
+ th[!dir].tuple.iifidx = info.indev->ifindex;
+ for (i = info.num_encaps - 1; i >= 0; i--) {
+ th[!dir].tuple.encap[j].id = info.encap[i].id;
+ th[!dir].tuple.encap[j].proto = info.encap[i].proto;
+ if (info.ingress_vlans & BIT(i))
+ th[!dir].tuple.in_vlan_ingress |= BIT(j);
+ j++;
+ }
+ th[!dir].tuple.encap_num = info.num_encaps;
+
+ th[dir].tuple.mtu = dst_dev->mtu;
+ ether_addr_copy(th[dir].tuple.out.h_source, src_ha);
+ ether_addr_copy(th[dir].tuple.out.h_dest, dst_ha);
+ th[dir].tuple.out.ifidx = info.outdev->ifindex;
+ th[dir].tuple.xmit_type = FLOW_OFFLOAD_XMIT_DIRECT;
+
+ return 0;
+}
+
+int flow_offload_bridge_init(struct flow_offload *flow,
+ const struct nft_pktinfo *pkt,
+ enum ip_conntrack_dir dir,
+ struct nft_flowtable *ft)
+{
+ const struct net_device *in_dev, *out_dev;
+ struct ethhdr *eth = eth_hdr(pkt->skb);
+ struct flow_offload_tuple *tuple;
+ int err, i = 0;
+
+ in_dev = nft_in(pkt);
+ if (!in_dev || !nft_flowtable_find_dev(in_dev, ft))
+ return -1;
+
+ out_dev = nft_out(pkt);
+ if (!out_dev || !nft_flowtable_find_dev(out_dev, ft))
+ return -1;
+
+ tuple = &flow->tuplehash[!dir].tuple;
+
+ if (skb_vlan_tag_present(pkt->skb)) {
+ tuple->encap[i].id = skb_vlan_tag_get(pkt->skb);
+ tuple->encap[i].proto = pkt->skb->vlan_proto;
+ i++;
+ }
+
+ switch (eth_hdr(pkt->skb)->h_proto) {
+ case htons(ETH_P_8021Q): {
+ struct vlan_hdr *vhdr = (struct vlan_hdr *)(skb_mac_header(pkt->skb)
+ + sizeof(struct ethhdr));
+ tuple->encap[i].id = ntohs(vhdr->h_vlan_TCI);
+ tuple->encap[i].proto = htons(ETH_P_8021Q);
+ i++;
+ break;
+ }
+ case htons(ETH_P_PPP_SES): {
+ struct pppoe_hdr *phdr = (struct pppoe_hdr *)(skb_mac_header(pkt->skb)
+ + sizeof(struct ethhdr));
+
+ tuple->encap[i].id = ntohs(phdr->sid);
+ tuple->encap[i].proto = htons(ETH_P_PPP_SES);
+ i++;
+ break;
+ }
+ }
+ tuple->encap_num = i;
+
+ err = nft_dev_fill_bridge_path(flow, ft, !dir, out_dev, in_dev,
+ eth->h_dest, eth->h_source);
+ if (err < 0)
+ return err;
+
+ err = nft_dev_fill_bridge_path(flow, ft, dir, in_dev, out_dev,
+ eth->h_source, eth->h_dest);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(flow_offload_bridge_init);
diff --git a/net/netfilter/nft_flow_offload.c b/net/netfilter/nft_flow_offload.c
index 0be62841155b6..d0d63ef7cecd5 100644
--- a/net/netfilter/nft_flow_offload.c
+++ b/net/netfilter/nft_flow_offload.c
@@ -53,6 +53,7 @@ static void nft_flow_offload_eval(const struct nft_expr *expr,
{
struct nft_flow_offload *priv = nft_expr_priv(expr);
struct nf_flowtable *flowtable = &priv->flowtable->data;
+ bool routing = flowtable->type->family != NFPROTO_BRIDGE;
struct tcphdr _tcph, *tcph = NULL;
struct nf_flow_route route = {};
enum ip_conntrack_info ctinfo;
@@ -109,14 +110,21 @@ static void nft_flow_offload_eval(const struct nft_expr *expr,
goto out;
dir = CTINFO2DIR(ctinfo);
- if (nft_flow_route(pkt, ct, &route, dir, priv->flowtable) < 0)
- goto err_flow_route;
+ if (routing) {
+ if (nft_flow_route(pkt, ct, &route, dir, priv->flowtable) < 0)
+ goto err_flow_route;
+ }
flow = flow_offload_alloc(ct);
if (!flow)
goto err_flow_alloc;
- flow_offload_route_init(flow, &route);
+ if (routing)
+ flow_offload_route_init(flow, &route);
+ else
+ if (flow_offload_bridge_init(flow, pkt, dir, priv->flowtable) < 0)
+ goto err_flow_add;
+
if (tcph)
flow_offload_ct_tcp(ct);
@@ -164,8 +172,10 @@ static void nft_flow_offload_eval(const struct nft_expr *expr,
err_flow_add:
flow_offload_free(flow);
err_flow_alloc:
- dst_release(route.tuple[dir].dst);
- dst_release(route.tuple[!dir].dst);
+ if (routing) {
+ dst_release(route.tuple[dir].dst);
+ dst_release(route.tuple[!dir].dst);
+ }
err_flow_route:
clear_bit(IPS_OFFLOAD_BIT, &ct->status);
out:
--
2.53.0
^ permalink raw reply related
* [PATCH v12 nf-next 6/7] netfilter: nft_flow_offload: Add NFPROTO_BRIDGE to validate
From: Eric Woudstra @ 2026-07-07 9:10 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Pablo Neira Ayuso, Florian Westphal,
Phil Sutter, Nikolay Aleksandrov, Ido Schimmel, Kuniyuki Iwashima,
Stanislav Fomichev, Samiullah Khawaja, Hangbin Liu, Krishna Kumar,
Martin Karsten
Cc: netdev, netfilter-devel, bridge, Eric Woudstra
In-Reply-To: <20260707091045.967678-1-ericwouds@gmail.com>
Need to add NFPROTO_BRIDGE to nft_flow_offload_validate() to support
the bridge-fastpath.
Reviewed-by: Nikolay Aleksandrov <razor@blackwall.org>
Signed-off-by: Eric Woudstra <ericwouds@gmail.com>
---
net/netfilter/nft_flow_offload.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/net/netfilter/nft_flow_offload.c b/net/netfilter/nft_flow_offload.c
index 4f68fb64f1657..0be62841155b6 100644
--- a/net/netfilter/nft_flow_offload.c
+++ b/net/netfilter/nft_flow_offload.c
@@ -179,7 +179,8 @@ static int nft_flow_offload_validate(const struct nft_ctx *ctx,
if (ctx->family != NFPROTO_IPV4 &&
ctx->family != NFPROTO_IPV6 &&
- ctx->family != NFPROTO_INET)
+ ctx->family != NFPROTO_INET &&
+ ctx->family != NFPROTO_BRIDGE)
return -EOPNOTSUPP;
return nft_chain_validate_hooks(ctx->chain, hook_mask);
--
2.53.0
^ permalink raw reply related
* [PATCH v12 nf-next 5/7] netfilter: nft_flow_offload: nft_flow_offload_eval: check thoff==0
From: Eric Woudstra @ 2026-07-07 9:10 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Pablo Neira Ayuso, Florian Westphal,
Phil Sutter, Nikolay Aleksandrov, Ido Schimmel, Kuniyuki Iwashima,
Stanislav Fomichev, Samiullah Khawaja, Hangbin Liu, Krishna Kumar,
Martin Karsten
Cc: netdev, netfilter-devel, bridge, Eric Woudstra
In-Reply-To: <20260707091045.967678-1-ericwouds@gmail.com>
In case of flow through bridge, when evaluating traffic with double vlan,
pppoe and pppoe-in-q. In this case thoff will be valid only when meta has
been processed. If meta was not processed in nftables, thoff is zero.
Signed-off-by: Eric Woudstra <ericwouds@gmail.com>
---
net/netfilter/nft_flow_offload.c | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/net/netfilter/nft_flow_offload.c b/net/netfilter/nft_flow_offload.c
index f8c7f9f631e48..4f68fb64f1657 100644
--- a/net/netfilter/nft_flow_offload.c
+++ b/net/netfilter/nft_flow_offload.c
@@ -59,7 +59,7 @@ static void nft_flow_offload_eval(const struct nft_expr *expr,
struct flow_offload *flow;
enum ip_conntrack_dir dir;
struct nf_conn *ct;
- int ret;
+ int ret, thoff;
if (nft_flow_offload_skip(pkt->skb, nft_pf(pkt)))
goto out;
@@ -70,8 +70,11 @@ static void nft_flow_offload_eval(const struct nft_expr *expr,
switch (ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum) {
case IPPROTO_TCP:
- tcph = skb_header_pointer(pkt->skb, nft_thoff(pkt),
- sizeof(_tcph), &_tcph);
+ thoff = nft_thoff(pkt);
+ if (thoff == 0)
+ goto out;
+ tcph = skb_header_pointer(pkt->skb, thoff, sizeof(_tcph),
+ &_tcph);
if (unlikely(!tcph || tcph->fin || tcph->rst ||
!nf_conntrack_tcp_established(ct)))
goto out;
--
2.53.0
^ permalink raw reply related
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