* Re: [PATCH v5 23/27] clk: mediatek: Add MT8196 disp-ao clock support
From: Laura Nao @ 2026-04-02 10:05 UTC (permalink / raw)
To: jason-jh.lin
Cc: Guangjie.Song, Nancy.Lin, Paul-pl.Chen,
Project_Global_Chrome_Upstream_Group, Singo.Chang, Sirius.Wang,
angelogioacchino.delregno, conor+dt, devicetree, kernel, krzk+dt,
laura.nao, linux-arm-kernel, linux-clk, linux-kernel,
linux-mediatek, matthias.bgg, mturquette, netdev, nfraprado,
p.zabel, richardcochran, robh, sboyd, wenst
In-Reply-To: <2d418383ff2d6ff40ffb3b4f8e2b0c0e665c3b58.camel@mediatek.com>
Hi Jason-JH,
On 4/2/26 08:30, Jason-JH Lin (林睿祥) wrote:
> On Fri, 2025-08-29 at 11:19 +0200, Laura Nao wrote:
>> Add support for the MT8196 disp-ao clock controller, which provides
>> clock gate control for the display system. It is integrated with the
>> mtk-mmsys driver, which registers the disp-ao clock driver via
>> platform_device_register_data().
>>
>> Reviewed-by: Nícolas F. R. A. Prado <nfraprado@collabora.com>
>> Reviewed-by: AngeloGioacchino Del Regno
>> <angelogioacchino.delregno@collabora.com>
>> Signed-off-by: Laura Nao <laura.nao@collabora.com>
>> ---
>> drivers/clk/mediatek/Makefile | 2 +-
>> drivers/clk/mediatek/clk-mt8196-vdisp_ao.c | 80
>> ++++++++++++++++++++++
>> 2 files changed, 81 insertions(+), 1 deletion(-)
>> create mode 100644 drivers/clk/mediatek/clk-mt8196-vdisp_ao.c
>>
>> diff --git a/drivers/clk/mediatek/Makefile
>> b/drivers/clk/mediatek/Makefile
>> index fe5699411d8b..5b8969ff1985 100644
>> --- a/drivers/clk/mediatek/Makefile
>> +++ b/drivers/clk/mediatek/Makefile
>> @@ -157,7 +157,7 @@ obj-$(CONFIG_COMMON_CLK_MT8196_IMP_IIC_WRAP) +=
>> clk-mt8196-imp_iic_wrap.o
>> obj-$(CONFIG_COMMON_CLK_MT8196_MCUSYS) += clk-mt8196-mcu.o
>> obj-$(CONFIG_COMMON_CLK_MT8196_MDPSYS) += clk-mt8196-mdpsys.o
>> obj-$(CONFIG_COMMON_CLK_MT8196_MFGCFG) += clk-mt8196-mfg.o
>> -obj-$(CONFIG_COMMON_CLK_MT8196_MMSYS) += clk-mt8196-disp0.o clk-
>> mt8196-disp1.o
>> +obj-$(CONFIG_COMMON_CLK_MT8196_MMSYS) += clk-mt8196-disp0.o clk-
>> mt8196-disp1.o clk-mt8196-vdisp_ao.o
>> obj-$(CONFIG_COMMON_CLK_MT8196_PEXTPSYS) += clk-mt8196-pextp.o
>> obj-$(CONFIG_COMMON_CLK_MT8196_UFSSYS) += clk-mt8196-ufs_ao.o
>> obj-$(CONFIG_COMMON_CLK_MT8365) += clk-mt8365-apmixedsys.o clk-
>> mt8365.o
>> diff --git a/drivers/clk/mediatek/clk-mt8196-vdisp_ao.c
>> b/drivers/clk/mediatek/clk-mt8196-vdisp_ao.c
>> new file mode 100644
>> index 000000000000..fddb69d1c3eb
>> --- /dev/null
>> +++ b/drivers/clk/mediatek/clk-mt8196-vdisp_ao.c
>> @@ -0,0 +1,80 @@
>> +// SPDX-License-Identifier: GPL-2.0-only
>> +/*
>> + * Copyright (c) 2025 MediaTek Inc.
>> + * Guangjie Song <guangjie.song@mediatek.com>
>> + * Copyright (c) 2025 Collabora Ltd.
>> + * Laura Nao <laura.nao@collabora.com>
>> + */
>> +#include <dt-bindings/clock/mediatek,mt8196-clock.h>
>> +
>> +#include <linux/clk-provider.h>
>> +#include <linux/module.h>
>> +#include <linux/of_device.h>
>> +#include <linux/platform_device.h>
>> +
>> +#include "clk-gate.h"
>> +#include "clk-mtk.h"
>> +
>> +static const struct mtk_gate_regs mm_v_cg_regs = {
>> + .set_ofs = 0x104,
>> + .clr_ofs = 0x108,
>> + .sta_ofs = 0x100,
>> +};
>> +
>> +static const struct mtk_gate_regs mm_v_hwv_regs = {
>> + .set_ofs = 0x0030,
>> + .clr_ofs = 0x0034,
>> + .sta_ofs = 0x2c18,
>> +};
>> +
>> +#define GATE_MM_AO_V(_id, _name, _parent, _shift) { \
>> + .id = _id, \
>> + .name = _name, \
>> + .parent_name = _parent, \
>> + .regs = &mm_v_cg_regs, \
>> + .shift = _shift, \
>> + .ops = &mtk_clk_gate_ops_setclr, \
>> + .flags = CLK_OPS_PARENT_ENABLE | \
>> + CLK_IS_CRITICAL, \
>> + }
>> +
>> +#define GATE_HWV_MM_V(_id, _name, _parent, _shift) { \
>> + .id = _id, \
>> + .name = _name, \
>> + .parent_name = _parent, \
>> + .regs = &mm_v_cg_regs, \
>> + .hwv_regs = &mm_v_hwv_regs, \
>> + .shift = _shift, \
>> + .ops = &mtk_clk_gate_hwv_ops_setclr, \
>> + .flags = CLK_OPS_PARENT_ENABLE, \
>> + }
>> +
>> +static const struct mtk_gate mm_v_clks[] = {
>> + GATE_HWV_MM_V(CLK_MM_V_DISP_VDISP_AO_CONFIG,
>> "mm_v_disp_vdisp_ao_config", "disp", 0),
>> + GATE_HWV_MM_V(CLK_MM_V_DISP_DPC, "mm_v_disp_dpc", "disp",
>> 16),
>> + GATE_MM_AO_V(CLK_MM_V_SMI_SUB_SOMM0, "mm_v_smi_sub_somm0",
>> "disp", 2),
>> +};
>> +
>> +static const struct mtk_clk_desc mm_v_mcd = {
>> + .clks = mm_v_clks,
>> + .num_clks = ARRAY_SIZE(mm_v_clks),
>> +};
>> +
>> +static const struct of_device_id of_match_clk_mt8196_vdisp_ao[] = {
>> + { .compatible = "mediatek,mt8196-vdisp-ao", .data =
>> &mm_v_mcd },
>
> Hi Laura,
>
> We are going to send mtk-mmsys driver for MT8196 recently, but we found
> the compatible name is used here.
>
> As your commit message, vdisp-ao is integrated with the mtk-mmsys
> driver, which registers the vdisp-ao clock driver via
> platform_device_register_data().
>
> Shouldn't this compatible name belong to mmsys driver for MT8196?
>
That's right, my fault for missing that! Thanks for the heads up.
I'm aware Angelo is currently restructuring mediatek-drm (including
mmsys and mutex), and that might affect the way vdisp-ao is loaded too.
So I'm not sure whether it makes sense to send a patch to fix this
right away.
Best,
Laura
^ permalink raw reply
* Re: [PATCH net v6 1/4] selftests: Migrate nsim-only MACsec tests to Python
From: Sabrina Dubroca @ 2026-04-02 10:03 UTC (permalink / raw)
To: Cosmin Ratiu
Cc: netdev, Andrew Lunn, David S . Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Simon Horman, Stanislav Fomichev,
Shuah Khan, linux-kselftest, Dragos Tatulea
In-Reply-To: <20260330130130.989236-2-cratiu@nvidia.com>
2026-03-30, 16:01:27 +0300, Cosmin Ratiu wrote:
> -#
> -# test ethtool features when toggling offload
> -#
> -
> -ip link add link $NSIM_NETDEV $MACSEC_NETDEV type macsec offload mac
> -TMP_FEATS_ON_1="$(ethtool -k $MACSEC_NETDEV)"
> -
> -ip link set $MACSEC_NETDEV type macsec offload off
> -TMP_FEATS_OFF_1="$(ethtool -k $MACSEC_NETDEV)"
> -
> -ip link set $MACSEC_NETDEV type macsec offload mac
> -TMP_FEATS_ON_2="$(ethtool -k $MACSEC_NETDEV)"
> -
> -[ "$TMP_FEATS_ON_1" = "$TMP_FEATS_ON_2" ]
> -check $?
[...]
> -TMP_FEATS_OFF_2="$(ethtool -k $MACSEC_NETDEV)"
> -[ "$TMP_FEATS_OFF_1" = "$TMP_FEATS_OFF_2" ]
> -check $?
[...]
> -TMP_FEATS_ON_3="$(ethtool -k $MACSEC_NETDEV)"
> -[ "$TMP_FEATS_ON_1" = "$TMP_FEATS_ON_3" ]
> -check $?
Those feature comparisons are missing in the new python test. The rest
of the patch looks good.
--
Sabrina
^ permalink raw reply
* Re: [PATCH v4 net-next 5/5] net: pcs: pcs-mtk-lynxi: deprecate "mediatek,pnswap"
From: Vladimir Oltean @ 2026-04-02 9:53 UTC (permalink / raw)
To: Frank Wunderlich
Cc: netdev, devicetree, linux-kernel, linux-mediatek, Daniel Golle,
Horatiu Vultur, Bj√∏rn Mork, Andrew Lunn,
Heiner Kallweit, Russell King, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Matthias Brugger, AngeloGioacchino Del Regno,
Eric Woudstra, Alexander Couzens, Chester A. Unal, DENG Qingfang,
Sean Wang, Felix Fietkau
In-Reply-To: <4dbc3dabfdbc3bdf6b8d411e62a27fa8988e3388@linux.dev>
On Thu, Apr 02, 2026 at 05:50:33AM +0000, Frank Wunderlich wrote:
> Hi,
Hi,
Please don't top-post :(
> i tried using these properties in sgmiisys0 node (which should be mapped to mac0 and the mt7530 switch) without success [1].
>
> it looks like these properties are not read somewhere.
Can you please clarify whether your problem is with the SerDes connected
to a switch port or to a GMAC?
Because if to a switch port, mt7531_create_sgmii() doesn't have any
phandle to the SGMIISYS. That was from existing code.
pcs = mtk_pcs_lynxi_create(priv->dev, NULL, regmap,
MT7531_PHYA_CTRL_SIGNAL3);
The LynxI PCS will be instantiated without a fwnode and only the
defaults will apply.
> the flow is
>
> mtk_probe (eth driver)
>
> if (MTK_HAS_CAPS(eth->soc->caps, MTK_SGMII)) {
> err = mtk_sgmii_init(eth);
>
> and there calling mtk_pcs_lynxi_create with the sgmiisys-node (for each mac, so imho mac0=sgmiisys0)
> but handling the sgmiisys only as syscon, not a "real" pcs node [2].
>
> but your new code calls phy_get_tx_polarity and should read out this properties, but from subnode "pcs", so next try was
>
> &sgmiisys0 {
> pcs {
> rx-polarity = <PHY_POL_NORMAL>;
> tx-polarity = <PHY_POL_INVERT>;
> };
> };
>
> which results in completely strange behaviour (looks like sgmiisys1 is mapped to mac0, but based on code in mtk_sgmii_init 0=0 should be right):
>
> [ 2.765218] SGMSYS_QPHY_WRAP_CTRL = 0x501, will write 0x500
> [ 9.143849] SGMSYS_QPHY_WRAP_CTRL = 0x500, will write 0x501
>
> but nevertheless i tried changing sgmiisys0 to sgmiisys1 and got the dame result as before
>
> [ 2.713644] SGMSYS_QPHY_WRAP_CTRL = 0x501, will write 0x500
> [ 9.061509] SGMSYS_QPHY_WRAP_CTRL = 0x500, will write 0x500
>
> i can only change the second serdes with sgmiisys0, but not the first.
I assume the second SerDes is mapped to a GMAC port which does
instantiate the LynxI PCS with a fwnode, right? If so, the behaviour is
consistent with the code. Only mtk-soc-eth uses mediatek,sgmiisys AFAICS.
> mapping between mac and sgmiisys in dts in mt7986a.dtsi [3] are like this:
>
> eth: ethernet@15100000 {
> compatible = "mediatek,mt7986-eth";
> mediatek,sgmiisys = <&sgmiisys0>, <&sgmiisys1>;
> ...
> };
>
> ð {
> status = "okay";
>
> gmac0: mac@0 {
> compatible = "mediatek,eth-mac";
> ...
> };
>
> gmac1: mac@1 {
> compatible = "mediatek,eth-mac";
> ...
> };
> };
>
> maybe it is time to revive the PCS framework discussion ([4]-[6])?
>
> [1] https://github.com/frank-w/BPI-Router-Linux/commit/4846a7bb352fe5911136cba33813f099bac035fd
> [2] https://elixir.bootlin.com/linux/v7.0-rc4/source/drivers/net/ethernet/mediatek/mtk_eth_soc.c#L5001
> [3] https://elixir.bootlin.com/linux/v7.0-rc4/source/arch/arm64/boot/dts/mediatek/mt7986a.dtsi#L528
>
> [4] * https://patchwork.kernel.org/project/netdevbpf/patch/20250610233134.3588011-4-sean.anderson@linux.dev/ (v6)
> > pcs-framework itself had not yet got a response from netdev maintainer (only other parts)
> [5] * https://patchwork.kernel.org/project/netdevbpf/patch/20250511201250.3789083-4-ansuelsmth@gmail.com/ (v4)
> > discussion: https://lore.kernel.org/netdev/20250511201250.3789083-1-ansuelsmth@gmail.com/
> [6] * https://patchwork.kernel.org/project/netdevbpf/patch/ba4e359584a6b3bc4b3470822c42186d5b0856f9.1721910728.git.daniel@makrotopia.org/
> > discussion: https://patchwork.kernel.org/project/netdevbpf/patch/8aa905080bdb6760875d62cb3b2b41258837f80e.1702352117.git.daniel@makrotopia.org/
I'm not exactly sure how device+driver for the PCS devices would help in
this case though? Because the LynxI PCS driver would just retrieve the
fwnode on its own, rather than it being passed by the mtk_pcs_lynxi_create()
caller?
We need to have a very good model of what happens when the PCS provider
goes away, especially in multi-port scenarios. It is a similar issue as
to what happens when a phy_device goes away.
https://lore.kernel.org/netdev/20260311153421.u454m3e4blkstymt@skbuf/
I'm not saying "let's not do that", but we'd effectively introducing an
issue that currently does not exist, with the PCS lifetime being managed
by the consumer.
Do you have any better idea by now why SGMSYS_QPHY_WRAP_CTRL is 0x501
for SGMIISYS #0? Is that its out-of-reset value?
^ permalink raw reply
* [PATCH net v3] tipc: fix bc_ackers underflow on duplicate GRP_ACK_MSG
From: Oleh Konko @ 2026-04-02 9:48 UTC (permalink / raw)
To: netdev@vger.kernel.org
Cc: jmaloy@redhat.com, davem@davemloft.net, edumazet@google.com,
kuba@kernel.org, pabeni@redhat.com, horms@kernel.org,
tipc-discussion@lists.sourceforge.net,
linux-kernel@vger.kernel.org, stable@vger.kernel.org
The GRP_ACK_MSG handler in tipc_group_proto_rcv() currently decrements
bc_ackers on every inbound group ACK, even when the same member has
already acknowledged the current broadcast round.
Because bc_ackers is a u16, a duplicate ACK received after the last
legitimate ACK wraps the counter to 65535. Once wrapped,
tipc_group_bc_cong() keeps reporting congestion and later group
broadcasts on the affected socket stay blocked until the group is
recreated.
Fix this by ignoring duplicate or stale ACKs before touching bc_acked or
bc_ackers. This makes repeated GRP_ACK_MSG handling idempotent and
prevents the underflow path.
Fixes: 2f487712b893 ("tipc: guarantee that group broadcast doesn't bypass group unicast")
Cc: stable@vger.kernel.org
Signed-off-by: Oleh Konko <security@1seal.org>
---
v3:
- correct the Fixes tag to the commit that introduced GRP_ACK_MSG and bc_ackers
v2:
- make duplicate or stale GRP_ACK_MSG a full no-op via early return
- place acked in reverse xmas tree style
net/tipc/group.c | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/net/tipc/group.c b/net/tipc/group.c
index e0e6227b433..14e6732624e 100644
--- a/net/tipc/group.c
+++ b/net/tipc/group.c
@@ -746,6 +746,7 @@ void tipc_group_proto_rcv(struct tipc_group *grp, bool *usr_wakeup,
u32 port = msg_origport(hdr);
struct tipc_member *m, *pm;
u16 remitted, in_flight;
+ u16 acked;
if (!grp)
return;
@@ -798,7 +799,10 @@ void tipc_group_proto_rcv(struct tipc_group *grp, bool *usr_wakeup,
case GRP_ACK_MSG:
if (!m)
return;
- m->bc_acked = msg_grp_bc_acked(hdr);
+ acked = msg_grp_bc_acked(hdr);
+ if (less_eq(acked, m->bc_acked))
+ return;
+ m->bc_acked = acked;
if (--grp->bc_ackers)
return;
list_del_init(&m->small_win);
--
2.50.0
^ permalink raw reply related
* Re: [PATCH net-next v7] selftests: net: add tests for PPP
From: Paolo Abeni @ 2026-04-02 9:48 UTC (permalink / raw)
To: Qingfang Deng, Jakub Kicinski, Dianne Skoll
Cc: Shuah Khan, David S. Miller, Eric Dumazet, Simon Horman,
Felix Maurer, Sebastian Andrzej Siewior, Matthieu Baerts (NGI0),
linux-kernel, linux-kselftest, linux-ppp, netdev, Paul Mackerras
In-Reply-To: <CALW65jaTbaosbO8ypKZ6G37HxuxXabeTaP_Fhq72xTovmDj+8g@mail.gmail.com>
On 4/2/26 4:26 AM, Qingfang Deng wrote:
> On Thu, Apr 2, 2026 at 8:56 AM Jakub Kicinski <kuba@kernel.org> wrote:
>> On Wed, 1 Apr 2026 23:45:38 +0800 Qingfang Deng wrote:
>>>> # iperf3: error - unable to connect to server - server may have stopped running or use a different port, firewall issue, etc.: Network is unreachable
>>>> # TEST: PPPoE [FAIL]
>>>> not ok 1 selftests: net/ppp: pppoe.sh # exit=1
>>>
>>> It looks like pppoe-server fails to start. You may check the syslog to
>>> see what's going on.
>>
>> Hm, we don't capture syslog automatically :S We capture stdout and
>> stderr
>>
>> I'm not even sure there's something listening on syslog in virtme-ng
>> Is it possible to make the daemon log to stdout or a file and dump
>> that file to stdout on failure? Even if we fix the current issue
>> it will be quite inconvenient if we have to go into syslog every time
>> the test is failing. The test output should provide enough info to
>> debug failures.
>
> Add Cc: Dianne
>
> I checked the man page of pppoe-server and did not find such option.
> We can instead start our own listener and redirect the syslog to a
> file or stdout:
>
> socat -u UNIX-RECV:/dev/log,ignoreeof STDOUT &
Note that similar failures in the past in other test-cases were usually
due to timing issues. i.e. the pppoe-server starts in background too
late for the client.
Possibly a somewhat clean solution is to wait in a loop for the
'pppoe-server ready' event, somewhat similar to wait_local_port_listen().
Otherwise an hackish sleep in between should do.
Note that you should be possibly able to reproduce this kind of failure
in a virtualized/slow enough environment (i.e. using vng as described on
nipa wiki)
Finally, note that in this test script you should start the socat
listener only if /dev/log does not exists, or ignore socat startup
errors - because self-test should run also on different systems.
/P
^ permalink raw reply
* Re: [REGRESSION] net: add xmit recursion limit to tunnel xmit functions
From: Paolo Abeni @ 2026-04-02 9:31 UTC (permalink / raw)
To: Jakub Kicinski, Chris Arges
Cc: Weiming Shi, Eric Dumazet, netdev, kernel-team, jgriege
In-Reply-To: <20260401173843.7a2bb607@kernel.org>
On 4/2/26 2:38 AM, Jakub Kicinski wrote:
> On Tue, 31 Mar 2026 15:09:33 -0500 Chris Arges wrote:
>> Commit 6f1a9140ecda ("net: add xmit recursion limit to tunnel xmit functions")
>> introduces a regression in our systems using legitimate multi-level tunnel
>> encapsulation stacks. Our testing was with stable 6.12.y and 6.18.y where this
>> patch was backported.
>>
>> One of the usecases we have are packets being sent to a GRE device, then
>> MPLS/LWT route, then IPIP device, then SIT/FOU device. What we observed is that
>> packets are getting dropped at the last device and we get the message "Dead
>> loop on virtual device" sent from the iptunnel_xmit function.
>
> Last device meaning limit of 5 would have worked for you?
> I don't think there was any hard math behind the 4 so ++
> on the limit may be simplest, but I defer to Paolo..
AFAICS the '4' limit was an empirical choice, and the reported splat
used 7 devices, so moving to 5 should fit, even if I must admit it feels
a bit a whack a mole situation. On the flip side a revert of the blamed
commit feels not safe.
@Chris, could you please share a patch increasing the limit to 5?
Thanks,
Paolo
^ permalink raw reply
* Re: [net-next PATCH 04/10] bitfield.h: add FIELD_WIDTH()
From: David Laight @ 2026-04-02 9:27 UTC (permalink / raw)
To: Luiz Angelo Daros de Luca
Cc: Yury Norov, Andrew Lunn, Vladimir Oltean, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
Linus Walleij, Alvin Šipraga, Yury Norov, Rasmus Villemoes,
Russell King, netdev, linux-kernel
In-Reply-To: <CAJq09z7+cA9_1fWzkrJebutJD0b1BGhrC1sUyoqqb=ddKCAXcw@mail.gmail.com>
On Thu, 2 Apr 2026 01:00:20 -0300
Luiz Angelo Daros de Luca <luizluca@gmail.com> wrote:
> > > +/**
> > > + * FIELD_WIDTH() - return the width of a bitfield
> > > + * @_mask: shifted mask defining the field's length and position
> > > + *
> > > + * Returns the number of contiguous bits covered by @_mask.
> > > + * This corresponds to the bit width of FIELD_MAX(@_mask).
> > > + */
> > > +#define FIELD_WIDTH(_mask) \
> >
> > Please no underscored names unless necessary.
>
> I used _mask to maintain consistency with the existing public macros
> in this file, such as FIELD_GET, FIELD_PREP, FIELD_MAX, and FIELD_FIT.
> All of them use the underscore prefix for parameters. Should I diverge
> from them?
>
> > > + ({ \
> > > + __BF_FIELD_CHECK(_mask, 0ULL, 0ULL, "FIELD_WIDTH: "); \
> > > + __bf_shf(~FIELD_MAX(_mask)); \
> > > + })
> >
> > I believe, this should be:
> >
> > #define FIELD_WIDTH(mask) ({ \
> > __BF_FIELD_CHECK_MASK(mask, 0ULL, "FIELD_WIDTH: "); \
> > HWEIGHT(mask); \
> > })
>
> HWEIGHT() is indeed much cleaner. However, to keep bitfield.h
> self-contained and avoid adding more includes, I'll try
> __builtin_popcountll() in a similar way __builtin_ffsll is already
> used.
>
> I also noticed the suggestion to use __BF_FIELD_CHECK_MASK instead of
> __BF_FIELD_CHECK. Both FIELD_MAX and FIELD_FIT currently use the full
> __BF_FIELD_CHECK with 0ULL as a dummy register to ensure the mask fits
> within the header's supported limits. If __BF_FIELD_CHECK_MASK is
> preferred for FIELD_WIDTH, I can certainly use it, but should we also
> update FIELD_MAX and FIELD_FIT for consistency?
All of the calls with the 0ULL placeholder (especially for the register)
should really be removed.
Last time I looked there where some calls that only had placeholders.
They just bloat the pre-processor output and slow down compilation.
>
> I intend to send a v2 with the following implementation:
>
> #define __bf_shf(x) (__builtin_ffsll(x) - 1)
> +#define __bf_hweight(x) __builtin_popcountll(x)
>
> #define __scalar_type_to_unsigned_cases(type) \
> unsigned type: (unsigned type)0, \
> @@ -111,6 +112,19 @@
> (typeof(_mask))((_mask) >> __bf_shf(_mask)); \
> })
>
> +/**
> + * FIELD_WIDTH() - return the width of a bitfield
> + * @_mask: shifted mask defining the field's length and position
> + *
> + * Returns the number of contiguous bits covered by @_mask.
> + * This corresponds to the bit width of FIELD_MAX(@_mask).
> + */
> +#define FIELD_WIDTH(_mask) \
> + ({ \
You ought to have:
auto _fw_mask = mask;
here. While _mask has to be a constant, if it comes from GENMASK()
it is very long.
> + __BF_FIELD_CHECK_MASK(_mask, 0ULL, "FIELD_WIDTH: "); \
> + (typeof(_mask))__bf_hweight(_mask); \
Why the cast of the result?
They are everywhere in that file, and many are pointless.
But there is no point adding another one.
I'm not even sure you need the extra define.
Just use __builtin_popcountll().
David
> + })
> +
>
> Regards,
>
> Luiz
>
^ permalink raw reply
* Re: "Dead loop on virtual device" error without softirq-BKL on PREEMPT_RT
From: Daniel Vacek @ 2026-04-02 9:21 UTC (permalink / raw)
To: Sebastian Andrzej Siewior
Cc: edumazet, kuba, linux-kernel, linux-rt-devel, netdev, spasswolf,
tglx, Aaron Tomlin
In-Reply-To: <20260402083123.1H4kyAEn@linutronix.de>
On Thu, 2 Apr 2026 at 10:31, Sebastian Andrzej Siewior
<bigeasy@linutronix.de> wrote:
> On 2026-04-02 09:50:35 [+0200], Daniel Vacek wrote:
> > My idea was that the non-preemptible one can record `current` task
> > instead of the CPU to detect the deadlock. And that would also work
> > for the preemptible case (it would actually match the lock owner
> > approach as you did for the PREEMPT_RT case).
> > One code for both configurations, no special-casing. I'd argue that's
> > a better result. Am I missing something?
> >
> > The size of the netdev_queue structure would grow by 8 bytes for !RT
> > case, but that's not a big deal, IMO. For RT case it would just fill
> > the hole.
>
> We have xmit_lock_owner as int. If you replace it with task_struct *
> then on 64bit the size of the struct netdev_queue will remain unchanged
> as it fills the hole before the following long.
> Then you could record `current' as the lock owner in both cases. This
> should work.
Well, that's the patch I originally sent then.
https://lore.kernel.org/linux-rt-devel/20260318103009.2120920-1-neelx@suse.com/
--nX
>
> Sebastian
^ permalink raw reply
* Re: [PATCH net] net/sched: taprio: fix NULL pointer dereference in class dump
From: Paolo Abeni @ 2026-04-02 9:15 UTC (permalink / raw)
To: Weiming Shi, Vinicius Costa Gomes, Jamal Hadi Salim, Jiri Pirko,
David S . Miller, Eric Dumazet, Jakub Kicinski
Cc: Simon Horman, netdev, Xiang Mei, stable
In-Reply-To: <20260330102904.2677818-5-bestswngs@gmail.com>
On 3/30/26 12:29 PM, Weiming Shi wrote:
> When a TAPRIO child qdisc is deleted via RTM_DELQDISC, taprio_graft()
> is called with new == NULL and stores NULL into q->qdiscs[cl - 1].
> Subsequent RTM_GETTCLASS dump operations walk all classes via
> taprio_walk() and call taprio_dump_class(), which calls taprio_leaf()
> returning the NULL pointer, then dereferences it to read child->handle,
> causing a kernel NULL pointer dereference.
>
> The bug is reachable with namespace-scoped CAP_NET_ADMIN on any kernel
> with CONFIG_NET_SCH_TAPRIO enabled. On systems with unprivileged user
> namespaces enabled, an unprivileged local user can trigger a kernel
> panic by creating a taprio qdisc inside a new network namespace,
> grafting an explicit child qdisc, deleting it, and requesting a class
> dump. The RTM_GETTCLASS dump itself requires no capability.
>
> Oops: general protection fault, probably for non-canonical address 0xdffffc0000000007: 0000 [#1] SMP KASAN NOPTI
> KASAN: null-ptr-deref in range [0x0000000000000038-0x000000000000003f]
> RIP: 0010:taprio_dump_class (net/sched/sch_taprio.c:2475)
> Call Trace:
> <TASK>
> tc_fill_tclass (net/sched/sch_api.c:1966)
> qdisc_class_dump (net/sched/sch_api.c:2329)
> taprio_walk (net/sched/sch_taprio.c:2510)
> tc_dump_tclass_qdisc (net/sched/sch_api.c:2353)
> tc_dump_tclass_root (net/sched/sch_api.c:2370)
> tc_dump_tclass (net/sched/sch_api.c:2431)
> rtnl_dumpit (net/core/rtnetlink.c:6827)
> netlink_dump (net/netlink/af_netlink.c:2325)
> rtnetlink_rcv_msg (net/core/rtnetlink.c:6927)
> netlink_rcv_skb (net/netlink/af_netlink.c:2550)
> </TASK>
>
> Fix this by substituting &noop_qdisc when new is NULL in
> taprio_graft(), following the same pattern used by multiq_graft() and
> prio_graft(). This ensures q->qdiscs[] slots are never NULL, making
> all consumer paths (dump, enqueue, dequeue) safe. The noop_qdisc is a
> kernel-global builtin qdisc that drops all packets, which is
> functionally equivalent to a NULL child for data path purposes. The
> refcount increment and flag modification are guarded with
> != &noop_qdisc to avoid modifying the global singleton.
>
> Fixes: 665338b2a7a0 ("net/sched: taprio: dump class stats for the actual q->qdiscs[]")
> Cc: stable@vger.kernel.org
> Reported-by: Xiang Mei <xmei5@asu.edu>
> Signed-off-by: Weiming Shi <bestswngs@gmail.com>
> ---
> net/sched/sch_taprio.c | 7 +++++--
> 1 file changed, 5 insertions(+), 2 deletions(-)
>
> diff --git a/net/sched/sch_taprio.c b/net/sched/sch_taprio.c
> index f721c03514f60..cecaef16c0dd1 100644
> --- a/net/sched/sch_taprio.c
> +++ b/net/sched/sch_taprio.c
> @@ -2183,6 +2183,9 @@ static int taprio_graft(struct Qdisc *sch, unsigned long cl,
> if (!dev_queue)
> return -EINVAL;
>
> + if (!new)
> + new = &noop_qdisc;
Sashiko says:
---
Does replacing a deleted child with &noop_qdisc instead of NULL leak the
qlen and backlog counters of the root taprio qdisc?
Before this change, taprio_enqueue() checked for a NULL child and dropped
the packet safely without incrementing the root qdisc's stats. With the
child now set to &noop_qdisc, the check passes and execution proceeds to
taprio_enqueue_one().
Inside taprio_enqueue_one(), the root qdisc's qlen and backlog are
incremented before calling the child's enqueue function:
taprio_enqueue_one() {
...
sch->q.qlen++;
sch->qstats.backlog += qdisc_pkt_len(skb);
...
qdisc_enqueue(skb, child, to_free);
}
When the child is noop_qdisc, its enqueue callback drops the packet and
returns a drop status. However, taprio_enqueue_one() does not appear to
roll back the incremented qlen and backlog counters to account for the
drop.
Can this permanently inflate the root qdisc's queue length and backlog,
breaking statistics and disabling transmission fast-path bypasses?
---
It looks like you additionally need to replace NULL checks for
q->qdiscs[] with check vs '&noop_qdisc' in many places.
/P
^ permalink raw reply
* Re: [PATCH v4] net: caif: fix stack out-of-bounds write in cfctrl_link_setup()
From: Paolo Abeni @ 2026-04-02 9:05 UTC (permalink / raw)
To: Kangzheng Gu, davem, edumazet, kuba, horms, kees, thorsten.blum,
arnd, sjur.brandeland
Cc: netdev, linux-kernel, stable
In-Reply-To: <20260330065342.145549-1-xiaoguai0992@gmail.com>
On 3/30/26 8:53 AM, Kangzheng Gu wrote:
> diff --git a/net/caif/cfctrl.c b/net/caif/cfctrl.c
> index c6cc2bfed65d..373ab1dc67a7 100644
> --- a/net/caif/cfctrl.c
> +++ b/net/caif/cfctrl.c
> @@ -416,8 +416,16 @@ static int cfctrl_link_setup(struct cfctrl *cfctrl, struct cfpkt *pkt, u8 cmdrsp
> cp = (u8 *) linkparam.u.rfm.volume;
> for (tmp = cfpkt_extr_head_u8(pkt);
> cfpkt_more(pkt) && tmp != '\0';
> - tmp = cfpkt_extr_head_u8(pkt))
> + tmp = cfpkt_extr_head_u8(pkt)) {
> + if (cp >= (u8 *)linkparam.u.rfm.volume +
> + sizeof(linkparam.u.rfm.volume) - 1) {
> + pr_warn("Request reject, volume name length exceeds %zu\n",
> + sizeof(linkparam.u.rfm.volume));
It looks like this printk is remotely triggerable from each incoming
(malformed) packet. It should be rate-limited.
Thanks,
Paolo
^ permalink raw reply
* Re: [PATCH v10 net-next 08/11] net/nebula-matrix: add vsi resource implementation
From: Mohsin Bashir @ 2026-04-02 9:06 UTC (permalink / raw)
To: illusion.wang, dimon.zhao, alvin.wang, sam.chen, netdev
Cc: andrew+netdev, corbet, kuba, linux-doc, lorenzo, pabeni, horms,
vadim.fedorenko, lukas.bulwahn, edumazet, enelsonmoore, skhan,
hkallweit1, jani.nikula, open list
In-Reply-To: <20260401022318.28550-9-illusion.wang@nebula-matrix.com>
On 3/31/26 7:23 PM, illusion.wang wrote:
> The HW (Hardware) layer code can have a quick review since it is
> highly chip-specific.
> Chip initialization includes the initialization of the DP module, the
> intf module, and the P4 registers.
> The initialization of the DP module encompasses the initialization of
> the dped(downstream pkt edit), uped(upstream pkt edit), dsch(downstream
> schedule), ustore, dstore, dvn, uvn, and uqm modules.
>
> Signed-off-by: illusion.wang <illusion.wang@nebula-matrix.com>
> ---
> .../net/ethernet/nebula-matrix/nbl/Makefile | 1 +
> .../nbl/nbl_hw/nbl_hw_leonis/nbl_hw_leonis.c | 406 ++++++++++++++++++
> .../nbl/nbl_hw/nbl_hw_leonis/nbl_hw_leonis.h | 12 +-
> .../nbl_hw_leonis/nbl_resource_leonis.c | 4 +
> .../nebula-matrix/nbl/nbl_hw/nbl_resource.h | 1 +
> .../nebula-matrix/nbl/nbl_hw/nbl_vsi.c | 51 +++
> .../nebula-matrix/nbl/nbl_hw/nbl_vsi.h | 11 +
> .../nbl/nbl_include/nbl_def_hw.h | 4 +
> .../nbl/nbl_include/nbl_include.h | 31 ++
> 9 files changed, 520 insertions(+), 1 deletion(-)
> create mode 100644 drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_vsi.c
> create mode 100644 drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_vsi.h
>
> diff --git a/drivers/net/ethernet/nebula-matrix/nbl/Makefile b/drivers/net/ethernet/nebula-matrix/nbl/Makefile
> index a56e722a5ac7..241bbb572b5e 100644
> --- a/drivers/net/ethernet/nebula-matrix/nbl/Makefile
> +++ b/drivers/net/ethernet/nebula-matrix/nbl/Makefile
> @@ -10,6 +10,7 @@ nbl-objs += nbl_common/nbl_common.o \
> nbl_hw/nbl_hw_leonis/nbl_hw_leonis_regs.o \
> nbl_hw/nbl_resource.o \
> nbl_hw/nbl_interrupt.o \
> + nbl_hw/nbl_vsi.o \
> nbl_core/nbl_dispatch.o \
> nbl_core/nbl_dev.o \
> nbl_main.o
> diff --git a/drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_hw_leonis/nbl_hw_leonis.c b/drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_hw_leonis/nbl_hw_leonis.c
> index aa5e91c2b278..37cfd322d422 100644
> --- a/drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_hw_leonis/nbl_hw_leonis.c
> +++ b/drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_hw_leonis/nbl_hw_leonis.c
> @@ -8,6 +8,7 @@
> #include <linux/io.h>
> #include <linux/spinlock.h>
> #include "nbl_hw_leonis.h"
> +#include "nbl_hw_leonis_regs.h"
>
> static void nbl_hw_read_mbx_regs(struct nbl_hw_mgt *hw_mgt, u64 reg,
> u32 *data, u32 len)
> @@ -63,6 +64,408 @@ static void nbl_hw_wr_regs(struct nbl_hw_mgt *hw_mgt, u64 reg, const u32 *data,
> spin_unlock(&hw_mgt->reg_lock);
> }
>
> +static u32 nbl_hw_get_quirks(struct nbl_hw_mgt *hw_mgt)
> +{
> + u32 quirks;
> +
> + nbl_hw_read_mbx_regs(hw_mgt, NBL_LEONIS_QUIRKS_OFFSET, &quirks,
> + sizeof(u32));
> +
> + if (quirks == NBL_LEONIS_ILLEGAL_REG_VALUE)
> + return 0;
> +
> + return quirks;
> +}
> +
> +static void nbl_configure_dped_checksum(struct nbl_hw_mgt *hw_mgt)
> +{
> + union dped_l4_ck_cmd_40_u l4_ck_cmd_40;
> +
> + /* DPED dped_l4_ck_cmd_40 for sctp */
> + nbl_hw_rd_regs(hw_mgt, NBL_DPED_L4_CK_CMD_40_ADDR, (u32 *)&l4_ck_cmd_40,
> + sizeof(l4_ck_cmd_40));
> + l4_ck_cmd_40.info.en = 1;
> + nbl_hw_wr_regs(hw_mgt, NBL_DPED_L4_CK_CMD_40_ADDR, (u32 *)&l4_ck_cmd_40,
> + sizeof(l4_ck_cmd_40));
> +}
> +
> +static int nbl_dped_init(struct nbl_hw_mgt *hw_mgt)
> +{
> + nbl_hw_wr32(hw_mgt, NBL_DPED_VLAN_OFFSET, 0xC);
> + nbl_hw_wr32(hw_mgt, NBL_DPED_DSCP_OFFSET_0, 0x8);
> + nbl_hw_wr32(hw_mgt, NBL_DPED_DSCP_OFFSET_1, 0x4);
> +
> + /* dped checksum offload */
> + nbl_configure_dped_checksum(hw_mgt);
> +
> + return 0;
> +}
> +
> +static int nbl_uped_init(struct nbl_hw_mgt *hw_mgt)
> +{
> + struct ped_hw_edit_profile hw_edit;
> +
> + nbl_hw_rd_regs(hw_mgt, NBL_UPED_HW_EDT_PROF_TABLE(NBL_DPED_V4_TCP_IDX),
> + (u32 *)&hw_edit, sizeof(hw_edit));
> + hw_edit.l3_len = 0;
> + nbl_hw_wr_regs(hw_mgt, NBL_UPED_HW_EDT_PROF_TABLE(NBL_DPED_V4_TCP_IDX),
> + (u32 *)&hw_edit, sizeof(hw_edit));
> +
> + nbl_hw_rd_regs(hw_mgt, NBL_UPED_HW_EDT_PROF_TABLE(NBL_DPED_V6_TCP_IDX),
> + (u32 *)&hw_edit, sizeof(hw_edit));
> + hw_edit.l3_len = 1;
> + nbl_hw_wr_regs(hw_mgt, NBL_UPED_HW_EDT_PROF_TABLE(NBL_DPED_V6_TCP_IDX),
> + (u32 *)&hw_edit, sizeof(hw_edit));
> +
> + return 0;
> +}
> +
> +static void nbl_shaping_eth_init(struct nbl_hw_mgt *hw_mgt, u8 eth_id, u8 speed)
> +{
> + struct nbl_shaping_dvn_dport dvn_dport = { 0 };
> + struct nbl_shaping_dport dport = { 0 };
> + u32 rate, half_rate;
> +
> + if (speed == NBL_FW_PORT_SPEED_100G) {
> + rate = NBL_SHAPING_DPORT_100G_RATE;
> + half_rate = NBL_SHAPING_DPORT_HALF_100G_RATE;
> + } else {
> + rate = NBL_SHAPING_DPORT_25G_RATE;
> + half_rate = NBL_SHAPING_DPORT_HALF_25G_RATE;
> + }
> +
> + dport.cir = rate;
> + dport.pir = rate;
> + dport.depth = max(dport.cir * 2, NBL_LR_LEONIS_NET_BUCKET_DEPTH);
> + dport.cbs = dport.depth;
> + dport.pbs = dport.depth;
> + dport.valid = 1;
> +
> + dvn_dport.cir = half_rate;
> + dvn_dport.pir = rate;
> + dvn_dport.depth = dport.depth;
> + dvn_dport.cbs = dvn_dport.depth;
> + dvn_dport.pbs = dvn_dport.depth;
> + dvn_dport.valid = 1;
> +
> + nbl_hw_wr_regs(hw_mgt, NBL_SHAPING_DPORT_REG(eth_id), (u32 *)&dport,
> + sizeof(dport));
> + nbl_hw_wr_regs(hw_mgt, NBL_SHAPING_DVN_DPORT_REG(eth_id),
> + (u32 *)&dvn_dport, sizeof(dvn_dport));
> +}
> +
> +static int nbl_shaping_init(struct nbl_hw_mgt *hw_mgt, u8 speed)
> +{
> +#define NBL_SHAPING_FLUSH_INTERVAL 128
> + struct nbl_shaping_net net_shaping = { 0 };
> + struct dsch_psha_en psha_en = { 0 };
> + int i;
> +
> + for (i = 0; i < NBL_MAX_ETHERNET; i++)
> + nbl_shaping_eth_init(hw_mgt, i, speed);
> +
> + psha_en.en = 0xF;
> + nbl_hw_wr_regs(hw_mgt, NBL_DSCH_PSHA_EN_ADDR, (u32 *)&psha_en,
> + sizeof(psha_en));
> +
> + for (i = 0; i < NBL_MAX_FUNC; i++) {
> + nbl_hw_wr_regs(hw_mgt, NBL_SHAPING_NET_REG(i),
> + (u32 *)&net_shaping, sizeof(net_shaping));
> + if ((i % NBL_SHAPING_FLUSH_INTERVAL) == 0)
> + nbl_flush_writes(hw_mgt);
> + }
> + nbl_flush_writes(hw_mgt);
> + return 0;
> +}
> +
> +static int nbl_dsch_qid_max_init(struct nbl_hw_mgt *hw_mgt)
> +{
> + struct dsch_vn_quanta quanta = { 0 };
> +
> + quanta.h_qua = NBL_HOST_QUANTA;
> + quanta.e_qua = NBL_ECPU_QUANTA;
> + nbl_hw_wr_regs(hw_mgt, NBL_DSCH_VN_QUANTA_ADDR, (u32 *)&quanta,
> + sizeof(quanta));
> + nbl_hw_wr32(hw_mgt, NBL_DSCH_HOST_QID_MAX, NBL_MAX_QUEUE_ID);
> +
> + nbl_hw_wr32(hw_mgt, NBL_DVN_ECPU_QUEUE_NUM, 0);
> + nbl_hw_wr32(hw_mgt, NBL_UVN_ECPU_QUEUE_NUM, 0);
> +
> + return 0;
> +}
> +
> +static int nbl_ustore_init(struct nbl_hw_mgt *hw_mgt, u8 eth_num)
> +{
> + struct nbl_ustore_port_drop_th drop_th = { 0 };
> + struct ustore_pkt_len pkt_len;
> + int i;
> +
> + nbl_hw_rd_regs(hw_mgt, NBL_USTORE_PKT_LEN_ADDR, (u32 *)&pkt_len,
> + sizeof(pkt_len));
Why are we issuing read while ignoring return values? Issuing read to
clear registers? A comment here would help.
> + /* min arp packet length 42 (14 + 28) */
> + pkt_len.min = 42;
> + nbl_hw_wr_regs(hw_mgt, NBL_USTORE_PKT_LEN_ADDR, (u32 *)&pkt_len,
> + sizeof(pkt_len));
> +
> + drop_th.en = 1;
> + if (eth_num == 1)
> + drop_th.disc_th = NBL_USTORE_SINGLE_ETH_DROP_TH;
> + else if (eth_num == 2)
> + drop_th.disc_th = NBL_USTORE_DUAL_ETH_DROP_TH;
> + else
> + drop_th.disc_th = NBL_USTORE_QUAD_ETH_DROP_TH;
> +
> + for (i = 0; i < NBL_MAX_ETHERNET; i++)
> + nbl_hw_wr_regs(hw_mgt, NBL_USTORE_PORT_DROP_TH_REG_ARR(i),
> + (u32 *)&drop_th, sizeof(drop_th));
> +
> + for (i = 0; i < NBL_MAX_ETHERNET; i++) {
> + nbl_hw_rd32(hw_mgt, NBL_USTORE_BUF_PORT_DROP_PKT(i));
> + nbl_hw_rd32(hw_mgt, NBL_USTORE_BUF_PORT_TRUN_PKT(i));
> + }
> +
> + return 0;
> +}
> +
> +static int nbl_dstore_init(struct nbl_hw_mgt *hw_mgt, u8 speed)
> +{
> + struct dstore_port_drop_th drop_th;
> + struct dstore_d_dport_fc_th fc_th;
> + struct dstore_disc_bp_th bp_th;
> + int i;
> +
> + for (i = 0; i < NBL_DSTORE_PORT_DROP_TH_DEPTH; i++) {
> + nbl_hw_rd_regs(hw_mgt, NBL_DSTORE_PORT_DROP_TH_REG(i),
> + (u32 *)&drop_th, sizeof(drop_th));
> + drop_th.en = 0;
> + nbl_hw_wr_regs(hw_mgt, NBL_DSTORE_PORT_DROP_TH_REG(i),
> + (u32 *)&drop_th, sizeof(drop_th));
> + }
> +
> + nbl_hw_rd_regs(hw_mgt, NBL_DSTORE_DISC_BP_TH, (u32 *)&bp_th,
> + sizeof(bp_th));
> + bp_th.en = 1;
> + nbl_hw_wr_regs(hw_mgt, NBL_DSTORE_DISC_BP_TH, (u32 *)&bp_th,
> + sizeof(bp_th));
> +
> + for (i = 0; i < NBL_MAX_ETHERNET; i++) {
> + nbl_hw_rd_regs(hw_mgt, NBL_DSTORE_D_DPORT_FC_TH_REG(i),
> + (u32 *)&fc_th, sizeof(fc_th));
> + if (speed == NBL_FW_PORT_SPEED_100G) {
> + fc_th.xoff_th = NBL_DSTORE_DROP_XOFF_TH_100G;
> + fc_th.xon_th = NBL_DSTORE_DROP_XON_TH_100G;
> + } else {
> + fc_th.xoff_th = NBL_DSTORE_DROP_XOFF_TH;
> + fc_th.xon_th = NBL_DSTORE_DROP_XON_TH;
> + }
> +
> + fc_th.fc_en = 1;
> + nbl_hw_wr_regs(hw_mgt, NBL_DSTORE_D_DPORT_FC_TH_REG(i),
> + (u32 *)&fc_th, sizeof(fc_th));
> + }
> +
> + return 0;
> +}
> +
> +static void nbl_dvn_descreq_num_cfg(struct nbl_hw_mgt *hw_mgt, u32 descreq_num)
> +{
> + u32 split_ring_num = (descreq_num >> 16) & 0xffff;
> + struct nbl_dvn_descreq_num_cfg num_cfg = { 0 };
> + u32 packet_ring_num = descreq_num & 0xffff;
> +
> + packet_ring_num =
> + clamp(packet_ring_num, PACKET_RING_MIN, PACKET_RING_MAX);
> + num_cfg.packed_l1_num =
> + (packet_ring_num - PACKET_RING_BASE) / PACKET_RING_DIV;
> +
> + split_ring_num = clamp(split_ring_num, SPLIT_RING_MIN,
> + SPLIT_RING_MAX);
> + num_cfg.avring_cfg_num = split_ring_num > SPLIT_RING_MIN ?
> + SPLIT_RING_CFG_16 :
> + SPLIT_RING_CFG_8;
> +
> + nbl_hw_wr_regs(hw_mgt, NBL_DVN_DESCREQ_NUM_CFG, (u32 *)&num_cfg,
> + sizeof(num_cfg));
> +}
> +
> +static int nbl_dvn_init(struct nbl_hw_mgt *hw_mgt, u8 speed)
> +{
> + struct nbl_dvn_desc_wr_merge_timeout timeout = { 0 };
> + struct nbl_dvn_dif_req_rd_ro_flag ro_flag = { 0 };
> +
> + timeout.cfg_cycle = DEFAULT_DVN_DESC_WR_MERGE_TIMEOUT_MAX;
> + nbl_hw_wr_regs(hw_mgt, NBL_DVN_DESC_WR_MERGE_TIMEOUT, (u32 *)&timeout,
> + sizeof(timeout));
> +
> + ro_flag.rd_desc_ro_en = 1;
> + ro_flag.rd_data_ro_en = 1;
> + ro_flag.rd_avring_ro_en = 1;
> + nbl_hw_wr_regs(hw_mgt, NBL_DVN_DIF_REQ_RD_RO_FLAG, (u32 *)&ro_flag,
> + sizeof(ro_flag));
> +
> + if (speed == NBL_FW_PORT_SPEED_100G)
> + nbl_dvn_descreq_num_cfg(hw_mgt,
> + DEFAULT_DVN_100G_DESCREQ_NUMCFG);
> + else
> + nbl_dvn_descreq_num_cfg(hw_mgt, DEFAULT_DVN_DESCREQ_NUMCFG);
> +
> + return 0;
> +}
> +
> +static int nbl_uvn_init(struct nbl_hw_mgt *hw_mgt)
> +{
> + struct uvn_desc_prefetch_init prefetch_init = { 0 };
> + struct uvn_desc_wr_timeout desc_wr_timeout = { 0 };
> + struct uvn_dif_req_ro_flag flag = { 0 };
> + struct uvn_queue_err_mask mask = { 0 };
> + u16 wr_timeout = 0x12c;
> + u32 timeout = 119760; /* 200us 200000/1.67 */
> + u32 quirks;
> +
> + nbl_hw_wr32(hw_mgt, NBL_UVN_DESC_RD_WAIT, timeout);
> +
> + desc_wr_timeout.num = wr_timeout;
> + nbl_hw_wr_regs(hw_mgt, NBL_UVN_DESC_WR_TIMEOUT, (u32 *)&desc_wr_timeout,
> + sizeof(desc_wr_timeout));
> +
> + flag.avail_rd = 1;
> + flag.desc_rd = 1;
> + flag.pkt_wr = 1;
> + flag.desc_wr = 0;
> + nbl_hw_wr_regs(hw_mgt, NBL_UVN_DIF_REQ_RO_FLAG, (u32 *)&flag,
> + sizeof(flag));
> +
> + nbl_hw_rd_regs(hw_mgt, NBL_UVN_QUEUE_ERR_MASK, (u32 *)&mask,
> + sizeof(mask));
> + mask.dif_err = 1;
> + nbl_hw_wr_regs(hw_mgt, NBL_UVN_QUEUE_ERR_MASK, (u32 *)&mask,
> + sizeof(mask));
> +
> + prefetch_init.num = NBL_UVN_DESC_PREFETCH_NUM;
> + prefetch_init.sel = 0;
> + quirks = nbl_hw_get_quirks(hw_mgt);
> + if (!(quirks & BIT(NBL_QUIRKS_UVN_PREFETCH_ALIGN)))
> + prefetch_init.sel = 1;
> + nbl_hw_wr_regs(hw_mgt, NBL_UVN_DESC_PREFETCH_INIT,
> + (u32 *)&prefetch_init, sizeof(prefetch_init));
> +
> + return 0;
> +}
> +
> +static int nbl_uqm_init(struct nbl_hw_mgt *hw_mgt)
> +{
> + struct nbl_uqm_que_type que_type = { 0 };
> + u32 cnt = 0;
> + int i;
> +
> + nbl_hw_wr_regs(hw_mgt, NBL_UQM_FWD_DROP_CNT, &cnt, sizeof(cnt));
> +
> + nbl_hw_wr_regs(hw_mgt, NBL_UQM_DROP_PKT_CNT, &cnt, sizeof(cnt));
> + nbl_hw_wr_regs(hw_mgt, NBL_UQM_DROP_PKT_SLICE_CNT, &cnt,
> + sizeof(cnt));
> + nbl_hw_wr_regs(hw_mgt, NBL_UQM_DROP_PKT_LEN_ADD_CNT, &cnt,
> + sizeof(cnt));
> + nbl_hw_wr_regs(hw_mgt, NBL_UQM_DROP_HEAD_PNTR_ADD_CNT, &cnt,
> + sizeof(cnt));
> + nbl_hw_wr_regs(hw_mgt, NBL_UQM_DROP_WEIGHT_ADD_CNT, &cnt,
> + sizeof(cnt));
> +
> + for (i = 0; i < NBL_UQM_PORT_DROP_DEPTH; i++) {
> + nbl_hw_wr_regs(hw_mgt,
> + NBL_UQM_PORT_DROP_PKT_CNT + (sizeof(cnt) * i),
> + &cnt, sizeof(cnt));
> + nbl_hw_wr_regs(hw_mgt,
> + NBL_UQM_PORT_DROP_PKT_SLICE_CNT +
> + (sizeof(cnt) * i),
> + &cnt, sizeof(cnt));
> + nbl_hw_wr_regs(hw_mgt,
> + NBL_UQM_PORT_DROP_PKT_LEN_ADD_CNT +
> + (sizeof(cnt) * i),
> + &cnt, sizeof(cnt));
> + nbl_hw_wr_regs(hw_mgt,
> + NBL_UQM_PORT_DROP_HEAD_PNTR_ADD_CNT +
> + (sizeof(cnt) * i),
> + &cnt, sizeof(cnt));
> + nbl_hw_wr_regs(hw_mgt,
> + NBL_UQM_PORT_DROP_WEIGHT_ADD_CNT +
> + (sizeof(cnt) * i),
> + &cnt, sizeof(cnt));
> + }
> +
> + for (i = 0; i < NBL_UQM_DPORT_DROP_DEPTH; i++)
> + nbl_hw_wr_regs(hw_mgt,
> + NBL_UQM_DPORT_DROP_CNT + (sizeof(cnt) * i),
> + &cnt, sizeof(cnt));
> +
> + que_type.bp_drop = 0;
> + nbl_hw_wr_regs(hw_mgt, NBL_UQM_QUE_TYPE, (u32 *)&que_type,
> + sizeof(que_type));
> +
> + return 0;
> +}
> +
> +static int nbl_dp_init(struct nbl_hw_mgt *hw_mgt, u8 speed, u8 eth_num)
> +{
> + nbl_dped_init(hw_mgt);
> + nbl_uped_init(hw_mgt);
> + nbl_shaping_init(hw_mgt, speed);
> + nbl_dsch_qid_max_init(hw_mgt);
> + nbl_ustore_init(hw_mgt, eth_num);
> + nbl_dstore_init(hw_mgt, speed);
> + nbl_dvn_init(hw_mgt, speed);
> + nbl_uvn_init(hw_mgt);
> + nbl_uqm_init(hw_mgt);
> +
> + return 0;
> +}
> +
> +static int nbl_host_padpt_init(struct nbl_hw_mgt *hw_mgt)
> +{
> + /* padpt flow control register */
> + nbl_hw_wr32(hw_mgt, NBL_HOST_PADPT_HOST_CFG_FC_CPLH_UP, 0x10400);
> + nbl_hw_wr32(hw_mgt, NBL_HOST_PADPT_HOST_CFG_FC_PD_DN, 0x10080);
> + nbl_hw_wr32(hw_mgt, NBL_HOST_PADPT_HOST_CFG_FC_PH_DN, 0x10010);
> + nbl_hw_wr32(hw_mgt, NBL_HOST_PADPT_HOST_CFG_FC_NPH_DN, 0x10010);
> +
> + return 0;
> +}
> +
> +static int nbl_intf_init(struct nbl_hw_mgt *hw_mgt)
> +{
> + nbl_host_padpt_init(hw_mgt);
> + return 0;
> +}
> +
> +static void nbl_hw_set_driver_status(struct nbl_hw_mgt *hw_mgt, bool active)
> +{
> + u32 status;
> +
> + status = nbl_hw_rd32(hw_mgt, NBL_DRIVER_STATUS_REG);
> +
> + status = (status & ~(1 << NBL_DRIVER_STATUS_BIT)) |
> + (active << NBL_DRIVER_STATUS_BIT);
> +
> + nbl_hw_wr32(hw_mgt, NBL_DRIVER_STATUS_REG, status);
> +}
> +
> +static void nbl_hw_deinit_chip_module(struct nbl_hw_mgt *hw_mgt)
> +{
> + nbl_hw_set_driver_status(hw_mgt, false);
> +}
> +
> +static int nbl_hw_init_chip_module(struct nbl_hw_mgt *hw_mgt, u8 eth_speed,
> + u8 eth_num)
> +{
> + nbl_dp_init(hw_mgt, eth_speed, eth_num);
> + nbl_intf_init(hw_mgt);
> +
> + nbl_write_all_regs(hw_mgt);
> + nbl_hw_set_driver_status(hw_mgt, true);
> + hw_mgt->version = nbl_hw_rd32(hw_mgt, NBL_HW_DUMMY_REG);
> +
> + return 0;
> +}
> +
> static void nbl_hw_enable_mailbox_irq(struct nbl_hw_mgt *hw_mgt, u16 func_id,
> bool enable_msix, u16 global_vec_id)
> {
> @@ -262,6 +665,9 @@ static u32 nbl_hw_get_fw_eth_map(struct nbl_hw_mgt *hw_mgt)
> }
>
> static struct nbl_hw_ops hw_ops = {
> + .init_chip_module = nbl_hw_init_chip_module,
> + .deinit_chip_module = nbl_hw_deinit_chip_module,
> +
> .configure_msix_map = nbl_hw_configure_msix_map,
> .configure_msix_info = nbl_hw_configure_msix_info,
>
> diff --git a/drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_hw_leonis/nbl_hw_leonis.h b/drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_hw_leonis/nbl_hw_leonis.h
> index 8831394ed11b..7487d0e757e3 100644
> --- a/drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_hw_leonis/nbl_hw_leonis.h
> +++ b/drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_hw_leonis/nbl_hw_leonis.h
> @@ -128,7 +128,8 @@ struct nbl_function_msix_map {
> #define NBL_DPED_VLAN_OFFSET (NBL_DP_DPED_BASE + 0x000003F4)
> #define NBL_DPED_DSCP_OFFSET_0 (NBL_DP_DPED_BASE + 0x000003F8)
> #define NBL_DPED_DSCP_OFFSET_1 (NBL_DP_DPED_BASE + 0x000003FC)
> -
> +#define NBL_DPED_V4_TCP_IDX 5
> +#define NBL_DPED_V6_TCP_IDX 6
> /* DPED hw_edt_prof/ UPED hw_edt_prof */
> struct ped_hw_edit_profile {
> u32 l4_len:2;
> @@ -268,6 +269,15 @@ struct dsch_vn_quanta {
>
> #define DEFAULT_DVN_DESC_WR_MERGE_TIMEOUT_MAX 0x3FF
>
> +#define PACKET_RING_MIN 8U
> +#define PACKET_RING_MAX 32U
> +#define SPLIT_RING_MIN 8U
> +#define SPLIT_RING_MAX 16U
> +#define PACKET_RING_BASE 8U
> +#define PACKET_RING_DIV 4U
> +#define SPLIT_RING_CFG_8 0U
> +#define SPLIT_RING_CFG_16 1U
> +
> struct nbl_dvn_descreq_num_cfg {
> u32 avring_cfg_num:1; /* spilit ring descreq_num 0:8,1:16 */
> u32 rsv0:3;
> diff --git a/drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_hw_leonis/nbl_resource_leonis.c b/drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_hw_leonis/nbl_resource_leonis.c
> index 770a255ab06f..403e74b9b1d0 100644
> --- a/drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_hw_leonis/nbl_resource_leonis.c
> +++ b/drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_hw_leonis/nbl_resource_leonis.c
> @@ -46,6 +46,10 @@ nbl_res_setup_ops(struct device *dev, struct nbl_resource_mgt *res_mgt)
>
> if (!is_ops_inited) {
> ret = nbl_intr_setup_ops(&res_ops);
> + if (ret)
> + return ERR_PTR(-ENOMEM);
> +
> + ret = nbl_vsi_setup_ops(&res_ops);
> if (ret)
> return ERR_PTR(-ENOMEM);
> is_ops_inited = true;
> diff --git a/drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_resource.h b/drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_resource.h
> index 1b80676cf19a..675649ffb271 100644
> --- a/drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_resource.h
> +++ b/drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_resource.h
> @@ -110,6 +110,7 @@ void nbl_res_get_eth_id(struct nbl_resource_mgt *res_mgt, u16 vsi_id,
> u8 *eth_mode, u8 *eth_id, u8 *logic_eth_id);
> int nbl_intr_mgt_start(struct nbl_resource_mgt *res_mgt);
> int nbl_intr_setup_ops(struct nbl_resource_ops *resource_ops);
> +int nbl_vsi_setup_ops(struct nbl_resource_ops *resource_ops);
> void nbl_res_pf_dev_vsi_type_to_hw_vsi_type(u16 src_type,
> enum nbl_vsi_serv_type *dst_type);
> #endif
> diff --git a/drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_vsi.c b/drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_vsi.c
> new file mode 100644
> index 000000000000..67b9b23ad012
> --- /dev/null
> +++ b/drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_vsi.c
> @@ -0,0 +1,51 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (c) 2025 Nebula Matrix Limited.
> + */
> +#include <linux/device.h>
> +#include "nbl_vsi.h"
> +
> +static void nbl_res_vsi_deinit_chip_module(struct nbl_resource_mgt *res_mgt)
> +{
> + struct nbl_hw_ops *hw_ops = res_mgt->hw_ops_tbl->ops;
> +
> + hw_ops->deinit_chip_module(res_mgt->hw_ops_tbl->priv);
> +}
> +
> +static int nbl_res_vsi_init_chip_module(struct nbl_resource_mgt *res_mgt)
> +{
> + u8 eth_speed = res_mgt->resource_info->board_info.eth_speed;
> + u8 eth_num = res_mgt->resource_info->board_info.eth_num;
> + struct nbl_hw_ops *hw_ops = res_mgt->hw_ops_tbl->ops;
> + struct nbl_hw_mgt *p = res_mgt->hw_ops_tbl->priv;
> + int ret;
> +
> + ret = hw_ops->init_chip_module(p, eth_speed, eth_num);
> +
> + return ret;
> +}
> +
> +/* NBL_VSI_SET_OPS(ops_name, func)
> + *
> + * Use X Macros to reduce setup and remove codes.
> + */
> +#define NBL_VSI_OPS_TBL \
> +do { \
> + NBL_VSI_SET_OPS(init_chip_module, \
> + nbl_res_vsi_init_chip_module); \
> + NBL_VSI_SET_OPS(deinit_chip_module, \
> + nbl_res_vsi_deinit_chip_module); \
> +} while (0)
> +
> +int nbl_vsi_setup_ops(struct nbl_resource_ops *res_ops)
> +{
> +#define NBL_VSI_SET_OPS(name, func) \
> + do { \
> + res_ops->NBL_NAME(name) = func; \
> + ; \
> + } while (0)
> + NBL_VSI_OPS_TBL;
> +#undef NBL_VSI_SET_OPS
> +
> + return 0;
> +}
> diff --git a/drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_vsi.h b/drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_vsi.h
> new file mode 100644
> index 000000000000..6fd79add57d6
> --- /dev/null
> +++ b/drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_vsi.h
> @@ -0,0 +1,11 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (c) 2025 Nebula Matrix Limited.
> + */
> +
> +#ifndef _NBL_VSI_H_
> +#define _NBL_VSI_H_
> +
> +#include "nbl_resource.h"
> +
> +#endif
> diff --git a/drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_def_hw.h b/drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_def_hw.h
> index f97f7a810ad0..93dfe74d3643 100644
> --- a/drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_def_hw.h
> +++ b/drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_def_hw.h
> @@ -11,6 +11,10 @@
> struct nbl_hw_mgt;
> struct nbl_adapter;
> struct nbl_hw_ops {
> + int (*init_chip_module)(struct nbl_hw_mgt *hw_mgt, u8 eth_speed,
> + u8 eth_num);
> + void (*deinit_chip_module)(struct nbl_hw_mgt *hw_mgt);
> +
> void (*configure_msix_map)(struct nbl_hw_mgt *hw_mgt, u16 func_id,
> bool valid, dma_addr_t dma_addr, u8 bus,
> u8 devid, u8 function);
> diff --git a/drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_include.h b/drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_include.h
> index e4f11e6ded94..5203bb2a9a5f 100644
> --- a/drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_include.h
> +++ b/drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_include.h
> @@ -45,4 +45,35 @@ struct nbl_init_param {
> bool pci_using_dac;
> };
>
> +enum nbl_fw_port_speed {
> + NBL_FW_PORT_SPEED_10G,
> + NBL_FW_PORT_SPEED_25G,
> + NBL_FW_PORT_SPEED_50G,
> + NBL_FW_PORT_SPEED_100G,
> +};
> +
> +#define NBL_OPS_CALL(func, para) \
> +do { \
> + typeof(func) _func = (func); \
> + if (_func) \
> + _func para; \
> +} while (0)
> +
> +#define NBL_OPS_CALL_RET(func, para) \
> +({ \
> + typeof(func) _func = (func); \
> + _func ? _func para : 0; \
> +})
> +
> +#define NBL_OPS_CALL_RET_PTR(func, para) \
> +({ \
> + typeof(func) _func = (func); \
> + _func ? _func para : NULL; \
> +})
> +
> +enum nbl_performance_mode {
> + NBL_QUIRKS_NO_TOE,
> + NBL_QUIRKS_UVN_PREFETCH_ALIGN,
> +};
> +
> #endif
^ permalink raw reply
* Re: commit 0c4f1c02d27a880b cause a deadlock issue
From: Thorsten Leemhuis @ 2026-04-02 8:56 UTC (permalink / raw)
To: He, Guocai (CN)
Cc: miriam.rachel.korenblit@intel.com, johannes.berg@intel.com,
Friend, Linux kernel regressions list
In-Reply-To: <PH0PR11MB55934A1C9A8C35B1E751C247CD50A@PH0PR11MB5593.namprd11.prod.outlook.com>
On 4/1/26 04:28, He, Guocai (CN) wrote:
> Johannes and Miriam and all:
>
> when we test on kernel 6.1.166, find a deadlock caused by commit
> https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=0c4f1c02d27a880b10b58c63f574f13bed4f711d
Does this problem happen with 6.19/7.0-rc6 as well? That is an important
detail to know if this is a general problem (that needs to be fixed in
mainline) or something the backport caused (which mainline developers
might not care about, as that is the domain of the stable team).
Ciao, Thorsten
> test by command "rtcwake -m mem -s 10"
> It will hang (detail please see the attached test.log file )
>
> I revert this commit and it works.
>
> #my analysis:
>
> This creates the following call chain:
> wiphy_suspend()
> cfg80211_leave_all()
> cfg80211_leave() # takes wdev->mtx <- LOCK #1
> __cfg80211_leave()
> cfg80211_stop_p2p_device() # <- NEW from 0c4f1c02d27a
> ieee80211_stop_p2p_device() # mac80211 callback
> ieee80211_sdata_stop()
> ieee80211_do_stop()
> sdata_lock(sdata) # = mutex_lock(&sdata->wdev.mtx)
> <- LOCK #2, SAME mutex!
> DEADLOCK
>
>
> In 6.1.x:
> cfg80211_leave define as:
> wdev_lock(wdev);
> __cfg80211_leave(rdev, wdev);
> wdev_unlock(wdev);
>
> but upstream, __cfg80211_leave is renamed to cfg80211_leave.
> call cfg80211_leave directly. so have no this issue.
>
> Do anybody meets this issue too?
>
>
> Thanks very much.
>
> Guocai
^ permalink raw reply
* Re: [PATCH v10 net-next 07/11] net/nebula-matrix: add intr resource implementation
From: Mohsin Bashir @ 2026-04-02 9:05 UTC (permalink / raw)
To: illusion.wang, dimon.zhao, alvin.wang, sam.chen, netdev
Cc: andrew+netdev, corbet, kuba, linux-doc, lorenzo, pabeni, horms,
vadim.fedorenko, lukas.bulwahn, edumazet, enelsonmoore, skhan,
hkallweit1, jani.nikula, open list
In-Reply-To: <20260401022318.28550-8-illusion.wang@nebula-matrix.com>
On 3/31/26 7:23 PM, illusion.wang wrote:
> MSI-X Interrupt Configuration:
> Dynamically allocate and manage MSI-X interrupt vectors, including
> network interrupts and other types of interrupts.
> Interrupt Mapping Table Management: Maintain the MSI-X mapping table
> (msix_map_table) to establish interrupt associations between hardware
> and software.
> Interrupt Enabling/Disabling:
> Support enabling or disabling specific interrupts through hardware
> operations.
> Interrupt Information Query: Provide interfaces to obtain the
> hardware register addresses and data of interrupts.
>
> Signed-off-by: illusion.wang <illusion.wang@nebula-matrix.com>
> ---
> .../net/ethernet/nebula-matrix/nbl/Makefile | 1 +
> .../nbl/nbl_hw/nbl_hw_leonis/nbl_hw_leonis.c | 71 +++++
> .../nbl_hw_leonis/nbl_resource_leonis.c | 13 +
> .../nebula-matrix/nbl/nbl_hw/nbl_interrupt.c | 243 ++++++++++++++++++
> .../nebula-matrix/nbl/nbl_hw/nbl_interrupt.h | 12 +
> .../nebula-matrix/nbl/nbl_hw/nbl_resource.h | 33 +++
> .../nbl/nbl_include/nbl_include.h | 2 +
> 7 files changed, 375 insertions(+)
> create mode 100644 drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_interrupt.c
> create mode 100644 drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_interrupt.h
>
> diff --git a/drivers/net/ethernet/nebula-matrix/nbl/Makefile b/drivers/net/ethernet/nebula-matrix/nbl/Makefile
> index b03c20f9988e..a56e722a5ac7 100644
> --- a/drivers/net/ethernet/nebula-matrix/nbl/Makefile
> +++ b/drivers/net/ethernet/nebula-matrix/nbl/Makefile
> @@ -9,6 +9,7 @@ nbl-objs += nbl_common/nbl_common.o \
> nbl_hw/nbl_hw_leonis/nbl_resource_leonis.o \
> nbl_hw/nbl_hw_leonis/nbl_hw_leonis_regs.o \
> nbl_hw/nbl_resource.o \
> + nbl_hw/nbl_interrupt.o \
> nbl_core/nbl_dispatch.o \
> nbl_core/nbl_dev.o \
> nbl_main.o
> diff --git a/drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_hw_leonis/nbl_hw_leonis.c b/drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_hw_leonis/nbl_hw_leonis.c
> index 4ef0d5989a76..aa5e91c2b278 100644
> --- a/drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_hw_leonis/nbl_hw_leonis.c
> +++ b/drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_hw_leonis/nbl_hw_leonis.c
> @@ -63,6 +63,73 @@ static void nbl_hw_wr_regs(struct nbl_hw_mgt *hw_mgt, u64 reg, const u32 *data,
> spin_unlock(&hw_mgt->reg_lock);
> }
>
> +static void nbl_hw_enable_mailbox_irq(struct nbl_hw_mgt *hw_mgt, u16 func_id,
> + bool enable_msix, u16 global_vec_id)
> +{
> + struct nbl_mailbox_qinfo_map_table mb_qinfo_map = { 0 };
> +
> + nbl_hw_rd_regs(hw_mgt, NBL_MAILBOX_QINFO_MAP_REG_ARR(func_id),
> + (u32 *)&mb_qinfo_map, sizeof(mb_qinfo_map));
> +
> + if (enable_msix) {
> + mb_qinfo_map.msix_idx = global_vec_id;
> + mb_qinfo_map.msix_idx_valid = 1;
> + } else {
> + mb_qinfo_map.msix_idx = 0;
> + mb_qinfo_map.msix_idx_valid = 0;
> + }
> +
> + nbl_hw_wr_regs(hw_mgt, NBL_MAILBOX_QINFO_MAP_REG_ARR(func_id),
> + (u32 *)&mb_qinfo_map, sizeof(mb_qinfo_map));
> +}
> +
> +static void nbl_hw_configure_msix_map(struct nbl_hw_mgt *hw_mgt, u16 func_id,
> + bool valid, dma_addr_t dma_addr, u8 bus,
> + u8 devid, u8 function)
> +{
> + struct nbl_function_msix_map function_msix_map = { 0 };
> +
> + if (valid) {
> + function_msix_map.msix_map_base_addr = dma_addr;
> + /* use af's bdf, because dma memmory is alloc by af */
> + function_msix_map.function = function;
> + function_msix_map.devid = devid;
> + function_msix_map.bus = bus;
> + function_msix_map.valid = 1;
> + }
> +
> + nbl_hw_wr_regs(hw_mgt,
> + NBL_PCOMPLETER_FUNCTION_MSIX_MAP_REG_ARR(func_id),
> + (u32 *)&function_msix_map, sizeof(function_msix_map));
> +}
> +
> +static void nbl_hw_configure_msix_info(struct nbl_hw_mgt *hw_mgt, u16 func_id,
> + bool valid, u16 interrupt_id, u8 bus,
> + u8 devid, u8 function, bool msix_mask_en)
> +{
> + struct nbl_pcompleter_host_msix_fid_table host_msix_fid_tbl = { 0 };
> + struct nbl_host_msix_info msix_info = { 0 };
> +
> + if (valid) {
> + host_msix_fid_tbl.vld = 1;
> + host_msix_fid_tbl.fid = func_id;
> +
> + msix_info.intrl_pnum = 0;
> + msix_info.intrl_rate = 0;
> + msix_info.function = function;
> + msix_info.devid = devid;
> + msix_info.bus = bus;
> + msix_info.valid = 1;
> + if (msix_mask_en)
> + msix_info.msix_mask_en = 1;
> + }
> +
> + nbl_hw_wr_regs(hw_mgt, NBL_PADPT_HOST_MSIX_INFO_REG_ARR(interrupt_id),
> + (u32 *)&msix_info, sizeof(msix_info));
> + nbl_hw_wr_regs(hw_mgt, NBL_PCOMPLETER_HOST_MSIX_FID_TABLE(interrupt_id),
> + (u32 *)&host_msix_fid_tbl, sizeof(host_msix_fid_tbl));
> +}
> +
> static void nbl_hw_update_mailbox_queue_tail_ptr(struct nbl_hw_mgt *hw_mgt,
> u16 tail_ptr, u8 txrx)
> {
> @@ -195,6 +262,9 @@ static u32 nbl_hw_get_fw_eth_map(struct nbl_hw_mgt *hw_mgt)
> }
>
> static struct nbl_hw_ops hw_ops = {
> + .configure_msix_map = nbl_hw_configure_msix_map,
> + .configure_msix_info = nbl_hw_configure_msix_info,
> +
> .update_mailbox_queue_tail_ptr = nbl_hw_update_mailbox_queue_tail_ptr,
> .config_mailbox_rxq = nbl_hw_config_mailbox_rxq,
> .config_mailbox_txq = nbl_hw_config_mailbox_txq,
> @@ -204,6 +274,7 @@ static struct nbl_hw_ops hw_ops = {
> .get_real_bus = nbl_hw_get_real_bus,
>
> .cfg_mailbox_qinfo = nbl_hw_cfg_mailbox_qinfo,
> + .enable_mailbox_irq = nbl_hw_enable_mailbox_irq,
>
> .get_fw_eth_num = nbl_hw_get_fw_eth_num,
> .get_fw_eth_map = nbl_hw_get_fw_eth_map,
> diff --git a/drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_hw_leonis/nbl_resource_leonis.c b/drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_hw_leonis/nbl_resource_leonis.c
> index 00569aa3459a..770a255ab06f 100644
> --- a/drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_hw_leonis/nbl_resource_leonis.c
> +++ b/drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_hw_leonis/nbl_resource_leonis.c
> @@ -12,6 +12,7 @@ static struct nbl_resource_ops res_ops = {
> .get_eth_id = nbl_res_get_eth_id,
> };
>
> +static bool is_ops_inited;
> static struct nbl_resource_mgt *
> nbl_res_setup_res_mgt(struct nbl_common_info *common)
> {
> @@ -37,11 +38,19 @@ static struct nbl_resource_ops_tbl *
> nbl_res_setup_ops(struct device *dev, struct nbl_resource_mgt *res_mgt)
> {
> struct nbl_resource_ops_tbl *res_ops_tbl;
> + int ret;
>
> res_ops_tbl = devm_kzalloc(dev, sizeof(*res_ops_tbl), GFP_KERNEL);
> if (!res_ops_tbl)
> return ERR_PTR(-ENOMEM);
>
> + if (!is_ops_inited) {
> + ret = nbl_intr_setup_ops(&res_ops);
> + if (ret)
> + return ERR_PTR(-ENOMEM);
> + is_ops_inited = true;
> + }
> +
> res_ops_tbl->ops = &res_ops;
> res_ops_tbl->priv = res_mgt;
>
> @@ -203,6 +212,10 @@ static int nbl_res_start(struct nbl_resource_mgt *res_mgt)
> ret = nbl_res_ctrl_dev_vsi_info_init(res_mgt);
> if (ret)
> return ret;
> +
> + ret = nbl_intr_mgt_start(res_mgt);
> + if (ret)
> + return ret;
> }
> return 0;
> }
> diff --git a/drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_interrupt.c b/drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_interrupt.c
> new file mode 100644
> index 000000000000..aeea866be9c3
> --- /dev/null
> +++ b/drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_interrupt.c
> @@ -0,0 +1,243 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (c) 2025 Nebula Matrix Limited.
> + */
> +#include <linux/device.h>
> +#include <linux/dma-mapping.h>
> +#include "nbl_interrupt.h"
> +
> +static int nbl_res_intr_destroy_msix_map(struct nbl_resource_mgt *res_mgt,
> + u16 func_id)
> +{
> + struct nbl_interrupt_mgt *intr_mgt = res_mgt->intr_mgt;
> + struct nbl_hw_ops *hw_ops = res_mgt->hw_ops_tbl->ops;
> + struct device *dev = res_mgt->common->dev;
> + struct nbl_msix_map_table *msix_map_table;
> + u16 *interrupts;
> + u16 intr_num, i;
> +
> + /* use ctrl dev bdf */
> + hw_ops->configure_msix_map(res_mgt->hw_ops_tbl->priv, func_id, false, 0,
> + 0, 0, 0);
> +
> + intr_num = intr_mgt->func_intr_res[func_id].num_interrupts;
> + interrupts = intr_mgt->func_intr_res[func_id].interrupts;
> +
> + WARN_ON(!interrupts);
So we WARN_ON no interrupts and then proceed to iterate over
interrupts[]? This will surely crash. WARN_ON is not providing any
safety here.
> + for (i = 0; i < intr_num; i++) {
> + if (interrupts[i] >= NBL_MAX_OTHER_INTERRUPT)
> + clear_bit(interrupts[i] - NBL_MAX_OTHER_INTERRUPT,
> + intr_mgt->interrupt_net_bitmap);
> + else
> + clear_bit(interrupts[i],
> + intr_mgt->interrupt_others_bitmap);
> +
> + hw_ops->configure_msix_info(res_mgt->hw_ops_tbl->priv, func_id,
> + false, interrupts[i], 0, 0, 0,
> + false);
> + }
> +
> + kfree(interrupts);
> + intr_mgt->func_intr_res[func_id].interrupts = NULL;
> + intr_mgt->func_intr_res[func_id].num_interrupts = 0;
> +
> + msix_map_table = &intr_mgt->func_intr_res[func_id].msix_map_table;
> + dma_free_coherent(dev, msix_map_table->size, msix_map_table->base_addr,
> + msix_map_table->dma);
> + msix_map_table->size = 0;
> + msix_map_table->base_addr = NULL;
> + msix_map_table->dma = 0;
> +
> + return 0;
> +}
> +
> +static int nbl_res_intr_configure_msix_map(struct nbl_resource_mgt *res_mgt,
> + u16 func_id, u16 num_net_msix,
> + u16 num_others_msix,
> + bool net_msix_mask_en)
> +{
> + struct nbl_interrupt_mgt *intr_mgt = res_mgt->intr_mgt;
> + struct nbl_hw_ops *hw_ops = res_mgt->hw_ops_tbl->ops;
> + struct nbl_common_info *common = res_mgt->common;
> + struct nbl_msix_map_table *msix_map_table;
> + struct nbl_msix_map *msix_map_entries;
> + struct device *dev = common->dev;
> + u16 requested, intr_index;
> + u8 bus, devid, function;
> + bool msix_mask_en;
> + u16 *interrupts;
> + int ret = 0;
> + u16 i;
> +
> + requested = num_net_msix + num_others_msix;
> + if (requested > NBL_MSIX_MAP_TABLE_MAX_ENTRIES)
> + return -EINVAL;
> + if (intr_mgt->func_intr_res[func_id].interrupts)
> + nbl_res_intr_destroy_msix_map(res_mgt, func_id);
> +
> + ret = nbl_res_func_id_to_bdf(res_mgt, func_id, &bus, &devid, &function);
> + if (ret == U32_MAX)
> + return -EINVAL;
> +
> + msix_map_table = &intr_mgt->func_intr_res[func_id].msix_map_table;
> + WARN_ON(msix_map_table->base_addr);
> + msix_map_table->size =
> + sizeof(struct nbl_msix_map) * NBL_MSIX_MAP_TABLE_MAX_ENTRIES;
> + msix_map_table->base_addr = dma_alloc_coherent(dev,
> + msix_map_table->size,
> + &msix_map_table->dma,
> + GFP_KERNEL);
> + if (!msix_map_table->base_addr) {
> + pr_err("Allocate DMA memory for function msix map table failed\n");
> + msix_map_table->size = 0;
> + return -ENOMEM;
> + }
> +
> + interrupts = kcalloc(requested, sizeof(interrupts[0]), GFP_KERNEL);
> + if (!interrupts) {
> + ret = -ENOMEM;
> + goto alloc_interrupts_err;
> + }
> +
> + intr_mgt->func_intr_res[func_id].interrupts = interrupts;
> + intr_mgt->func_intr_res[func_id].num_interrupts = requested;
> + intr_mgt->func_intr_res[func_id].num_net_interrupts = num_net_msix;
> +
> + for (i = 0; i < num_net_msix; i++) {
> + intr_index = find_first_zero_bit(intr_mgt->interrupt_net_bitmap,
> + NBL_MAX_NET_INTERRUPT);
> + if (intr_index == NBL_MAX_NET_INTERRUPT) {
> + pr_err("There is no available interrupt left\n");
Can we use dev_err instead of pr_err? Here and at other places as well?
> + ret = -EAGAIN;
> + goto get_interrupt_err;
> + }
> + interrupts[i] = intr_index + NBL_MAX_OTHER_INTERRUPT;
> + set_bit(intr_index, intr_mgt->interrupt_net_bitmap);
> + }
> +
> + for (i = num_net_msix; i < requested; i++) {
> + intr_index =
> + find_first_zero_bit(intr_mgt->interrupt_others_bitmap,
> + NBL_MAX_OTHER_INTERRUPT);
> + if (intr_index == NBL_MAX_OTHER_INTERRUPT) {
> + pr_err("There is no available interrupt left\n");
> + ret = -EAGAIN;
> + goto get_interrupt_err;
> + }
> + interrupts[i] = intr_index;
> + set_bit(intr_index, intr_mgt->interrupt_others_bitmap);
> + }
> +
> + msix_map_entries = msix_map_table->base_addr;
> + for (i = 0; i < requested; i++) {
> + msix_map_entries[i].global_msix_index = interrupts[i];
> + msix_map_entries[i].valid = 1;
> +
> + if (i < num_net_msix && net_msix_mask_en)
> + msix_mask_en = 1;
> + else
> + msix_mask_en = 0;
> + hw_ops->configure_msix_info(res_mgt->hw_ops_tbl->priv, func_id,
> + true, interrupts[i], bus, devid,
> + function, msix_mask_en);
> + }
> +
> + /* use ctrl dev bdf */
> + hw_ops->configure_msix_map(res_mgt->hw_ops_tbl->priv, func_id, true,
> + msix_map_table->dma, common->hw_bus,
> + common->devid, common->function);
> +
> + return 0;
> +
> +get_interrupt_err:
> + while (i--) {
> + intr_index = interrupts[i];
> + if (intr_index >= NBL_MAX_OTHER_INTERRUPT)
> + clear_bit(intr_index - NBL_MAX_OTHER_INTERRUPT,
> + intr_mgt->interrupt_net_bitmap);
> + else
> + clear_bit(intr_index,
> + intr_mgt->interrupt_others_bitmap);
> + }
> + kfree(interrupts);
> + intr_mgt->func_intr_res[func_id].num_interrupts = 0;
> + intr_mgt->func_intr_res[func_id].interrupts = NULL;
> +
> +alloc_interrupts_err:
> + dma_free_coherent(dev, msix_map_table->size, msix_map_table->base_addr,
> + msix_map_table->dma);
> + msix_map_table->size = 0;
> + msix_map_table->base_addr = NULL;
> + msix_map_table->dma = 0;
> +
> + return ret;
> +}
> +
> +static int nbl_res_intr_enable_mailbox_irq(struct nbl_resource_mgt *res_mgt,
> + u16 func_id, u16 vector_id,
> + bool enable_msix)
> +{
> + struct nbl_interrupt_mgt *intr_mgt = res_mgt->intr_mgt;
> + struct nbl_hw_ops *hw_ops = res_mgt->hw_ops_tbl->ops;
> + u16 global_vec_id;
> +
> + global_vec_id = intr_mgt->func_intr_res[func_id].interrupts[vector_id];
> + hw_ops->enable_mailbox_irq(res_mgt->hw_ops_tbl->priv, func_id,
> + enable_msix, global_vec_id);
> +
> + return 0;
> +}
> +
> +/* NBL_INTR_SET_OPS(ops_name, func)
> + *
> + * Use X Macros to reduce setup and remove codes.
> + */
> +#define NBL_INTR_OPS_TBL \
> +do { \
> + NBL_INTR_SET_OPS(configure_msix_map, \
> + nbl_res_intr_configure_msix_map); \
> + NBL_INTR_SET_OPS(destroy_msix_map, \
> + nbl_res_intr_destroy_msix_map); \
> + NBL_INTR_SET_OPS(enable_mailbox_irq, \
> + nbl_res_intr_enable_mailbox_irq); \
> +} while (0)
> +
> +/* Structure starts here, adding an op should not modify anything below */
> +static struct nbl_interrupt_mgt *nbl_intr_setup_mgt(struct device *dev)
> +{
> + struct nbl_interrupt_mgt *intr_mgt;
> +
> + intr_mgt = devm_kzalloc(dev, sizeof(*intr_mgt), GFP_KERNEL);
> + if (!intr_mgt)
> + return ERR_PTR(-ENOMEM);
> +
> + return intr_mgt;
> +}
> +
> +int nbl_intr_mgt_start(struct nbl_resource_mgt *res_mgt)
> +{
> + struct device *dev = res_mgt->common->dev;
> + struct nbl_interrupt_mgt *intr_mgt;
> + int ret;
> +
> + intr_mgt = nbl_intr_setup_mgt(dev);
> + if (IS_ERR(intr_mgt)) {
> + ret = PTR_ERR(intr_mgt);
> + return ret;
> + }
> + res_mgt->intr_mgt = intr_mgt;
> + return 0;
> +}
> +
> +int nbl_intr_setup_ops(struct nbl_resource_ops *res_ops)
> +{
> +#define NBL_INTR_SET_OPS(name, func) \
> + do { \
> + res_ops->NBL_NAME(name) = func; \
> + ; \
> + } while (0)
> + NBL_INTR_OPS_TBL;
> +#undef NBL_INTR_SET_OPS
> +
> + return 0;
> +}
> diff --git a/drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_interrupt.h b/drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_interrupt.h
> new file mode 100644
> index 000000000000..b876bf30084b
> --- /dev/null
> +++ b/drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_interrupt.h
> @@ -0,0 +1,12 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (c) 2025 Nebula Matrix Limited.
> + */
> +
> +#ifndef _NBL_INTERRUPT_H_
> +#define _NBL_INTERRUPT_H_
> +
> +#include "nbl_resource.h"
> +
> +#define NBL_MSIX_MAP_TABLE_MAX_ENTRIES 1024
> +#endif
> diff --git a/drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_resource.h b/drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_resource.h
> index 51b5b958cde8..1b80676cf19a 100644
> --- a/drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_resource.h
> +++ b/drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_resource.h
> @@ -18,6 +18,37 @@
>
> struct nbl_resource_mgt;
>
> +/* --------- INTERRUPT ---------- */
> +#define NBL_MAX_OTHER_INTERRUPT 1024
> +#define NBL_MAX_NET_INTERRUPT 4096
> +
> +struct nbl_msix_map {
> + u16 valid:1;
> + u16 global_msix_index:13;
> + u16 rsv:2;
> +};
> +
> +struct nbl_msix_map_table {
> + struct nbl_msix_map *base_addr;
> + dma_addr_t dma;
> + size_t size;
> +};
> +
> +struct nbl_func_interrupt_resource_mng {
> + u16 num_interrupts;
> + u16 num_net_interrupts;
> + u16 msix_base;
> + u16 msix_max;
> + u16 *interrupts;
> + struct nbl_msix_map_table msix_map_table;
> +};
> +
> +struct nbl_interrupt_mgt {
> + DECLARE_BITMAP(interrupt_net_bitmap, NBL_MAX_NET_INTERRUPT);
> + DECLARE_BITMAP(interrupt_others_bitmap, NBL_MAX_OTHER_INTERRUPT);
> + struct nbl_func_interrupt_resource_mng func_intr_res[NBL_MAX_FUNC];
> +};
> +
> /* --------- INFO ---------- */
> struct nbl_sriov_info {
> unsigned int bdf;
> @@ -77,6 +108,8 @@ int nbl_res_func_id_to_bdf(struct nbl_resource_mgt *res_mgt, u16 func_id,
> u8 *bus, u8 *dev, u8 *function);
> void nbl_res_get_eth_id(struct nbl_resource_mgt *res_mgt, u16 vsi_id,
> u8 *eth_mode, u8 *eth_id, u8 *logic_eth_id);
> +int nbl_intr_mgt_start(struct nbl_resource_mgt *res_mgt);
> +int nbl_intr_setup_ops(struct nbl_resource_ops *resource_ops);
> void nbl_res_pf_dev_vsi_type_to_hw_vsi_type(u16 src_type,
> enum nbl_vsi_serv_type *dst_type);
> #endif
> diff --git a/drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_include.h b/drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_include.h
> index 6a0bf5e8ca32..e4f11e6ded94 100644
> --- a/drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_include.h
> +++ b/drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_include.h
> @@ -19,6 +19,8 @@
>
> #define NBL_MAX_FUNC 520
> #define NBL_MAX_ETHERNET 4
> +/* Used for macros to pass checkpatch */
> +#define NBL_NAME(x) x
>
> enum nbl_product_type {
> NBL_LEONIS_TYPE,
^ permalink raw reply
* Re: [PATCH] net: qualcomm: qca_uart: report the consumed byte on RX skb allocation failure
From: Stefan Wahren @ 2026-04-02 9:05 UTC (permalink / raw)
To: Pengpeng Hou, Andrew Lunn, David S . Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni
Cc: netdev, linux-kernel, stable
In-Reply-To: <20260402071207.4036-1-pengpeng@iscas.ac.cn>
Am 02.04.26 um 09:12 schrieb Pengpeng Hou:
> qca_tty_receive() consumes each input byte before checking whether a
> completed frame needs a fresh receive skb. When the current byte completes
> a frame, the driver delivers that frame and then allocates a new skb for
> the next one.
>
> If that allocation fails, the current code returns i even though data[i]
> has already been consumed and may already have completed the delivered
> frame. Since serdev interprets the return value as the number of accepted
> bytes, this under-reports progress by one byte and can replay the final
> byte of the completed frame into a fresh parser state on the next call.
>
> Return i + 1 in that failure path so the accepted-byte count matches the
> actual receive-state progress.
>
> Fixes: dfc768fbe618 ("net: qualcomm: add QCA7000 UART driver")
> Cc: stable@vger.kernel.org
> Signed-off-by: Pengpeng Hou <pengpeng@iscas.ac.cn>
Reviewed-by: Stefan Wahren <wahrenst@gmx.net>
Thanks
^ permalink raw reply
* Re: [PATCH v10 net-next 06/11] net/nebula-matrix: add common resource implementation
From: Mohsin Bashir @ 2026-04-02 9:05 UTC (permalink / raw)
To: illusion.wang, dimon.zhao, alvin.wang, sam.chen, netdev
Cc: andrew+netdev, corbet, kuba, linux-doc, lorenzo, pabeni, horms,
vadim.fedorenko, lukas.bulwahn, edumazet, enelsonmoore, skhan,
hkallweit1, jani.nikula, open list
In-Reply-To: <20260401022318.28550-7-illusion.wang@nebula-matrix.com>
On 3/31/26 7:23 PM, illusion.wang wrote:
> The Resource layer processes the entries/data of various modules within
> the processing chip to accomplish specific entry management operations,
> this describes the module business capabilities of the chip and the data
> it manages.
> The resource layer comprises the following sub-modules: common,
> interrupt, and vsi(txrx,queue not contained this time)
>
> This patch provides the common part, including the conversion
> relationships among vsi_id, func_id, eth_id, and pf_id. These
> relationships may be utilized in the upper layer or the resource layer.
>
> Signed-off-by: illusion.wang <illusion.wang@nebula-matrix.com>
> ---
> .../net/ethernet/nebula-matrix/nbl/Makefile | 1 +
> .../nbl/nbl_hw/nbl_hw_leonis/nbl_hw_leonis.c | 55 ++++++
> .../nbl_hw_leonis/nbl_resource_leonis.c | 157 ++++++++++++++++++
> .../nebula-matrix/nbl/nbl_hw/nbl_resource.c | 110 ++++++++++++
> .../nebula-matrix/nbl/nbl_hw/nbl_resource.h | 52 ++++++
> .../nbl/nbl_include/nbl_def_common.h | 15 ++
> .../nbl/nbl_include/nbl_def_resource.h | 16 ++
> .../nbl/nbl_include/nbl_include.h | 8 +
> 8 files changed, 414 insertions(+)
> create mode 100644 drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_resource.c
>
> diff --git a/drivers/net/ethernet/nebula-matrix/nbl/Makefile b/drivers/net/ethernet/nebula-matrix/nbl/Makefile
> index c9bc060732e7..b03c20f9988e 100644
> --- a/drivers/net/ethernet/nebula-matrix/nbl/Makefile
> +++ b/drivers/net/ethernet/nebula-matrix/nbl/Makefile
> @@ -8,6 +8,7 @@ nbl-objs += nbl_common/nbl_common.o \
> nbl_hw/nbl_hw_leonis/nbl_hw_leonis.o \
> nbl_hw/nbl_hw_leonis/nbl_resource_leonis.o \
> nbl_hw/nbl_hw_leonis/nbl_hw_leonis_regs.o \
> + nbl_hw/nbl_resource.o \
> nbl_core/nbl_dispatch.o \
> nbl_core/nbl_dev.o \
> nbl_main.o
> diff --git a/drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_hw_leonis/nbl_hw_leonis.c b/drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_hw_leonis/nbl_hw_leonis.c
> index 83a4dc584f48..4ef0d5989a76 100644
> --- a/drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_hw_leonis/nbl_hw_leonis.c
> +++ b/drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_hw_leonis/nbl_hw_leonis.c
> @@ -9,6 +9,18 @@
> #include <linux/spinlock.h>
> #include "nbl_hw_leonis.h"
>
> +static void nbl_hw_read_mbx_regs(struct nbl_hw_mgt *hw_mgt, u64 reg,
> + u32 *data, u32 len)
> +{
> + u32 i;
> +
> + if (len % 4)
> + return;
> +
> + for (i = 0; i < len / 4; i++)
> + data[i] = nbl_mbx_rd32(hw_mgt, reg + i * sizeof(u32));
> +}
> +
> static void nbl_hw_write_mbx_regs(struct nbl_hw_mgt *hw_mgt, u64 reg,
> const u32 *data, u32 len)
> {
> @@ -130,6 +142,14 @@ static u32 nbl_hw_get_host_pf_mask(struct nbl_hw_mgt *hw_mgt)
> return data;
> }
>
> +static u32 nbl_hw_get_real_bus(struct nbl_hw_mgt *hw_mgt)
> +{
> + u32 data;
> +
> + data = nbl_hw_rd32(hw_mgt, NBL_PCIE_HOST_TL_CFG_BUSDEV);
> + return data >> 5;
> +}
> +
> static void nbl_hw_cfg_mailbox_qinfo(struct nbl_hw_mgt *hw_mgt, u16 func_id,
> u16 bus, u16 devid, u16 function)
> {
> @@ -144,6 +164,36 @@ static void nbl_hw_cfg_mailbox_qinfo(struct nbl_hw_mgt *hw_mgt, u16 func_id,
> (u32 *)&mb_qinfo_map, sizeof(mb_qinfo_map));
> }
>
> +static void nbl_hw_get_board_info(struct nbl_hw_mgt *hw_mgt,
> + struct nbl_board_port_info *board_info)
> +{
> + union nbl_fw_board_cfg_dw3 dw3 = { .info = { 0 } };
> +
> + nbl_hw_read_mbx_regs(hw_mgt, NBL_FW_BOARD_DW3_OFFSET, (u32 *)&dw3,
> + sizeof(dw3));
> + board_info->eth_num = dw3.info.port_num;
> + board_info->eth_speed = dw3.info.port_speed;
> + board_info->p4_version = dw3.info.p4_version;
> +}
> +
> +static u32 nbl_hw_get_fw_eth_num(struct nbl_hw_mgt *hw_mgt)
> +{
> + union nbl_fw_board_cfg_dw3 dw3 = { .info = { 0 } };
> +
> + nbl_hw_read_mbx_regs(hw_mgt, NBL_FW_BOARD_DW3_OFFSET, (u32 *)&dw3,
> + sizeof(dw3));
> + return dw3.info.port_num;
> +}
> +
> +static u32 nbl_hw_get_fw_eth_map(struct nbl_hw_mgt *hw_mgt)
> +{
> + union nbl_fw_board_cfg_dw6 dw6 = { .info = { 0 } };
> +
> + nbl_hw_read_mbx_regs(hw_mgt, NBL_FW_BOARD_DW6_OFFSET, (u32 *)&dw6,
> + sizeof(dw6));
> + return dw6.info.eth_bitmap;
> +}
> +
> static struct nbl_hw_ops hw_ops = {
> .update_mailbox_queue_tail_ptr = nbl_hw_update_mailbox_queue_tail_ptr,
> .config_mailbox_rxq = nbl_hw_config_mailbox_rxq,
> @@ -151,8 +201,13 @@ static struct nbl_hw_ops hw_ops = {
> .stop_mailbox_rxq = nbl_hw_stop_mailbox_rxq,
> .stop_mailbox_txq = nbl_hw_stop_mailbox_txq,
> .get_host_pf_mask = nbl_hw_get_host_pf_mask,
> + .get_real_bus = nbl_hw_get_real_bus,
> +
> .cfg_mailbox_qinfo = nbl_hw_cfg_mailbox_qinfo,
>
> + .get_fw_eth_num = nbl_hw_get_fw_eth_num,
> + .get_fw_eth_map = nbl_hw_get_fw_eth_map,
> + .get_board_info = nbl_hw_get_board_info,
> };
>
> /* Structure starts here, adding an op should not modify anything below */
> diff --git a/drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_hw_leonis/nbl_resource_leonis.c b/drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_hw_leonis/nbl_resource_leonis.c
> index 355f7ea66b88..00569aa3459a 100644
> --- a/drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_hw_leonis/nbl_resource_leonis.c
> +++ b/drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_hw_leonis/nbl_resource_leonis.c
> @@ -4,9 +4,12 @@
> */
> #include <linux/device.h>
> #include <linux/pci.h>
> +#include <linux/bits.h>
> #include "nbl_resource_leonis.h"
>
> static struct nbl_resource_ops res_ops = {
> + .get_vsi_id = nbl_res_func_id_to_vsi_id,
> + .get_eth_id = nbl_res_get_eth_id,
> };
>
> static struct nbl_resource_mgt *
> @@ -45,8 +48,162 @@ nbl_res_setup_ops(struct device *dev, struct nbl_resource_mgt *res_mgt)
> return res_ops_tbl;
> }
>
> +static int nbl_res_ctrl_dev_setup_eth_info(struct nbl_resource_mgt *res_mgt)
> +{
> + struct nbl_hw_ops *hw_ops = res_mgt->hw_ops_tbl->ops;
> + struct device *dev = res_mgt->common->dev;
> + struct nbl_eth_info *eth_info;
> + u32 eth_bitmap, eth_id;
> + u32 eth_num = 0;
> + int i;
> +
> + eth_info = devm_kzalloc(dev, sizeof(*eth_info), GFP_KERNEL);
> + if (!eth_info)
> + return -ENOMEM;
> +
> + res_mgt->resource_info->eth_info = eth_info;
> +
> + eth_info->eth_num =
> + (u8)hw_ops->get_fw_eth_num(res_mgt->hw_ops_tbl->priv);
> + eth_bitmap = hw_ops->get_fw_eth_map(res_mgt->hw_ops_tbl->priv);
> + /* for 2 eth port board, the eth_id is 0, 2 */
> + for (i = 0; i < NBL_MAX_ETHERNET; i++) {
> + if ((1 << i) & eth_bitmap) {
> + set_bit(i, eth_info->eth_bitmap);
> + eth_info->eth_id[eth_num] = i;
> + eth_info->logic_eth_id[i] = eth_num;
> + eth_num++;
> + }
> + }
> +
> + for (i = 0; i < res_mgt->resource_info->max_pf; i++) {
> + /* if pf_id < eth_num, the pf relate corresponding eth_id*/
nit: /* if pf_id < eth_num, the pf relate corresponding eth_id */
> + if (i < eth_num) {
> + eth_id = eth_info->eth_id[i];
> + eth_info->pf_bitmap[eth_id] |= BIT(i);
> + }
> + /* if pf_id >= eth_num, the pf relate eth 0*/
> + else
> + eth_info->pf_bitmap[0] |= BIT(i);
> + }
> +
> + return 0;
> +}
> +
> +static int nbl_res_ctrl_dev_sriov_info_init(struct nbl_resource_mgt *res_mgt)
> +{
> + struct nbl_hw_ops *hw_ops = res_mgt->hw_ops_tbl->ops;
> + struct nbl_hw_mgt *p = res_mgt->hw_ops_tbl->priv;
> + struct nbl_common_info *common = res_mgt->common;
> + struct nbl_sriov_info *sriov_info;
> + struct device *dev = common->dev;
> + u16 func_id, function;
> +
> + sriov_info = devm_kcalloc(dev, res_mgt->resource_info->max_pf,
> + sizeof(*sriov_info), GFP_KERNEL);
> + if (!sriov_info)
> + return -ENOMEM;
> +
> + res_mgt->resource_info->sriov_info = sriov_info;
> +
> + for (func_id = 0; func_id < res_mgt->resource_info->max_pf; func_id++) {
> + sriov_info = res_mgt->resource_info->sriov_info + func_id;
> + function = common->function + func_id;
> +
> + common->hw_bus = (u8)hw_ops->get_real_bus(p);
Why do we have to write to common->hw_bus in a loop? Can we do it once
outside the loop?
> + sriov_info->bdf = PCI_DEVID(common->hw_bus,
> + PCI_DEVFN(common->devid, function));
> + }
nit: space here
> + return 0;
> +}
> +
> +static int nbl_res_ctrl_dev_vsi_info_init(struct nbl_resource_mgt *res_mgt)
> +{
> + struct nbl_eth_info *eth_info = res_mgt->resource_info->eth_info;
> + struct nbl_common_info *common = res_mgt->common;
> + struct device *dev = common->dev;
> + struct nbl_vsi_info *vsi_info;
> + int i;
> +
> + vsi_info = devm_kcalloc(dev, res_mgt->resource_info->max_pf,
> + sizeof(*vsi_info), GFP_KERNEL);
> + if (!vsi_info)
> + return -ENOMEM;
> +
> + res_mgt->resource_info->vsi_info = vsi_info;
> + /*
> + * case 1 two port(2pf)
> + * pf0,pf1(NBL_VSI_SERV_PF_DATA_TYPE) vsi is 0,512
> +
> + * case 2 four port(4pf)
> + * pf0,pf1,pf2,pf3(NBL_VSI_SERV_PF_DATA_TYPE) vsi is 0,256,512,768
> +
> + */
> +
> + vsi_info->num = eth_info->eth_num;
> + for (i = 0; i < vsi_info->num; i++) {
> + vsi_info->serv_info[i][NBL_VSI_SERV_PF_DATA_TYPE].base_id =
> + i * NBL_VSI_ID_GAP(vsi_info->num);
> + vsi_info->serv_info[i][NBL_VSI_SERV_PF_DATA_TYPE].num = 1;
> + }
nit: space here
> + return 0;
> +}
> +
> +static int nbl_res_init_pf_num(struct nbl_resource_mgt *res_mgt)
> +{
> + struct nbl_hw_ops *hw_ops = res_mgt->hw_ops_tbl->ops;
> + u32 pf_num = 0;
> + u32 pf_mask;
> + int i;
> +
> + pf_mask = hw_ops->get_host_pf_mask(res_mgt->hw_ops_tbl->priv);
> + for (i = 0; i < NBL_MAX_PF; i++) {
> + if (!(pf_mask & (1 << i)))
> + pf_num++;
> + else
> + break;
> + }
> +
> + res_mgt->resource_info->max_pf = pf_num;
> +
> + if (!pf_num)
> + return -1;
Can we have an appropriate error code here instead of raw number? How
about -EINVAL?
> +
> + return 0;
> +}
> +
> +static void nbl_res_init_board_info(struct nbl_resource_mgt *res_mgt)
> +{
> + struct nbl_hw_ops *hw_ops = res_mgt->hw_ops_tbl->ops;
> +
> + hw_ops->get_board_info(res_mgt->hw_ops_tbl->priv,
> + &res_mgt->resource_info->board_info);
> +}
> +
> static int nbl_res_start(struct nbl_resource_mgt *res_mgt)
> {
> + struct nbl_common_info *common = res_mgt->common;
> + int ret = 0;
> +
> + if (common->has_ctrl) {
> + nbl_res_init_board_info(res_mgt);
> +
> + ret = nbl_res_init_pf_num(res_mgt);
> + if (ret)
> + return ret;
> +
> + ret = nbl_res_ctrl_dev_sriov_info_init(res_mgt);
> + if (ret)
> + return ret;
> +
> + ret = nbl_res_ctrl_dev_setup_eth_info(res_mgt);
> + if (ret)
> + return ret;
> +
> + ret = nbl_res_ctrl_dev_vsi_info_init(res_mgt);
> + if (ret)
> + return ret;
> + }
> return 0;
> }
>
> diff --git a/drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_resource.c b/drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_resource.c
> new file mode 100644
> index 000000000000..2afec802b0e3
> --- /dev/null
> +++ b/drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_resource.c
> @@ -0,0 +1,110 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (c) 2025 Nebula Matrix Limited.
> + */
> +
> +#include <linux/pci.h>
> +#include "nbl_resource.h"
> +
> +u16 nbl_res_pfid_to_vsi_id(struct nbl_resource_mgt *res_mgt, int pfid, u16 type)
> +{
> + struct nbl_vsi_info *vsi_info = res_mgt->resource_info->vsi_info;
> + enum nbl_vsi_serv_type dst_type = NBL_VSI_SERV_PF_DATA_TYPE;
> + struct nbl_common_info *common = res_mgt->common;
> + u16 vsi_id = U16_MAX;
> + u32 diff;
> +
> + diff = nbl_common_pf_id_subtraction_mgtpf_id(common, pfid);
> + if (diff == U32_MAX)
> + return vsi_id;
> +
> + if (diff < vsi_info->num) {
> + nbl_res_pf_dev_vsi_type_to_hw_vsi_type(type, &dst_type);
> + vsi_id = vsi_info->serv_info[diff][dst_type].base_id;
> + }
> + if (vsi_id == U16_MAX)
> + pr_err("convert pfid %d to vsi_id(%d) failed!\n", pfid, type);
> + return vsi_id;
> +}
> +
> +u16 nbl_res_func_id_to_vsi_id(struct nbl_resource_mgt *res_mgt, u16 func_id,
> + u16 type)
> +{
> + int pfid = func_id;
> +
> + return nbl_res_pfid_to_vsi_id(res_mgt, pfid, type);
> +}
> +
> +int nbl_res_vsi_id_to_pf_id(struct nbl_resource_mgt *res_mgt, u16 vsi_id)
> +{
> + struct nbl_vsi_info *vsi_info = res_mgt->resource_info->vsi_info;
> + struct nbl_common_info *common = res_mgt->common;
> + bool vsi_find = false;
> + u32 pf_id = U32_MAX;
> + int i, j;
> +
> + for (i = 0; i < vsi_info->num; i++) {
> + for (j = 0; j < NBL_VSI_SERV_MAX_TYPE; j++)
> + if (vsi_id >= vsi_info->serv_info[i][j].base_id &&
> + (vsi_id < vsi_info->serv_info[i][j].base_id +
> + vsi_info->serv_info[i][j].num)) {
> + vsi_find = true;
> + break;
> + }
> +
> + if (vsi_find)
> + break;
> + }
> +
> + if (vsi_find) {
> + if (j == NBL_VSI_SERV_PF_DATA_TYPE)
> + pf_id = i + common->mgt_pf;
> + }
> +
> + return pf_id;
So we have a possible case of returning U32_MAX when the return type is int.
> +}
> +
> +int nbl_res_func_id_to_bdf(struct nbl_resource_mgt *res_mgt, u16 func_id,
> + u8 *bus, u8 *dev, u8 *function)
> +{
> + struct nbl_common_info *common = res_mgt->common;
> + struct nbl_sriov_info *sriov_info;
> + int pfid = func_id;
> + u8 pf_bus, devfn;
> + u32 diff;
> +
> + diff = nbl_common_pf_id_subtraction_mgtpf_id(common, pfid);
> + if (diff == U32_MAX)
> + return U32_MAX;
> + sriov_info = res_mgt->resource_info->sriov_info + diff;
> + pf_bus = PCI_BUS_NUM(sriov_info->bdf);
> + devfn = sriov_info->bdf & 0xff;
> + *bus = pf_bus;
> + *dev = PCI_SLOT(devfn);
> + *function = PCI_FUNC(devfn);
nit: space here
> + return 0;
> +}
> +
> +void nbl_res_get_eth_id(struct nbl_resource_mgt *res_mgt, u16 vsi_id,
> + u8 *eth_mode, u8 *eth_id, u8 *logic_eth_id)
> +{
> + struct nbl_eth_info *eth_info = res_mgt->resource_info->eth_info;
> + u16 pf_id = nbl_res_vsi_id_to_pf_id(res_mgt, vsi_id);
> +
> + *eth_mode = eth_info->eth_num;
> + if (pf_id < eth_info->eth_num) {
> + *eth_id = eth_info->eth_id[pf_id];
> + *logic_eth_id = pf_id;
> + } else {
> + /* if pf_id >= eth_num, use eth_id 0 */
Perhaps we can elaborate this a little more
> + *eth_id = eth_info->eth_id[0];
> + *logic_eth_id = 0;
> + }
> +}
> +
> +void nbl_res_pf_dev_vsi_type_to_hw_vsi_type(u16 src_type,
> + enum nbl_vsi_serv_type *dst_type)
> +{
> + if (src_type == NBL_VSI_DATA)
> + *dst_type = NBL_VSI_SERV_PF_DATA_TYPE;
> +}
> diff --git a/drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_resource.h b/drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_resource.h
> index e08b6237da32..51b5b958cde8 100644
> --- a/drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_resource.h
> +++ b/drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_resource.h
> @@ -16,7 +16,48 @@
> #include "../nbl_include/nbl_def_common.h"
> #include "../nbl_core.h"
>
> +struct nbl_resource_mgt;
> +
> +/* --------- INFO ---------- */
> +struct nbl_sriov_info {
> + unsigned int bdf;
> +};
> +
> +struct nbl_eth_info {
> + DECLARE_BITMAP(eth_bitmap, NBL_MAX_ETHERNET);
> + u8 pf_bitmap[NBL_MAX_ETHERNET];
> + u8 eth_num;
> + u8 resv[3];
> + u8 eth_id[NBL_MAX_PF];
> + u8 logic_eth_id[NBL_MAX_PF];
> +};
> +
> +enum nbl_vsi_serv_type {
> + NBL_VSI_SERV_PF_DATA_TYPE,
> + NBL_VSI_SERV_MAX_TYPE,
> +};
> +
> +struct nbl_vsi_serv_info {
> + u16 base_id;
> + u16 num;
> +};
> +
> +struct nbl_vsi_info {
> + u16 num;
> + struct nbl_vsi_serv_info serv_info[NBL_MAX_ETHERNET]
> + [NBL_VSI_SERV_MAX_TYPE];
> +};
> +
> struct nbl_resource_info {
> + /* ctrl-dev owned pfs */
> + DECLARE_BITMAP(func_bitmap, NBL_MAX_FUNC);
> + struct nbl_sriov_info *sriov_info;
> + struct nbl_eth_info *eth_info;
> + struct nbl_vsi_info *vsi_info;
> + u32 base_qid;
> + u32 max_vf_num;
> + u8 max_pf;
> + struct nbl_board_port_info board_info;
> };
>
> struct nbl_resource_mgt {
> @@ -27,4 +68,15 @@ struct nbl_resource_mgt {
> struct nbl_interrupt_mgt *intr_mgt;
> };
>
> +int nbl_res_vsi_id_to_pf_id(struct nbl_resource_mgt *res_mgt, u16 vsi_id);
> +u16 nbl_res_pfid_to_vsi_id(struct nbl_resource_mgt *res_mgt, int pfid,
> + u16 type);
> +u16 nbl_res_func_id_to_vsi_id(struct nbl_resource_mgt *res_mgt, u16 func_id,
> + u16 type);
> +int nbl_res_func_id_to_bdf(struct nbl_resource_mgt *res_mgt, u16 func_id,
> + u8 *bus, u8 *dev, u8 *function);
> +void nbl_res_get_eth_id(struct nbl_resource_mgt *res_mgt, u16 vsi_id,
> + u8 *eth_mode, u8 *eth_id, u8 *logic_eth_id);
> +void nbl_res_pf_dev_vsi_type_to_hw_vsi_type(u16 src_type,
> + enum nbl_vsi_serv_type *dst_type);
> #endif
> diff --git a/drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_def_common.h b/drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_def_common.h
> index 5c532247c852..04ffc1918a46 100644
> --- a/drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_def_common.h
> +++ b/drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_def_common.h
> @@ -12,6 +12,21 @@
> #include "nbl_include.h"
>
> struct nbl_hash_tbl_mgt;
> +#define NBL_TWO_ETHERNET_PORT 2
> +#define NBL_FOUR_ETHERNET_PORT 4
> +#define NBL_DEFAULT_VSI_ID_GAP 1024
> +#define NBL_TWO_ETHERNET_VSI_ID_GAP 512
> +#define NBL_FOUR_ETHERNET_VSI_ID_GAP 256
> +
> +#define NBL_VSI_ID_GAP(m) \
> + ({ \
> + typeof(m) _m = (m); \
> + _m == NBL_FOUR_ETHERNET_PORT ? \
> + NBL_FOUR_ETHERNET_VSI_ID_GAP : \
> + (_m == NBL_TWO_ETHERNET_PORT ? \
> + NBL_TWO_ETHERNET_VSI_ID_GAP : \
> + NBL_DEFAULT_VSI_ID_GAP); \
> + })
>
> struct nbl_common_info {
> struct pci_dev *pdev;
> diff --git a/drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_def_resource.h b/drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_def_resource.h
> index 16add7e25eae..249b8f633cee 100644
> --- a/drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_def_resource.h
> +++ b/drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_def_resource.h
> @@ -6,9 +6,25 @@
> #ifndef _NBL_DEF_RESOURCE_H_
> #define _NBL_DEF_RESOURCE_H_
>
> +#include <linux/types.h>
> +
> struct nbl_resource_mgt;
> struct nbl_adapter;
> +
> struct nbl_resource_ops {
> + int (*init_chip_module)(struct nbl_resource_mgt *res_mgt);
> + void (*deinit_chip_module)(struct nbl_resource_mgt *res_mgt);
> +
> + int (*configure_msix_map)(struct nbl_resource_mgt *res_mgt, u16 func_id,
> + u16 num_net_msix, u16 num_others_msix,
> + bool net_msix_mask_en);
> + int (*destroy_msix_map)(struct nbl_resource_mgt *res_mgt, u16 func_id);
> + int (*enable_mailbox_irq)(struct nbl_resource_mgt *res_mgt, u16 func_id,
> + u16 vector_id, bool enable_msix);
> + u16 (*get_vsi_id)(struct nbl_resource_mgt *res_mgt, u16 func_id,
> + u16 type);
> + void (*get_eth_id)(struct nbl_resource_mgt *res_mgt, u16 vsi_id,
> + u8 *eth_mode, u8 *eth_id, u8 *logic_eth_id);
> };
>
> struct nbl_resource_ops_tbl {
> diff --git a/drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_include.h b/drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_include.h
> index a01c32f57d84..6a0bf5e8ca32 100644
> --- a/drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_include.h
> +++ b/drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_include.h
> @@ -17,11 +17,19 @@
> ((_id) == (max) ? 0 : (_id) + 1); \
> })
>
> +#define NBL_MAX_FUNC 520
> +#define NBL_MAX_ETHERNET 4
> +
> enum nbl_product_type {
> NBL_LEONIS_TYPE,
> NBL_PRODUCT_MAX,
> };
>
> +enum {
> + NBL_VSI_DATA = 0,
> + NBL_VSI_MAX,
> +};
> +
> struct nbl_func_caps {
> u32 has_ctrl:1;
> u32 has_net:1;
^ permalink raw reply
* Re: [PATCH v10 net-next 05/11] net/nebula-matrix: add channel layer
From: Mohsin Bashir @ 2026-04-02 9:04 UTC (permalink / raw)
To: illusion.wang, dimon.zhao, alvin.wang, sam.chen, netdev
Cc: andrew+netdev, corbet, kuba, linux-doc, lorenzo, pabeni, horms,
vadim.fedorenko, lukas.bulwahn, edumazet, enelsonmoore, skhan,
hkallweit1, jani.nikula, open list
In-Reply-To: <20260401022318.28550-6-illusion.wang@nebula-matrix.com>
On 3/31/26 7:23 PM, illusion.wang wrote:
> a channel management layer provides structured approach to handle
> communication between different components and drivers. Here's a summary
> of its key functionalities:
>
> 1. Message Handling Framework
> Message Registration/Unregistration: Functions (nbl_chan_register_msg,
> nbl_chan_unregister_msg) allow dynamic registration of message handlers
> for specific message types, enabling extensible communication protocols.
> Message Sending/Acknowledgment: Core functions (nbl_chan_send_msg,
> nbl_chan_send_ack) handle message transmission, including asynchronous
> operations with acknowledgment (ACK) support.
> Received ACKs are processed via nbl_chan_recv_ack_msg.
> Hash-Based Handler Lookup: A hash table (handle_hash_tbl) stores message
> handlers for efficient O(1) lookup by message type.
>
> 2. Channel Types and Queue Management
> Mailbox Channel: For direct communication between PF0 and Other PF.
> Queue Initialization/Teardown: Functions (nbl_chan_init_queue,
> nbl_chan_teardown_queue) manage transmit (TX) and receive (RX) queues.
>
> Queue Configuration: Hardware-specific queue parameters (e.g., buffer
> sizes, entry counts) are set via nbl_chan_config_queue, with hardware
> interactions delegated to hw_ops.
>
> 3. Hardware Abstraction Layer (HW Ops)
> Hardware-Specific Operations: The nbl_hw_ops structure abstracts
> hardware interactions: queue configuration (config_mailbox_txq/rxq),
> tail pointer updates(update_mailbox_queue_tail_ptr).
>
> Signed-off-by: illusion.wang <illusion.wang@nebula-matrix.com>
> ---
> .../net/ethernet/nebula-matrix/nbl/Makefile | 3 +-
> .../nbl/nbl_channel/nbl_channel.c | 768 +++++++++++++++++-
> .../nbl/nbl_channel/nbl_channel.h | 129 +++
> .../nebula-matrix/nbl/nbl_common/nbl_common.c | 207 +++++
> .../nebula-matrix/nbl/nbl_common/nbl_common.h | 34 +
> .../nbl/nbl_hw/nbl_hw_leonis/nbl_hw_leonis.c | 143 ++++
> .../nbl/nbl_include/nbl_def_channel.h | 87 ++
> .../nbl/nbl_include/nbl_def_common.h | 30 +
> .../nbl/nbl_include/nbl_def_hw.h | 28 +
> .../nbl/nbl_include/nbl_include.h | 6 +
> 10 files changed, 1433 insertions(+), 2 deletions(-)
> create mode 100644 drivers/net/ethernet/nebula-matrix/nbl/nbl_common/nbl_common.c
> create mode 100644 drivers/net/ethernet/nebula-matrix/nbl/nbl_common/nbl_common.h
>
> diff --git a/drivers/net/ethernet/nebula-matrix/nbl/Makefile b/drivers/net/ethernet/nebula-matrix/nbl/Makefile
> index 63116d1d7043..c9bc060732e7 100644
> --- a/drivers/net/ethernet/nebula-matrix/nbl/Makefile
> +++ b/drivers/net/ethernet/nebula-matrix/nbl/Makefile
> @@ -3,7 +3,8 @@
>
> obj-$(CONFIG_NBL) := nbl.o
>
> -nbl-objs += nbl_channel/nbl_channel.o \
> +nbl-objs += nbl_common/nbl_common.o \
> + nbl_channel/nbl_channel.o \
> nbl_hw/nbl_hw_leonis/nbl_hw_leonis.o \
> nbl_hw/nbl_hw_leonis/nbl_resource_leonis.o \
> nbl_hw/nbl_hw_leonis/nbl_hw_leonis_regs.o \
> diff --git a/drivers/net/ethernet/nebula-matrix/nbl/nbl_channel/nbl_channel.c b/drivers/net/ethernet/nebula-matrix/nbl/nbl_channel/nbl_channel.c
> index 9f4603870cb4..0da7968e93e8 100644
> --- a/drivers/net/ethernet/nebula-matrix/nbl/nbl_channel/nbl_channel.c
> +++ b/drivers/net/ethernet/nebula-matrix/nbl/nbl_channel/nbl_channel.c
> @@ -2,12 +2,763 @@
> /*
> * Copyright (c) 2025 Nebula Matrix Limited.
> */
> -
> +#include <linux/delay.h>
> #include <linux/device.h>
> #include <linux/pci.h>
> +#include <linux/bits.h>
> +#include <linux/dma-mapping.h>
> #include "nbl_channel.h"
>
> +static int nbl_chan_add_msg_handler(struct nbl_channel_mgt *chan_mgt,
> + u16 msg_type, nbl_chan_resp func,
> + void *priv)
> +{
> + struct nbl_chan_msg_node_data handler = { 0 };
> + int ret;
> +
> + handler.func = func;
> + handler.priv = priv;
> + ret = nbl_common_alloc_hash_node(chan_mgt->handle_hash_tbl, &msg_type,
> + &handler, NULL);
> +
> + return ret;
> +}
> +
> +static int nbl_chan_init_msg_handler(struct nbl_channel_mgt *chan_mgt)
> +{
> + struct nbl_common_info *common = chan_mgt->common;
> + struct nbl_hash_tbl_key tbl_key;
> +
> + tbl_key.dev = common->dev;
> + tbl_key.key_size = sizeof(u16);
> + tbl_key.data_size = sizeof(struct nbl_chan_msg_node_data);
> + tbl_key.bucket_size = NBL_CHAN_HANDLER_TBL_BUCKET_SIZE;
> +
> + chan_mgt->handle_hash_tbl = nbl_common_init_hash_table(&tbl_key);
> + if (!chan_mgt->handle_hash_tbl)
> + return -ENOMEM;
> +
> + return 0;
> +}
> +
> +static void nbl_chan_remove_msg_handler(struct nbl_channel_mgt *chan_mgt)
> +{
> + nbl_common_remove_hash_table(chan_mgt->handle_hash_tbl, NULL);
> +
> + chan_mgt->handle_hash_tbl = NULL;
> +}
> +
> +static void nbl_chan_init_queue_param(struct nbl_chan_info *chan_info,
> + u16 num_txq_entries, u16 num_rxq_entries,
> + u16 txq_buf_size, u16 rxq_buf_size)
> +{
> + mutex_init(&chan_info->txq_lock);
> + chan_info->num_txq_entries = num_txq_entries;
> + chan_info->num_rxq_entries = num_rxq_entries;
> + chan_info->txq_buf_size = txq_buf_size;
> + chan_info->rxq_buf_size = rxq_buf_size;
> +}
> +
> +static int nbl_chan_init_tx_queue(struct nbl_common_info *common,
> + struct nbl_chan_info *chan_info)
> +{
> + struct nbl_chan_ring *txq = &chan_info->txq;
> + struct device *dev = common->dev;
> + size_t size =
> + chan_info->num_txq_entries * sizeof(struct nbl_chan_tx_desc);
> +
> + txq->desc.tx_desc = dmam_alloc_coherent(dev, size, &txq->dma,
> + GFP_KERNEL);
> + if (!txq->desc.tx_desc)
> + return -ENOMEM;
> +
> + chan_info->wait = devm_kcalloc(dev, chan_info->num_txq_entries,
> + sizeof(*chan_info->wait),
> + GFP_KERNEL);
> + if (!chan_info->wait)
> + return -ENOMEM;
> +
> + txq->buf = devm_kcalloc(dev, chan_info->num_txq_entries,
> + sizeof(*txq->buf), GFP_KERNEL);
> + if (!txq->buf)
> + return -ENOMEM;
> +
> + return 0;
> +}
> +
> +static int nbl_chan_init_rx_queue(struct nbl_common_info *common,
> + struct nbl_chan_info *chan_info)
> +{
> + struct nbl_chan_ring *rxq = &chan_info->rxq;
> + struct device *dev = common->dev;
> + size_t size =
> + chan_info->num_rxq_entries * sizeof(struct nbl_chan_rx_desc);
> +
> + rxq->desc.rx_desc = dmam_alloc_coherent(dev, size, &rxq->dma,
> + GFP_KERNEL);
> + if (!rxq->desc.rx_desc) {
> + dev_err(dev,
> + "Allocate DMA for chan rx descriptor ring failed\n");
> + return -ENOMEM;
> + }
> +
> + rxq->buf = devm_kcalloc(dev, chan_info->num_rxq_entries,
> + sizeof(*rxq->buf), GFP_KERNEL);
> + if (!rxq->buf)
> + return -ENOMEM;
> +
> + return 0;
> +}
> +
> +static int nbl_chan_init_queue(struct nbl_common_info *common,
> + struct nbl_chan_info *chan_info)
> +{
> + int err;
> +
> + err = nbl_chan_init_tx_queue(common, chan_info);
> + if (err)
> + return err;
> +
> + err = nbl_chan_init_rx_queue(common, chan_info);
> + return err;
> +}
> +
> +static void nbl_chan_config_queue(struct nbl_channel_mgt *chan_mgt,
> + struct nbl_chan_info *chan_info, bool tx)
> +{
> + struct nbl_hw_ops *hw_ops = chan_mgt->hw_ops_tbl->ops;
> + struct nbl_hw_mgt *p = chan_mgt->hw_ops_tbl->priv;
> + int size_bwid = ilog2(chan_info->num_rxq_entries);
Looks like this assignment is dead code as we are overwriting it later
in both the branches.
> + struct nbl_chan_ring *ring;
> + dma_addr_t dma_addr;
> +
> + if (tx)
> + ring = &chan_info->txq;
> + else
> + ring = &chan_info->rxq;
> + dma_addr = ring->dma;
> + if (tx) {
> + size_bwid = ilog2(chan_info->num_txq_entries);
> + hw_ops->config_mailbox_txq(p, dma_addr, size_bwid);
> + } else {
> + size_bwid = ilog2(chan_info->num_rxq_entries);
> + hw_ops->config_mailbox_rxq(p, dma_addr, size_bwid);
> + }
> +}
> +
> +static int nbl_chan_alloc_all_tx_bufs(struct nbl_channel_mgt *chan_mgt,
> + struct nbl_chan_info *chan_info)
> +{
> + struct nbl_chan_ring *txq = &chan_info->txq;
> + struct device *dev = chan_mgt->common->dev;
> + struct nbl_chan_buf *buf;
> + u16 i;
> +
> + for (i = 0; i < chan_info->num_txq_entries; i++) {
> + buf = &txq->buf[i];
> + buf->va = dmam_alloc_coherent(dev, chan_info->txq_buf_size,
> + &buf->pa,
> + GFP_KERNEL);
> + if (!buf->va) {
> + dev_err(dev,
> + "Allocate buffer for chan tx queue failed\n");
> + return -ENOMEM;
> + }
> + }
> +
> + txq->next_to_clean = 0;
> + txq->next_to_use = 0;
> + txq->tail_ptr = 0;
> +
> + return 0;
> +}
> +
> +static int
> +nbl_chan_cfg_mailbox_qinfo_map_table(struct nbl_channel_mgt *chan_mgt)
> +{
> + struct nbl_hw_ops *hw_ops = chan_mgt->hw_ops_tbl->ops;
> + struct nbl_common_info *common = chan_mgt->common;
> + struct nbl_hw_mgt *p = chan_mgt->hw_ops_tbl->priv;
> + u16 func_id;
> + u32 pf_mask;
> +
> + pf_mask = hw_ops->get_host_pf_mask(p);
> + for (func_id = 0; func_id < NBL_MAX_PF; func_id++) {
> + if (!(pf_mask & (1 << func_id)))
> + hw_ops->cfg_mailbox_qinfo(p, func_id, common->hw_bus,
> + common->devid,
> + common->function + func_id);
> + }
> +
> + return 0;
> +}
> +
> +static int nbl_chan_cfg_qinfo_map_table(struct nbl_channel_mgt *chan_mgt,
> + u8 chan_type)
> +{
> + return nbl_chan_cfg_mailbox_qinfo_map_table(chan_mgt);
> +}
> +
> +#define NBL_UPDATE_QUEUE_TAIL_PTR(chan_info, hw_ops, chan_mgt, tail_ptr, qid)\
> +do { \
> + (void)(chan_info); \
> + typeof(hw_ops) _hw_ops = (hw_ops); \
> + typeof(chan_mgt) _chan_mgt = (chan_mgt); \
> + typeof(tail_ptr) _tail_ptr = (tail_ptr); \
> + typeof(qid) _qid = (qid); \
> + (_hw_ops)->update_mailbox_queue_tail_ptr( \
> + _chan_mgt->hw_ops_tbl->priv, _tail_ptr, _qid); \
> +} while (0)
> +
> +static int nbl_chan_alloc_all_rx_bufs(struct nbl_channel_mgt *chan_mgt,
> + struct nbl_chan_info *chan_info)
> +{
> + struct nbl_hw_ops *hw_ops = chan_mgt->hw_ops_tbl->ops;
> + struct nbl_chan_ring *rxq = &chan_info->rxq;
> + struct device *dev = chan_mgt->common->dev;
> + struct nbl_chan_rx_desc *desc;
> + struct nbl_chan_buf *buf;
> + u32 retry_times;
> + u16 i;
> +
> + for (i = 0; i < chan_info->num_rxq_entries; i++) {
> + buf = &rxq->buf[i];
> + buf->va = dmam_alloc_coherent(dev, chan_info->rxq_buf_size,
> + &buf->pa,
> + GFP_KERNEL);
> + if (!buf->va) {
> + dev_err(dev,
> + "Allocate buffer for chan rx queue failed\n");
> + goto err;
> + }
> + }
> +
> + desc = rxq->desc.rx_desc;
> + for (i = 0; i < chan_info->num_rxq_entries - 1; i++) {
> + buf = &rxq->buf[i];
> + desc[i].flags = BIT(NBL_CHAN_RX_DESC_AVAIL);
> + desc[i].buf_addr = buf->pa;
> + desc[i].buf_len = chan_info->rxq_buf_size;
> + }
> +
> + rxq->next_to_clean = 0;
> + rxq->next_to_use = chan_info->num_rxq_entries - 1;
> + rxq->tail_ptr = chan_info->num_rxq_entries - 1;
> +
> + NBL_UPDATE_QUEUE_TAIL_PTR(chan_info, hw_ops, chan_mgt, rxq->tail_ptr,
> + NBL_MB_RX_QID);
> +
> + for (retry_times = 0; retry_times < 3; retry_times++) {
> + NBL_UPDATE_QUEUE_TAIL_PTR(chan_info, hw_ops, chan_mgt,
> + rxq->tail_ptr, NBL_MB_RX_QID);
> + usleep_range(NBL_CHAN_TX_WAIT_US * 50,
> + NBL_CHAN_TX_WAIT_US * 60);
> + }
> +
Admittedly I have limited understanding here, but this loop looks
suspicious. We are updating the tail ptr 3 times without any
check/condition. Is this some sort of workaround to handle some HW
behavior? if so, a comment here would really help.
> + return 0;
> +err:
> + return -ENOMEM;
> +}
> +
> +static int nbl_chan_alloc_all_bufs(struct nbl_channel_mgt *chan_mgt,
> + struct nbl_chan_info *chan_info)
> +{
> + int err;
> +
> + err = nbl_chan_alloc_all_tx_bufs(chan_mgt, chan_info);
> + if (err)
> + return err;
> + err = nbl_chan_alloc_all_rx_bufs(chan_mgt, chan_info);
> + if (err)
> + goto alloc_rx_bufs_err;
Why do we need a label here if no cleanup is needed? How about simply
return nbl_chan_alloc_all_rx_bufs(chan_mgt, chan_info);
> +
> + return 0;
> +
> +alloc_rx_bufs_err:
> + return err;
> +}
> +
> +static void nbl_chan_stop_queue(struct nbl_channel_mgt *chan_mgt,
> + struct nbl_chan_info *chan_info)
> +{
> + struct nbl_hw_ops *hw_ops = chan_mgt->hw_ops_tbl->ops;
> +
> + hw_ops->stop_mailbox_rxq(chan_mgt->hw_ops_tbl->priv);
> + hw_ops->stop_mailbox_txq(chan_mgt->hw_ops_tbl->priv);
> +}
> +
> +static int nbl_chan_teardown_queue(struct nbl_channel_mgt *chan_mgt,
> + u8 chan_type)
> +{
> + struct nbl_chan_info *chan_info = chan_mgt->chan_info[chan_type];
> +
> + nbl_chan_stop_queue(chan_mgt, chan_info);
Minor nit: A space here perhaps?
> + return 0;
> +}
> +
> +static int nbl_chan_setup_queue(struct nbl_channel_mgt *chan_mgt, u8 chan_type)
> +{
> + struct nbl_chan_info *chan_info = chan_mgt->chan_info[chan_type];
> + struct nbl_common_info *common = chan_mgt->common;
> + int err;
> +
> + nbl_chan_init_queue_param(chan_info, NBL_CHAN_QUEUE_LEN,
> + NBL_CHAN_QUEUE_LEN, NBL_CHAN_BUF_LEN,
> + NBL_CHAN_BUF_LEN);
> + err = nbl_chan_init_queue(common, chan_info);
> + if (err)
> + return err;
> +
> + nbl_chan_config_queue(chan_mgt, chan_info, true); /* tx */
> + nbl_chan_config_queue(chan_mgt, chan_info, false); /* rx */
> +
> + err = nbl_chan_alloc_all_bufs(chan_mgt, chan_info);
> + if (err)
> + goto chan_q_setup_fail;
> +
> + return 0;
> +
> +chan_q_setup_fail:
> + nbl_chan_teardown_queue(chan_mgt, chan_type);
> + return err;
> +}
> +
> +static int nbl_chan_update_txqueue(struct nbl_channel_mgt *chan_mgt,
> + struct nbl_chan_info *chan_info,
> + struct nbl_chan_tx_param *param)
> +{
> + struct nbl_chan_ring *txq = &chan_info->txq;
> + struct nbl_chan_tx_desc *tx_desc =
> + NBL_CHAN_TX_RING_TO_DESC(txq, txq->next_to_use);
> + struct nbl_chan_buf *tx_buf =
> + NBL_CHAN_TX_RING_TO_BUF(txq, txq->next_to_use);
> +
> + if (param->arg_len > NBL_CHAN_BUF_LEN - sizeof(*tx_desc))
> + return -EINVAL;
> +
> + tx_desc->dstid = param->dstid;
> + tx_desc->msg_type = param->msg_type;
> + tx_desc->msgid = param->msgid;
> +
> + if (param->arg_len > NBL_CHAN_TX_DESC_EMBEDDED_DATA_LEN) {
> + memcpy(tx_buf->va, param->arg, param->arg_len);
> + tx_desc->buf_addr = tx_buf->pa;
> + tx_desc->buf_len = param->arg_len;
> + tx_desc->data_len = 0;
> + } else {
> + memcpy(tx_desc->data, param->arg, param->arg_len);
> + tx_desc->buf_len = 0;
> + tx_desc->data_len = param->arg_len;
> + }
> + tx_desc->flags = BIT(NBL_CHAN_TX_DESC_AVAIL);
> +
> + txq->next_to_use =
> + NBL_NEXT_ID(txq->next_to_use, chan_info->num_txq_entries - 1);
> + txq->tail_ptr++;
> +
> + return 0;
> +}
> +
> +static int nbl_chan_kick_tx_ring(struct nbl_channel_mgt *chan_mgt,
> + struct nbl_chan_info *chan_info)
> +{
> + struct nbl_hw_ops *hw_ops = chan_mgt->hw_ops_tbl->ops;
> + struct nbl_chan_ring *txq = &chan_info->txq;
> + struct device *dev = chan_mgt->common->dev;
> + struct nbl_chan_tx_desc *tx_desc;
> + int i = 0;
> +
> + NBL_UPDATE_QUEUE_TAIL_PTR(chan_info, hw_ops, chan_mgt, txq->tail_ptr,
> + NBL_MB_TX_QID);
> +
> + tx_desc = NBL_CHAN_TX_RING_TO_DESC(txq, txq->next_to_clean);
> +
> + while (!(tx_desc->flags & BIT(NBL_CHAN_TX_DESC_USED))) {
> + udelay(NBL_CHAN_TX_WAIT_US);
> + i++;
> +
> + if (!(i % NBL_CHAN_TX_REKICK_WAIT_TIMES))
> + NBL_UPDATE_QUEUE_TAIL_PTR(chan_info, hw_ops, chan_mgt,
> + txq->tail_ptr, NBL_MB_TX_QID);
> +
> + if (i == NBL_CHAN_TX_WAIT_TIMES) {
> + dev_err(dev, "chan send message type: %d timeout\n",
> + tx_desc->msg_type);
> + return -ETIMEDOUT;
> + }
> + }
> +
> + txq->next_to_clean = txq->next_to_use;
nit here as well. This looks like a common issue with this patch.
> + return 0;
> +}
> +
> +static void nbl_chan_recv_ack_msg(void *priv, u16 srcid, u16 msgid, void *data,
> + u32 data_len)
> +{
> + struct nbl_channel_mgt *chan_mgt = (struct nbl_channel_mgt *)priv;
> + struct nbl_chan_waitqueue_head *wait_head = NULL;
> + union nbl_chan_msg_id ack_msgid = { { 0 } };
> + struct device *dev = chan_mgt->common->dev;
> + struct nbl_chan_info *chan_info =
> + chan_mgt->chan_info[NBL_CHAN_TYPE_MAILBOX];
> + u32 *payload = (u32 *)data;
> + u32 ack_datalen;
> + u32 copy_len;
> +
> + if (data_len < 3 * sizeof(u32)) {
> + dev_err(dev, "Invalid ACK data_len: %u\n", data_len);
> + return;
> + }
> + ack_datalen = data_len - 3 * sizeof(u32);
> + ack_msgid.id = *(u16 *)(payload + 1);
> + if (ack_msgid.info.loc >= NBL_CHAN_QUEUE_LEN) {
> + dev_err(dev, "chan recv msg loc: %d err\n", ack_msgid.info.loc);
> + return;
> + }
> + wait_head = &chan_info->wait[ack_msgid.info.loc];
> + wait_head->ack_err = *(payload + 2);
> +
> + copy_len = min_t(u32, wait_head->ack_data_len, ack_datalen);
> + if (wait_head->ack_err >= 0 && copy_len > 0)
> + memcpy((char *)wait_head->ack_data, payload + 3, copy_len);
> + wait_head->ack_data_len = (u16)copy_len;
> +
> + /*
> + * Ensure all writes to ack_data and ack_data_len are completed
> + * before setting the 'acked' flag. This prevents other threads
> + * from observing stale or partially updated data.
> + */
> + wmb();
> + wait_head->acked = 1;
> + if (wait_head->need_waked)
> + wake_up(&wait_head->wait_queue);
> +}
> +
> +static void nbl_chan_recv_msg(struct nbl_channel_mgt *chan_mgt, void *data)
> +{
> + struct device *dev = chan_mgt->common->dev;
> + struct nbl_chan_msg_node_data *msg_handler;
> + u16 msg_type, payload_len, srcid, msgid;
> + struct nbl_chan_tx_desc *tx_desc;
> + void *payload;
> +
> + tx_desc = data;
> + msg_type = tx_desc->msg_type;
> + dev_dbg(dev, "recv msg_type: %d\n", tx_desc->msg_type);
> +
> + srcid = tx_desc->srcid;
> + msgid = tx_desc->msgid;
> + /* Only check if the value exceeds the maximum, relying on the hash
> + * table to filter invalid message IDs.
> + * The gap values are reserved for future protocol extensions.
> + */
> + if (msg_type >= NBL_CHAN_MSG_MAILBOX_MAX)
> + return;
> +
> + if (tx_desc->data_len) {
> + payload = (void *)tx_desc->data;
> + payload_len = tx_desc->data_len;
> + } else {
> + payload = (void *)(tx_desc + 1);
> + payload_len = tx_desc->buf_len;
> + }
> +
> + msg_handler =
> + nbl_common_get_hash_node(chan_mgt->handle_hash_tbl, &msg_type);
> + if (!msg_handler) {
> + pr_err("Invalid msg_type: %d\n", msg_type);
> + return;
> + }
> + msg_handler->func(msg_handler->priv, srcid, msgid, payload,
> + payload_len);
> +}
> +
> +static void nbl_chan_advance_rx_ring(struct nbl_channel_mgt *chan_mgt,
> + struct nbl_chan_info *chan_info,
> + struct nbl_chan_ring *rxq)
> +{
> + struct nbl_hw_ops *hw_ops = chan_mgt->hw_ops_tbl->ops;
> + struct nbl_chan_rx_desc *rx_desc;
> + struct nbl_chan_buf *rx_buf;
> + u16 next_to_use;
> +
> + next_to_use = rxq->next_to_use;
> + rx_desc = NBL_CHAN_RX_RING_TO_DESC(rxq, next_to_use);
> + rx_buf = NBL_CHAN_RX_RING_TO_BUF(rxq, next_to_use);
> +
> + rx_desc->flags = BIT(NBL_CHAN_RX_DESC_AVAIL);
> + rx_desc->buf_addr = rx_buf->pa;
> + rx_desc->buf_len = chan_info->rxq_buf_size;
> +
> + rxq->next_to_use++;
> + if (rxq->next_to_use == chan_info->num_rxq_entries)
> + rxq->next_to_use = 0;
> + rxq->tail_ptr++;
> +
> + NBL_UPDATE_QUEUE_TAIL_PTR(chan_info, hw_ops, chan_mgt, rxq->tail_ptr,
> + NBL_MB_RX_QID);
> +}
> +
> +static void nbl_chan_clean_queue(struct nbl_channel_mgt *chan_mgt,
> + struct nbl_chan_info *chan_info)
> +{
> + struct nbl_chan_ring *rxq = &chan_info->rxq;
> + struct device *dev = chan_mgt->common->dev;
> + struct nbl_chan_rx_desc *rx_desc;
> + struct nbl_chan_buf *rx_buf;
> + u16 next_to_clean;
> +
> + next_to_clean = rxq->next_to_clean;
> + rx_desc = NBL_CHAN_RX_RING_TO_DESC(rxq, next_to_clean);
> + rx_buf = NBL_CHAN_RX_RING_TO_BUF(rxq, next_to_clean);
> + while (rx_desc->flags & BIT(NBL_CHAN_RX_DESC_USED)) {
> + if (!(rx_desc->flags & BIT(NBL_CHAN_RX_DESC_WRITE)))
> + dev_dbg(dev,
> + "mailbox rx flag 0x%x has no NBL_CHAN_RX_DESC_WRITE\n",
> + rx_desc->flags);
> +
> + dma_rmb();
> + nbl_chan_recv_msg(chan_mgt, rx_buf->va);
> + nbl_chan_advance_rx_ring(chan_mgt, chan_info, rxq);
> + next_to_clean++;
> + if (next_to_clean == chan_info->num_rxq_entries)
> + next_to_clean = 0;
> + rx_desc = NBL_CHAN_RX_RING_TO_DESC(rxq, next_to_clean);
> + rx_buf = NBL_CHAN_RX_RING_TO_BUF(rxq, next_to_clean);
> + }
> + rxq->next_to_clean = next_to_clean;
> +}
> +
> +static void nbl_chan_clean_queue_subtask(struct nbl_channel_mgt *chan_mgt,
> + u8 chan_type)
> +{
> + struct nbl_chan_info *chan_info = chan_mgt->chan_info[chan_type];
> +
> + if (!test_bit(NBL_CHAN_INTERRUPT_READY, chan_info->state) ||
> + test_bit(NBL_CHAN_RESETTING, chan_info->state))
> + return;
> +
> + nbl_chan_clean_queue(chan_mgt, chan_info);
> +}
> +
> +static int nbl_chan_get_msg_id(struct nbl_chan_info *chan_info,
> + union nbl_chan_msg_id *msgid)
> +{
> + int valid_loc = chan_info->wait_head_index, i;
> + struct nbl_chan_waitqueue_head *wait = NULL;
> +
> + for (i = 0; i < NBL_CHAN_QUEUE_LEN; i++) {
> + wait = &chan_info->wait[valid_loc];
> +
> + if (wait->status != NBL_MBX_STATUS_WAITING) {
> + wait->msg_index = NBL_NEXT_ID(wait->msg_index,
> + NBL_CHAN_MSG_INDEX_MAX);
> + msgid->info.index = wait->msg_index;
> + msgid->info.loc = valid_loc;
> +
> + valid_loc = NBL_NEXT_ID(valid_loc,
> + chan_info->num_txq_entries - 1);
> + chan_info->wait_head_index = valid_loc;
> + return 0;
> + }
> +
> + valid_loc =
> + NBL_NEXT_ID(valid_loc, chan_info->num_txq_entries - 1);
> + }
> + /*
> + * the current NBL_CHAN_QUEUE_LEN configuration meets the design
> + * requirements and theoretically should not return errors, the
> + * following scenarios may still cause the waiting queue to
> + * become full:
> + * High-concurrency scenarios:
> + * If the sender (calling nbl_chan_send_msg()) generates messages
> + * at a rate far exceeding the receiver's ability to process
> + * acknowledgments (ACKs),the waiting queue may become fully occupied.
> + * Delayed or failed ACK handling by the receiver:
> + * The receiver may fail to send ACKs in a timely manner due to
> + * processing delays, blocking, or faults, causing the sender's
> + * waiting queue slots to remain occupied for an extended period.
> + */
> + return -EAGAIN;
> +}
> +
> +static int nbl_chan_send_msg(struct nbl_channel_mgt *chan_mgt,
> + struct nbl_chan_send_info *chan_send)
> +{
> + struct nbl_common_info *common = chan_mgt->common;
> + struct nbl_chan_waitqueue_head *wait_head;
> + struct nbl_chan_tx_param tx_param = { 0 };
> + union nbl_chan_msg_id msgid = { { 0 } };
> + int i = NBL_CHAN_TX_WAIT_ACK_TIMES, ret;
> + struct nbl_chan_info *chan_info =
> + chan_mgt->chan_info[NBL_CHAN_TYPE_MAILBOX];
> + struct device *dev = common->dev;
> +
> + if (test_bit(NBL_CHAN_ABNORMAL, chan_info->state))
> + return -EIO;
> +
> + mutex_lock(&chan_info->txq_lock);
> +
> + ret = nbl_chan_get_msg_id(chan_info, &msgid);
> + if (ret) {
> + mutex_unlock(&chan_info->txq_lock);
> + dev_err(dev,
> + "Channel tx wait head full, send msgtype:%u to dstid:%u failed\n",
> + chan_send->msg_type, chan_send->dstid);
> + return ret;
> + }
> +
> + tx_param.msg_type = chan_send->msg_type;
> + tx_param.arg = chan_send->arg;
> + tx_param.arg_len = chan_send->arg_len;
> + tx_param.dstid = chan_send->dstid;
> + tx_param.msgid = msgid.id;
> +
> + ret = nbl_chan_update_txqueue(chan_mgt, chan_info, &tx_param);
> + if (ret) {
> + mutex_unlock(&chan_info->txq_lock);
> + dev_err(dev,
> + "Channel tx queue full, send msgtype:%u to dstid:%u failed\n",
> + chan_send->msg_type, chan_send->dstid);
> + return ret;
> + }
> +
> + wait_head = &chan_info->wait[msgid.info.loc];
> + init_waitqueue_head(&wait_head->wait_queue);
> + wait_head->acked = 0;
> + wait_head->ack_data = chan_send->resp;
> + wait_head->ack_data_len = chan_send->resp_len;
> + wait_head->msg_type = chan_send->msg_type;
> + wait_head->need_waked = chan_send->ack;
> + wait_head->msg_index = msgid.info.index;
> + wait_head->status = chan_send->ack ? NBL_MBX_STATUS_WAITING :
> + NBL_MBX_STATUS_IDLE;
> +
> + ret = nbl_chan_kick_tx_ring(chan_mgt, chan_info);
> +
> + mutex_unlock(&chan_info->txq_lock);
> + if (ret) {
> + wait_head->status = NBL_MBX_STATUS_TIMEOUT;
> + return ret;
> + }
> + if (!chan_send->ack)
> + return 0;
> +
> + if (test_bit(NBL_CHAN_INTERRUPT_READY, chan_info->state)) {
> + ret = wait_event_timeout(wait_head->wait_queue,
> + wait_head->acked,
> + NBL_CHAN_ACK_WAIT_TIME);
> + if (!ret) {
> + wait_head->status = NBL_MBX_STATUS_TIMEOUT;
> + dev_err(dev,
> + "Channel waiting ack failed, message type: %d, msg id: %u\n",
> + chan_send->msg_type, msgid.id);
> + return -ETIMEDOUT;
> + }
> +
> + /*
> + * ensure that after observing 'acked == 1', all subsequent
> + * reads (ack_data_len, ack_err) observe the latest values
> + * written by the sender (nbl_chan_recv_ack_msg()). This
> + * prevents stale reads of ACK data or status.
> + */
> + rmb();
> + chan_send->ack_len = wait_head->ack_data_len;
> + wait_head->status = NBL_MBX_STATUS_IDLE;
> + return wait_head->ack_err;
> + }
> +
> + /*polling wait mailbox ack*/
> + while (i--) {
> + nbl_chan_clean_queue(chan_mgt, chan_info);
> +
> + if (wait_head->acked) {
> + chan_send->ack_len = wait_head->ack_data_len;
> + wait_head->status = NBL_MBX_STATUS_IDLE;
> + return wait_head->ack_err;
> + }
> + usleep_range(NBL_CHAN_TX_WAIT_ACK_US_MIN,
> + NBL_CHAN_TX_WAIT_ACK_US_MAX);
> + }
> +
> + wait_head->status = NBL_MBX_STATUS_TIMEOUT;
> + dev_err(dev,
> + "Channel polling ack failed, message type: %d msg id: %u\n",
> + chan_send->msg_type, msgid.id);
> + return -EFAULT;
> +}
> +
> +static int nbl_chan_send_ack(struct nbl_channel_mgt *chan_mgt,
> + struct nbl_chan_ack_info *chan_ack)
> +{
> + u32 len = 3 * sizeof(u32) + chan_ack->data_len;
> + struct nbl_chan_send_info chan_send;
> + u32 *tmp;
> + int ret;
> +
> + tmp = kzalloc(len, GFP_KERNEL);
> + if (!tmp)
> + return -ENOMEM;
> +
> + tmp[0] = chan_ack->msg_type;
> + tmp[1] = chan_ack->msgid;
> + tmp[2] = (u32)chan_ack->err;
> + if (chan_ack->data && chan_ack->data_len)
> + memcpy(&tmp[3], chan_ack->data, chan_ack->data_len);
> +
> + NBL_CHAN_SEND(chan_send, chan_ack->dstid, NBL_CHAN_MSG_ACK, tmp, len,
> + NULL, 0, 0);
> + ret = nbl_chan_send_msg(chan_mgt, &chan_send);
> + kfree(tmp);
> +
> + return ret;
> +}
> +
> +static int nbl_chan_register_msg(struct nbl_channel_mgt *chan_mgt, u16 msg_type,
> + nbl_chan_resp func, void *callback)
> +{
> + return nbl_chan_add_msg_handler(chan_mgt, msg_type, func, callback);
> +}
> +
> +static bool nbl_chan_check_queue_exist(struct nbl_channel_mgt *chan_mgt,
> + u8 chan_type)
> +{
> + struct nbl_chan_info *chan_info = chan_mgt->chan_info[chan_type];
> +
> + return chan_info ? true : false;
Perhaps we can simplify this as return !!chan_info;
> +}
> +
> +static void nbl_chan_register_chan_task(struct nbl_channel_mgt *chan_mgt,
> + u8 chan_type, struct work_struct *task)
> +{
> + struct nbl_chan_info *chan_info = chan_mgt->chan_info[chan_type];
> +
> + chan_info->clean_task = task;
> +}
> +
> +static void nbl_chan_set_queue_state(struct nbl_channel_mgt *chan_mgt,
> + enum nbl_chan_state state, u8 chan_type,
> + u8 set)
> +{
> + struct nbl_chan_info *chan_info = chan_mgt->chan_info[chan_type];
> +
> + if (set)
> + set_bit(state, chan_info->state);
> + else
> + clear_bit(state, chan_info->state);
> +}
> +
> static struct nbl_channel_ops chan_ops = {
> + .send_msg = nbl_chan_send_msg,
> + .send_ack = nbl_chan_send_ack,
> + .register_msg = nbl_chan_register_msg,
> + .cfg_chan_qinfo_map_table = nbl_chan_cfg_qinfo_map_table,
> + .check_queue_exist = nbl_chan_check_queue_exist,
> + .setup_queue = nbl_chan_setup_queue,
> + .teardown_queue = nbl_chan_teardown_queue,
> + .clean_queue_subtask = nbl_chan_clean_queue_subtask,
> + .register_chan_task = nbl_chan_register_chan_task,
> + .set_queue_state = nbl_chan_set_queue_state,
> };
>
> static struct nbl_channel_mgt *
> @@ -18,6 +769,7 @@ nbl_chan_setup_chan_mgt(struct nbl_adapter *adapter)
> struct device *dev = &adapter->pdev->dev;
> struct nbl_chan_info *mailbox;
> struct nbl_channel_mgt *chan_mgt;
> + int ret;
>
> chan_mgt = devm_kzalloc(dev, sizeof(*chan_mgt), GFP_KERNEL);
> if (!chan_mgt)
> @@ -32,6 +784,10 @@ nbl_chan_setup_chan_mgt(struct nbl_adapter *adapter)
> mailbox->chan_type = NBL_CHAN_TYPE_MAILBOX;
> chan_mgt->chan_info[NBL_CHAN_TYPE_MAILBOX] = mailbox;
>
> + ret = nbl_chan_init_msg_handler(chan_mgt);
> + if (ret)
> + return ERR_PTR(-ENOMEM);
> +
> return chan_mgt;
> }
>
> @@ -39,6 +795,7 @@ static struct nbl_channel_ops_tbl *
> nbl_chan_setup_ops(struct device *dev, struct nbl_channel_mgt *chan_mgt)
> {
> struct nbl_channel_ops_tbl *chan_ops_tbl;
> + int ret;
>
> chan_ops_tbl = devm_kzalloc(dev, sizeof(*chan_ops_tbl), GFP_KERNEL);
> if (!chan_ops_tbl)
> @@ -47,6 +804,11 @@ nbl_chan_setup_ops(struct device *dev, struct nbl_channel_mgt *chan_mgt)
> chan_ops_tbl->ops = &chan_ops;
> chan_ops_tbl->priv = chan_mgt;
>
> + ret = nbl_chan_register_msg(chan_mgt, NBL_CHAN_MSG_ACK,
> + nbl_chan_recv_ack_msg, chan_mgt);
> + if (ret)
> + return ERR_PTR(-ENOMEM);
> +
> return chan_ops_tbl;
> }
>
> @@ -73,10 +835,14 @@ int nbl_chan_init_common(struct nbl_adapter *adap)
> return 0;
>
> setup_ops_fail:
> + nbl_chan_remove_msg_handler(chan_mgt);
> setup_mgt_fail:
> return ret;
> }
>
> void nbl_chan_remove_common(struct nbl_adapter *adap)
> {
> + struct nbl_channel_mgt *chan_mgt = adap->core.chan_mgt;
> +
> + nbl_chan_remove_msg_handler(chan_mgt);
> }
> diff --git a/drivers/net/ethernet/nebula-matrix/nbl/nbl_channel/nbl_channel.h b/drivers/net/ethernet/nebula-matrix/nbl/nbl_channel/nbl_channel.h
> index 637912d1e806..901b1d7166fe 100644
> --- a/drivers/net/ethernet/nebula-matrix/nbl/nbl_channel/nbl_channel.h
> +++ b/drivers/net/ethernet/nebula-matrix/nbl/nbl_channel/nbl_channel.h
> @@ -15,10 +15,139 @@
> #include "../nbl_include/nbl_def_common.h"
> #include "../nbl_core.h"
>
> +#define NBL_CHAN_TX_RING_TO_DESC(tx_ring, i) \
> + (&((((tx_ring)->desc.tx_desc))[i]))
> +#define NBL_CHAN_RX_RING_TO_DESC(rx_ring, i) \
> + (&((((rx_ring)->desc.rx_desc))[i]))
> +#define NBL_CHAN_TX_RING_TO_BUF(tx_ring, i) (&(((tx_ring)->buf)[i]))
> +#define NBL_CHAN_RX_RING_TO_BUF(rx_ring, i) (&(((rx_ring)->buf)[i]))
> +
> +#define NBL_CHAN_TX_WAIT_US 100
> +#define NBL_CHAN_TX_REKICK_WAIT_TIMES 2000
> +#define NBL_CHAN_TX_WAIT_TIMES 30000
> +#define NBL_CHAN_TX_WAIT_ACK_US_MIN 100
> +#define NBL_CHAN_TX_WAIT_ACK_US_MAX 120
> +#define NBL_CHAN_TX_WAIT_ACK_TIMES 50000
> +#define NBL_CHAN_QUEUE_LEN 256
> +#define NBL_CHAN_BUF_LEN 4096
> +#define NBL_CHAN_TX_DESC_EMBEDDED_DATA_LEN 16
> +
> +#define NBL_CHAN_TX_DESC_AVAIL 0
> +#define NBL_CHAN_TX_DESC_USED 1
> +#define NBL_CHAN_RX_DESC_WRITE 1
> +#define NBL_CHAN_RX_DESC_AVAIL 3
> +#define NBL_CHAN_RX_DESC_USED 4
> +
> +#define NBL_CHAN_ACK_WAIT_TIME (3 * HZ)
> +
> +#define NBL_CHAN_HANDLER_TBL_BUCKET_SIZE 512
> +
> +enum {
> + NBL_MB_RX_QID = 0,
> + NBL_MB_TX_QID = 1,
> +};
> +
> +enum {
> + NBL_MBX_STATUS_IDLE = 0,
> + NBL_MBX_STATUS_WAITING,
> + NBL_MBX_STATUS_TIMEOUT = -1,
> +};
> +
> +struct nbl_chan_tx_param {
> + enum nbl_chan_msg_type msg_type;
> + void *arg;
> + size_t arg_len;
> + u16 dstid;
> + u16 msgid;
> +};
> +
> +struct nbl_chan_buf {
> + void *va;
> + dma_addr_t pa;
> + size_t size;
> +};
> +
> +struct nbl_chan_tx_desc {
> + u16 flags;
> + u16 srcid;
> + u16 dstid;
> + u16 data_len;
> + u16 buf_len;
> + u64 buf_addr;
> + u16 msg_type;
> + u8 data[16];
> + u16 msgid;
> + u8 rsv[26];
> +} __packed;
> +
> +struct nbl_chan_rx_desc {
> + u16 flags;
> + u32 buf_len;
> + u16 buf_id;
> + u64 buf_addr;
> +} __packed;
> +
> +union nbl_chan_desc_ptr {
> + struct nbl_chan_tx_desc *tx_desc;
> + struct nbl_chan_rx_desc *rx_desc;
> +};
> +
> +struct nbl_chan_ring {
> + union nbl_chan_desc_ptr desc;
> + struct nbl_chan_buf *buf;
> + u16 next_to_use;
> + u16 tail_ptr;
> + u16 next_to_clean;
> + dma_addr_t dma;
> +};
> +
> +#define NBL_CHAN_MSG_INDEX_MAX 63
> +
> +union nbl_chan_msg_id {
> + struct nbl_chan_msg_id_info {
> + u16 index : 6;
> + u16 loc : 10;
> + } info;
> + u16 id;
> +};
> +
> +struct nbl_chan_waitqueue_head {
> + struct wait_queue_head wait_queue;
> + char *ack_data;
> + int acked;
> + int ack_err;
> + u16 ack_data_len;
> + u16 need_waked;
> + u16 msg_type;
> + u8 status;
> + u8 msg_index;
> +};
> +
> struct nbl_chan_info {
> + struct nbl_chan_ring txq;
> + struct nbl_chan_ring rxq;
> + struct nbl_chan_waitqueue_head *wait;
> + /*
> + *Protects access to the TX queue (txq) and related metadata.
> + *This mutex ensures exclusive access when updating the TX queue
> + *or waiting for ACKs to prevent race conditions.
> + */
> + struct mutex txq_lock;
> + struct work_struct *clean_task;
> + u16 wait_head_index;
> + u16 num_txq_entries;
> + u16 num_rxq_entries;
> + u16 txq_buf_size;
> + u16 rxq_buf_size;
> + DECLARE_BITMAP(state, NBL_CHAN_STATE_NBITS);
> u8 chan_type;
> };
>
> +struct nbl_chan_msg_node_data {
> + nbl_chan_resp func;
> + void *priv;
> +};
> +
> struct nbl_channel_mgt {
> struct nbl_common_info *common;
> struct nbl_hw_ops_tbl *hw_ops_tbl;
> diff --git a/drivers/net/ethernet/nebula-matrix/nbl/nbl_common/nbl_common.c b/drivers/net/ethernet/nebula-matrix/nbl/nbl_common/nbl_common.c
> new file mode 100644
> index 000000000000..5f75902bbdc0
> --- /dev/null
> +++ b/drivers/net/ethernet/nebula-matrix/nbl/nbl_common/nbl_common.c
> @@ -0,0 +1,207 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (c) 2025 Nebula Matrix Limited.
> + */
> +
> +#include <linux/device.h>
> +#include "nbl_common.h"
> +
> +static struct nbl_common_wq_mgt *wq_mgt;
> +
> +void nbl_common_queue_work(struct work_struct *task)
> +{
> + queue_work(wq_mgt->ctrl_dev_wq, task);
> +}
> +
> +void nbl_common_destroy_wq(void)
> +{
> + destroy_workqueue(wq_mgt->ctrl_dev_wq);
> + kfree(wq_mgt);
> +}
> +
> +int nbl_common_create_wq(void)
> +{
> + wq_mgt = kzalloc_obj(*wq_mgt);
> + if (!wq_mgt)
> + return -ENOMEM;
> +
> + wq_mgt->ctrl_dev_wq = alloc_workqueue("%s", WQ_MEM_RECLAIM | WQ_UNBOUND,
> + 0, "nbl_ctrldev_wq");
> + if (!wq_mgt->ctrl_dev_wq) {
> + pr_err("Failed to create workqueue nbl_ctrldev_wq\n");
> + goto alloc_ctrl_dev_wq_failed;
> + }
> +
> + return 0;
> +alloc_ctrl_dev_wq_failed:
> + kfree(wq_mgt);
> + return -ENOMEM;
> +}
> +
> +u32 nbl_common_pf_id_subtraction_mgtpf_id(struct nbl_common_info *common,
> + u32 pf_id)
> +{
> + u32 diff = U32_MAX;
> +
> + if (pf_id >= common->mgt_pf)
> + diff = pf_id - common->mgt_pf;
> +
> + return diff;
> +}
> +
> +static u32 nbl_common_calc_hash_key(void *key, u32 key_size, u32 bucket_size)
> +{
> + u32 value = 0;
> + u32 hash_val;
> + u32 i;
> +
> + if (bucket_size == NBL_HASH_TBL_LIST_BUCKET_SIZE)
> + return 0;
> +
> + for (i = 0; i < key_size; i++)
> + value += *((u8 *)key + i);
> +
> + hash_val = __hash_32(value);
> +
> + return hash_val % bucket_size;
> +}
The hash looks pretty primitive. Looks like keys with same bytes but in
different order would collide. Mathmatically speaking, we are destroying
entropy before the actual hash even runs.
> +
> +/*
> + * alloc a hash table
> + * the table support multi thread
> + */
> +struct nbl_hash_tbl_mgt *
> +nbl_common_init_hash_table(struct nbl_hash_tbl_key *key)
> +{
> + struct nbl_hash_tbl_mgt *tbl_mgt;
> + int bucket_size;
> + int i;
> +
> + tbl_mgt = devm_kzalloc(key->dev, sizeof(*tbl_mgt), GFP_KERNEL);
> + if (!tbl_mgt)
> + return NULL;
> +
> + bucket_size = key->bucket_size;
> + tbl_mgt->hash = devm_kcalloc(key->dev, bucket_size,
> + sizeof(struct hlist_head), GFP_KERNEL);
> + if (!tbl_mgt->hash)
> + goto alloc_hash_failed;
> +
> + for (i = 0; i < bucket_size; i++)
> + INIT_HLIST_HEAD(tbl_mgt->hash + i);
> +
> + memcpy(&tbl_mgt->tbl_key, key, sizeof(struct nbl_hash_tbl_key));
> +
> + return tbl_mgt;
> +
> +alloc_hash_failed:
> + return NULL;
> +}
> +
> +/*
> + * The number of nodes in the hash table is guaranteed to be bounded
> + * (as defined in nbl_disp_setup_msg).
> + * So all hash nodes (struct nbl_hash_entry_node) and their associated keys/data
> + * are allocated via devm_kzalloc() and will be automatically freed when the
> + * device is removed. Functions like nbl_common_detach_hash_node() only remove
> + * nodes from the list but do not trigger immediate memory deallocation.
> + */
> +int nbl_common_alloc_hash_node(struct nbl_hash_tbl_mgt *tbl_mgt, void *key,
> + void *data, void **out_data)
> +{
> + struct nbl_hash_entry_node *hash_node;
> + u16 data_size;
> + u32 hash_val;
> + u16 key_size;
> +
> + hash_node = devm_kzalloc(tbl_mgt->tbl_key.dev, sizeof(*hash_node),
> + GFP_KERNEL);
> + if (!hash_node)
> + return -ENOMEM;
> +
> + key_size = tbl_mgt->tbl_key.key_size;
> + hash_node->key =
> + devm_kzalloc(tbl_mgt->tbl_key.dev, key_size, GFP_KERNEL);
> + if (!hash_node->key)
> + return -ENOMEM;
> +
> + data_size = tbl_mgt->tbl_key.data_size;
> + hash_node->data =
> + devm_kzalloc(tbl_mgt->tbl_key.dev, data_size, GFP_KERNEL);
> + if (!hash_node->data)
> + return -ENOMEM;
> +
> + memcpy(hash_node->key, key, key_size);
> + memcpy(hash_node->data, data, data_size);
> +
> + hash_val = nbl_common_calc_hash_key(key, key_size,
> + tbl_mgt->tbl_key.bucket_size);
> +
> + hlist_add_head(&hash_node->node, tbl_mgt->hash + hash_val);
> + tbl_mgt->node_num++;
> + if (out_data)
> + *out_data = hash_node->data;
> +
> + return 0;
> +}
> +
> +/*
> + * get a hash node, return the data if node exist
> + */
> +void *nbl_common_get_hash_node(struct nbl_hash_tbl_mgt *tbl_mgt, void *key)
> +{
> + struct nbl_hash_entry_node *hash_node;
> + struct hlist_head *head;
> + void *data = NULL;
> + u32 hash_val;
> + u16 key_size;
> +
> + key_size = tbl_mgt->tbl_key.key_size;
> + hash_val = nbl_common_calc_hash_key(key, key_size,
> + tbl_mgt->tbl_key.bucket_size);
> + head = tbl_mgt->hash + hash_val;
> +
> + hlist_for_each_entry(hash_node, head, node)
> + if (!memcmp(hash_node->key, key, key_size)) {
> + data = hash_node->data;
> + break;
> + }
> +
> + return data;
> +}
> +
> +/*
> + * Detaches the node from the hash list but does NOT free the memory.
> + * Memory is managed by devm and will be released automatically
> + * when the device is removed.
> + */
> +static void nbl_common_detach_hash_node(struct nbl_hash_tbl_mgt *tbl_mgt,
> + struct nbl_hash_entry_node *hash_node)
> +{
> + hlist_del(&hash_node->node);
> + tbl_mgt->node_num--;
> +}
> +
> +/*
> + * Detaches all nodes from the hash table but does NOT free their memory.
> + * Memory will be released automatically by devm when the device is removed.
> + */
> +void nbl_common_remove_hash_table(struct nbl_hash_tbl_mgt *tbl_mgt,
> + struct nbl_hash_tbl_del_key *key)
> +{
> + struct nbl_hash_entry_node *hash_node;
> + struct hlist_node *safe_node;
> + struct hlist_head *head;
> + u32 i;
> +
> + for (i = 0; i < tbl_mgt->tbl_key.bucket_size; i++) {
> + head = tbl_mgt->hash + i;
> + hlist_for_each_entry_safe(hash_node, safe_node, head, node) {
> + if (key && key->action_func)
> + key->action_func(key->action_priv,
> + hash_node->key,
> + hash_node->data);
> + nbl_common_detach_hash_node(tbl_mgt, hash_node);
> + }
> + }
> +}
> diff --git a/drivers/net/ethernet/nebula-matrix/nbl/nbl_common/nbl_common.h b/drivers/net/ethernet/nebula-matrix/nbl/nbl_common/nbl_common.h
> new file mode 100644
> index 000000000000..7d628d3556ee
> --- /dev/null
> +++ b/drivers/net/ethernet/nebula-matrix/nbl/nbl_common/nbl_common.h
> @@ -0,0 +1,34 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (c) 2025 Nebula Matrix Limited.
> + */
> +
> +#ifndef _NBL_COMMON_H_
> +#define _NBL_COMMON_H_
> +
> +#include <linux/types.h>
> +
> +#include "../nbl_include/nbl_include.h"
> +#include "../nbl_include/nbl_def_common.h"
> +
> +/* list only need one bucket size */
> +#define NBL_HASH_TBL_LIST_BUCKET_SIZE 1
> +
> +struct nbl_common_wq_mgt {
> + struct workqueue_struct *ctrl_dev_wq;
> +};
> +
> +struct nbl_hash_tbl_mgt {
> + struct nbl_hash_tbl_key tbl_key;
> + struct hlist_head *hash;
> + u16 node_num;
> +};
> +
> +/* it used for y_axis no necessay */
> +struct nbl_hash_entry_node {
> + struct hlist_node node;
> + void *key;
> + void *data;
> +};
> +
> +#endif
> diff --git a/drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_hw_leonis/nbl_hw_leonis.c b/drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_hw_leonis/nbl_hw_leonis.c
> index 6dd8d3a087dd..83a4dc584f48 100644
> --- a/drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_hw_leonis/nbl_hw_leonis.c
> +++ b/drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_hw_leonis/nbl_hw_leonis.c
> @@ -9,7 +9,150 @@
> #include <linux/spinlock.h>
> #include "nbl_hw_leonis.h"
>
> +static void nbl_hw_write_mbx_regs(struct nbl_hw_mgt *hw_mgt, u64 reg,
> + const u32 *data, u32 len)
> +{
> + u32 i;
> +
> + if (len % 4)
> + return;
> +
> + for (i = 0; i < len / 4; i++)
> + nbl_mbx_wr32(hw_mgt, reg + i * sizeof(u32), data[i]);
> +}
> +
> +static void nbl_hw_rd_regs(struct nbl_hw_mgt *hw_mgt, u64 reg, u32 *data,
> + u32 len)
> +{
> + u32 size = len / 4;
> + u32 i;
> +
> + if (len % 4)
> + return;
> +
> + spin_lock(&hw_mgt->reg_lock);
> +
> + for (i = 0; i < size; i++)
> + data[i] = rd32(hw_mgt->hw_addr, reg + i * sizeof(u32));
> + spin_unlock(&hw_mgt->reg_lock);
> +}
> +
> +static void nbl_hw_wr_regs(struct nbl_hw_mgt *hw_mgt, u64 reg, const u32 *data,
> + u32 len)
> +{
> + u32 size = len / 4;
> + u32 i;
> +
> + if (len % 4)
> + return;
> + spin_lock(&hw_mgt->reg_lock);
> + for (i = 0; i < size; i++)
> + wr32(hw_mgt->hw_addr, reg + i * sizeof(u32), data[i]);
> + spin_unlock(&hw_mgt->reg_lock);
> +}
> +
> +static void nbl_hw_update_mailbox_queue_tail_ptr(struct nbl_hw_mgt *hw_mgt,
> + u16 tail_ptr, u8 txrx)
> +{
> + /* local_qid 0 and 1 denote rx and tx queue respectively */
> + u32 local_qid = txrx;
> + u32 value = ((u32)tail_ptr << 16) | local_qid;
> +
> + /* wmb for doorbell */
> + wmb();
> + nbl_mbx_wr32(hw_mgt, NBL_MAILBOX_NOTIFY_ADDR, value);
> +}
> +
> +static void nbl_hw_config_mailbox_rxq(struct nbl_hw_mgt *hw_mgt,
> + dma_addr_t dma_addr, int size_bwid)
> +{
> + struct nbl_mailbox_qinfo_cfg_table qinfo_cfg_rx_table = { 0 };
> +
> + qinfo_cfg_rx_table.queue_rst = 1;
> + nbl_hw_write_mbx_regs(hw_mgt, NBL_MAILBOX_QINFO_CFG_RX_TABLE_ADDR,
> + (u32 *)&qinfo_cfg_rx_table,
> + sizeof(qinfo_cfg_rx_table));
> +
> + qinfo_cfg_rx_table.queue_base_addr_l = (u32)(dma_addr & 0xFFFFFFFF);
> + qinfo_cfg_rx_table.queue_base_addr_h = (u32)(dma_addr >> 32);
> + qinfo_cfg_rx_table.queue_size_bwind = (u32)size_bwid;
> + qinfo_cfg_rx_table.queue_rst = 0;
> + qinfo_cfg_rx_table.queue_en = 1;
> + nbl_hw_write_mbx_regs(hw_mgt, NBL_MAILBOX_QINFO_CFG_RX_TABLE_ADDR,
> + (u32 *)&qinfo_cfg_rx_table,
> + sizeof(qinfo_cfg_rx_table));
> +}
> +
> +static void nbl_hw_config_mailbox_txq(struct nbl_hw_mgt *hw_mgt,
> + dma_addr_t dma_addr, int size_bwid)
> +{
> + struct nbl_mailbox_qinfo_cfg_table qinfo_cfg_tx_table = { 0 };
> +
> + qinfo_cfg_tx_table.queue_rst = 1;
> + nbl_hw_write_mbx_regs(hw_mgt, NBL_MAILBOX_QINFO_CFG_TX_TABLE_ADDR,
> + (u32 *)&qinfo_cfg_tx_table,
> + sizeof(qinfo_cfg_tx_table));
> +
> + qinfo_cfg_tx_table.queue_base_addr_l = (u32)(dma_addr & 0xFFFFFFFF);
> + qinfo_cfg_tx_table.queue_base_addr_h = (u32)(dma_addr >> 32);
> + qinfo_cfg_tx_table.queue_size_bwind = (u32)size_bwid;
> + qinfo_cfg_tx_table.queue_rst = 0;
> + qinfo_cfg_tx_table.queue_en = 1;
> + nbl_hw_write_mbx_regs(hw_mgt, NBL_MAILBOX_QINFO_CFG_TX_TABLE_ADDR,
> + (u32 *)&qinfo_cfg_tx_table,
> + sizeof(qinfo_cfg_tx_table));
> +}
> +
> +static void nbl_hw_stop_mailbox_rxq(struct nbl_hw_mgt *hw_mgt)
> +{
> + struct nbl_mailbox_qinfo_cfg_table qinfo_cfg_rx_table = { 0 };
> +
> + nbl_hw_write_mbx_regs(hw_mgt, NBL_MAILBOX_QINFO_CFG_RX_TABLE_ADDR,
> + (u32 *)&qinfo_cfg_rx_table,
> + sizeof(qinfo_cfg_rx_table));
> +}
> +
> +static void nbl_hw_stop_mailbox_txq(struct nbl_hw_mgt *hw_mgt)
> +{
> + struct nbl_mailbox_qinfo_cfg_table qinfo_cfg_tx_table = { 0 };
> +
> + nbl_hw_write_mbx_regs(hw_mgt, NBL_MAILBOX_QINFO_CFG_TX_TABLE_ADDR,
> + (u32 *)&qinfo_cfg_tx_table,
> + sizeof(qinfo_cfg_tx_table));
> +}
> +
> +static u32 nbl_hw_get_host_pf_mask(struct nbl_hw_mgt *hw_mgt)
> +{
> + u32 data;
> +
> + nbl_hw_rd_regs(hw_mgt, NBL_PCIE_HOST_K_PF_MASK_REG, &data,
> + sizeof(data));
> + return data;
> +}
> +
> +static void nbl_hw_cfg_mailbox_qinfo(struct nbl_hw_mgt *hw_mgt, u16 func_id,
> + u16 bus, u16 devid, u16 function)
> +{
> + struct nbl_mailbox_qinfo_map_table mb_qinfo_map;
> +
> + memset(&mb_qinfo_map, 0, sizeof(mb_qinfo_map));
> + mb_qinfo_map.function = function;
> + mb_qinfo_map.devid = devid;
> + mb_qinfo_map.bus = bus;
> + mb_qinfo_map.msix_idx_valid = 0;
> + nbl_hw_wr_regs(hw_mgt, NBL_MAILBOX_QINFO_MAP_REG_ARR(func_id),
> + (u32 *)&mb_qinfo_map, sizeof(mb_qinfo_map));
> +}
> +
> static struct nbl_hw_ops hw_ops = {
> + .update_mailbox_queue_tail_ptr = nbl_hw_update_mailbox_queue_tail_ptr,
> + .config_mailbox_rxq = nbl_hw_config_mailbox_rxq,
> + .config_mailbox_txq = nbl_hw_config_mailbox_txq,
> + .stop_mailbox_rxq = nbl_hw_stop_mailbox_rxq,
> + .stop_mailbox_txq = nbl_hw_stop_mailbox_txq,
> + .get_host_pf_mask = nbl_hw_get_host_pf_mask,
> + .cfg_mailbox_qinfo = nbl_hw_cfg_mailbox_qinfo,
> +
> };
>
> /* Structure starts here, adding an op should not modify anything below */
> diff --git a/drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_def_channel.h b/drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_def_channel.h
> index 99e1d8455e0e..39d23160a7a4 100644
> --- a/drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_def_channel.h
> +++ b/drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_def_channel.h
> @@ -6,8 +6,42 @@
> #ifndef _NBL_DEF_CHANNEL_H_
> #define _NBL_DEF_CHANNEL_H_
>
> +#include <linux/types.h>
> +
> struct nbl_channel_mgt;
> struct nbl_adapter;
> +#define NBL_CHAN_SEND(chan_send, dst_id, mesg_type, argument, arg_length,\
> + response, resp_length, need_ack) \
> +do { \
> + typeof(chan_send) *__chan_send = &(chan_send); \
> + __chan_send->dstid = (dst_id); \
> + __chan_send->msg_type = (mesg_type); \
> + __chan_send->arg = (argument); \
> + __chan_send->arg_len = (arg_length); \
> + __chan_send->resp = (response); \
> + __chan_send->resp_len = (resp_length); \
> + __chan_send->ack = (need_ack); \
> +} while (0)
> +
> +#define NBL_CHAN_ACK(chan_ack, dst_id, mesg_type, msg_id, err_code, ack_data, \
> + data_length) \
> +do { \
> + typeof(chan_ack) *__chan_ack = &(chan_ack); \
> + __chan_ack->dstid = (dst_id); \
> + __chan_ack->msg_type = (mesg_type); \
> + __chan_ack->msgid = (msg_id); \
> + __chan_ack->err = (err_code); \
> + __chan_ack->data = (ack_data); \
> + __chan_ack->data_len = (data_length); \
> +} while (0)
> +
> +typedef void (*nbl_chan_resp)(void *, u16, u16, void *, u32);
> +
> +enum {
> + NBL_CHAN_RESP_OK,
> + NBL_CHAN_RESP_ERR,
> +};
> +
> enum nbl_chan_msg_type {
> NBL_CHAN_MSG_ACK,
> NBL_CHAN_MSG_ADD_MACVLAN,
> @@ -234,6 +268,13 @@ enum nbl_chan_msg_type {
> NBL_CHAN_MSG_MAILBOX_MAX,
> };
>
> +enum nbl_chan_state {
> + NBL_CHAN_INTERRUPT_READY,
> + NBL_CHAN_RESETTING,
> + NBL_CHAN_ABNORMAL,
> + NBL_CHAN_STATE_NBITS
> +};
> +
> struct nbl_chan_param_cfg_msix_map {
> u16 num_net_msix;
> u16 num_others_msix;
> @@ -257,12 +298,58 @@ struct nbl_chan_param_get_eth_id {
> u8 logic_eth_id;
> };
>
> +struct nbl_board_port_info {
> + u8 eth_num;
> + u8 eth_speed;
> + u8 p4_version;
> + u8 rsv[5];
> +};
> +
> +struct nbl_chan_send_info {
> + void *arg;
> + size_t arg_len;
> + void *resp;
> + size_t resp_len;
> + u16 dstid;
> + u16 msg_type;
> + u16 ack;
> + u16 ack_len;
> +};
> +
> +struct nbl_chan_ack_info {
> + void *data;
> + int err;
> + u32 data_len;
> + u16 dstid;
> + u16 msg_type;
> + u16 msgid;
> +};
> +
> enum nbl_channel_type {
> NBL_CHAN_TYPE_MAILBOX,
> NBL_CHAN_TYPE_MAX
> };
>
> struct nbl_channel_ops {
> + int (*send_msg)(struct nbl_channel_mgt *chan_mgt,
> + struct nbl_chan_send_info *chan_send);
> + int (*send_ack)(struct nbl_channel_mgt *chan_mgt,
> + struct nbl_chan_ack_info *chan_ack);
> + int (*register_msg)(struct nbl_channel_mgt *chan_mgt, u16 msg_type,
> + nbl_chan_resp func, void *callback_priv);
> + int (*cfg_chan_qinfo_map_table)(struct nbl_channel_mgt *chan_mgt,
> + u8 chan_type);
> + bool (*check_queue_exist)(struct nbl_channel_mgt *chan_mgt,
> + u8 chan_type);
> + int (*setup_queue)(struct nbl_channel_mgt *chan_mgt, u8 chan_type);
> + int (*teardown_queue)(struct nbl_channel_mgt *chan_mgt, u8 chan_type);
> + void (*clean_queue_subtask)(struct nbl_channel_mgt *chan_mgt,
> + u8 chan_type);
> + void (*register_chan_task)(struct nbl_channel_mgt *chan_mgt,
> + u8 chan_type, struct work_struct *task);
> + void (*set_queue_state)(struct nbl_channel_mgt *chan_mgt,
> + enum nbl_chan_state state, u8 chan_type,
> + u8 set);
> };
>
> struct nbl_channel_ops_tbl {
> diff --git a/drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_def_common.h b/drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_def_common.h
> index cc73d6a91f3c..5c532247c852 100644
> --- a/drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_def_common.h
> +++ b/drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_def_common.h
> @@ -11,6 +11,8 @@
> #include <linux/device.h>
> #include "nbl_include.h"
>
> +struct nbl_hash_tbl_mgt;
> +
> struct nbl_common_info {
> struct pci_dev *pdev;
> struct device *dev;
> @@ -32,4 +34,32 @@ struct nbl_common_info {
> u8 has_net;
> };
>
> +struct nbl_hash_tbl_key {
> + struct device *dev;
> + u16 key_size;
> + u16 data_size; /* no include key or node member */
> + u16 bucket_size;
> + u16 resv;
> +};
> +
> +struct nbl_hash_tbl_del_key {
> + void *action_priv;
> + void (*action_func)(void *priv, void *key, void *data);
> +};
> +
> +void nbl_common_queue_work(struct work_struct *task);
> +
> +void nbl_common_destroy_wq(void);
> +int nbl_common_create_wq(void);
> +u32 nbl_common_pf_id_subtraction_mgtpf_id(struct nbl_common_info *common,
> + u32 pf_id);
> +
> +struct nbl_hash_tbl_mgt *
> +nbl_common_init_hash_table(struct nbl_hash_tbl_key *key);
> +void nbl_common_remove_hash_table(struct nbl_hash_tbl_mgt *tbl_mgt,
> + struct nbl_hash_tbl_del_key *key);
> +int nbl_common_alloc_hash_node(struct nbl_hash_tbl_mgt *tbl_mgt, void *key,
> + void *data, void **out_data);
> +void *nbl_common_get_hash_node(struct nbl_hash_tbl_mgt *tbl_mgt, void *key);
> +
> #endif
> diff --git a/drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_def_hw.h b/drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_def_hw.h
> index 168504b30973..f97f7a810ad0 100644
> --- a/drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_def_hw.h
> +++ b/drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_def_hw.h
> @@ -6,9 +6,37 @@
> #ifndef _NBL_DEF_HW_H_
> #define _NBL_DEF_HW_H_
>
> +#include <linux/types.h>
> +
> struct nbl_hw_mgt;
> struct nbl_adapter;
> struct nbl_hw_ops {
> + void (*configure_msix_map)(struct nbl_hw_mgt *hw_mgt, u16 func_id,
> + bool valid, dma_addr_t dma_addr, u8 bus,
> + u8 devid, u8 function);
> + void (*configure_msix_info)(struct nbl_hw_mgt *hw_mgt, u16 func_id,
> + bool valid, u16 interrupt_id, u8 bus,
> + u8 devid, u8 function,
> + bool net_msix_mask_en);
> + void (*update_mailbox_queue_tail_ptr)(struct nbl_hw_mgt *hw_mgt,
> + u16 tail_ptr, u8 txrx);
> + void (*config_mailbox_rxq)(struct nbl_hw_mgt *hw_mgt,
> + dma_addr_t dma_addr, int size_bwid);
> + void (*config_mailbox_txq)(struct nbl_hw_mgt *hw_mgt,
> + dma_addr_t dma_addr, int size_bwid);
> + void (*stop_mailbox_rxq)(struct nbl_hw_mgt *hw_mgt);
> + void (*stop_mailbox_txq)(struct nbl_hw_mgt *hw_mgt);
> + u32 (*get_host_pf_mask)(struct nbl_hw_mgt *hw_mgt);
> + u32 (*get_real_bus)(struct nbl_hw_mgt *hw_mgt);
> +
> + void (*cfg_mailbox_qinfo)(struct nbl_hw_mgt *hw_mgt, u16 func_id,
> + u16 bus, u16 devid, u16 function);
> + void (*enable_mailbox_irq)(struct nbl_hw_mgt *hw_mgt, u16 func_id,
> + bool enable_msix, u16 global_vec_id);
> + u32 (*get_fw_eth_num)(struct nbl_hw_mgt *hw_mgt);
> + u32 (*get_fw_eth_map)(struct nbl_hw_mgt *hw_mgt);
> + void (*get_board_info)(struct nbl_hw_mgt *hw_mgt,
> + struct nbl_board_port_info *board);
> };
>
> struct nbl_hw_ops_tbl {
> diff --git a/drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_include.h b/drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_include.h
> index 50f30f756bf3..a01c32f57d84 100644
> --- a/drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_include.h
> +++ b/drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_include.h
> @@ -10,6 +10,12 @@
>
> /* ------ Basic definitions ------- */
> #define NBL_DRIVER_NAME "nbl"
> +#define NBL_MAX_PF 8
> +#define NBL_NEXT_ID(id, max) \
> + ({ \
> + typeof(id) _id = (id); \
> + ((_id) == (max) ? 0 : (_id) + 1); \
> + })
>
> enum nbl_product_type {
> NBL_LEONIS_TYPE,
^ permalink raw reply
* Re: [PATCH v10 net-next 04/11] net/nebula-matrix: channel msg value and msg struct
From: Mohsin Bashir @ 2026-04-02 9:03 UTC (permalink / raw)
To: illusion.wang, dimon.zhao, alvin.wang, sam.chen, netdev
Cc: andrew+netdev, corbet, kuba, linux-doc, lorenzo, pabeni, horms,
vadim.fedorenko, lukas.bulwahn, edumazet, enelsonmoore, skhan,
hkallweit1, jani.nikula, open list
In-Reply-To: <20260401022318.28550-5-illusion.wang@nebula-matrix.com>
On 3/31/26 7:23 PM, illusion.wang wrote:
> For compatibility, the msg id value is fixed, and each msg struct can
> only have fields added (not removed), new fields must be appended
>
> Signed-off-by: illusion.wang <illusion.wang@nebula-matrix.com>
> ---
> .../nbl/nbl_include/nbl_def_channel.h | 249 ++++++++++++++++++
> 1 file changed, 249 insertions(+)
>
> diff --git a/drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_def_channel.h b/drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_def_channel.h
> index ff03a53b9f5d..99e1d8455e0e 100644
> --- a/drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_def_channel.h
> +++ b/drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_def_channel.h
> @@ -8,6 +8,255 @@
>
> struct nbl_channel_mgt;
> struct nbl_adapter;
> +enum nbl_chan_msg_type {
> + NBL_CHAN_MSG_ACK,
> + NBL_CHAN_MSG_ADD_MACVLAN,
> + NBL_CHAN_MSG_DEL_MACVLAN,
> + NBL_CHAN_MSG_ADD_MULTI_RULE,
> + NBL_CHAN_MSG_DEL_MULTI_RULE,
> + NBL_CHAN_MSG_SETUP_MULTI_GROUP,
> + NBL_CHAN_MSG_REMOVE_MULTI_GROUP,
> + NBL_CHAN_MSG_REGISTER_NET,
> + NBL_CHAN_MSG_UNREGISTER_NET,
> + NBL_CHAN_MSG_ALLOC_TXRX_QUEUES,
> + NBL_CHAN_MSG_FREE_TXRX_QUEUES,
> + NBL_CHAN_MSG_SETUP_QUEUE,
> + NBL_CHAN_MSG_REMOVE_ALL_QUEUES,
> + NBL_CHAN_MSG_CFG_DSCH,
> + NBL_CHAN_MSG_SETUP_CQS,
> + NBL_CHAN_MSG_REMOVE_CQS,
> + NBL_CHAN_MSG_CFG_QDISC_MQPRIO,
> + NBL_CHAN_MSG_CONFIGURE_MSIX_MAP,
> + NBL_CHAN_MSG_DESTROY_MSIX_MAP,
> + NBL_CHAN_MSG_MAILBOX_ENABLE_IRQ,
> + NBL_CHAN_MSG_GET_GLOBAL_VECTOR,
> + NBL_CHAN_MSG_GET_VSI_ID,
> + NBL_CHAN_MSG_SET_PROSISC_MODE,
> + NBL_CHAN_MSG_GET_FIRMWARE_VERSION,
> + NBL_CHAN_MSG_GET_QUEUE_ERR_STATS,
> + NBL_CHAN_MSG_GET_COALESCE,
> + NBL_CHAN_MSG_SET_COALESCE,
> + NBL_CHAN_MSG_SET_SPOOF_CHECK_ADDR,
> + NBL_CHAN_MSG_SET_VF_SPOOF_CHECK,
> + NBL_CHAN_MSG_GET_RXFH_INDIR_SIZE,
> + NBL_CHAN_MSG_GET_RXFH_INDIR,
> + NBL_CHAN_MSG_GET_RXFH_RSS_KEY,
> + NBL_CHAN_MSG_GET_RXFH_RSS_ALG_SEL,
> + NBL_CHAN_MSG_GET_HW_CAPS,
> + NBL_CHAN_MSG_GET_HW_STATE,
> + NBL_CHAN_MSG_REGISTER_RDMA,
> + NBL_CHAN_MSG_UNREGISTER_RDMA,
> + NBL_CHAN_MSG_GET_REAL_HW_ADDR,
> + NBL_CHAN_MSG_GET_REAL_BDF,
> + NBL_CHAN_MSG_GRC_PROCESS,
> + NBL_CHAN_MSG_SET_SFP_STATE,
> + NBL_CHAN_MSG_SET_ETH_LOOPBACK,
> + NBL_CHAN_MSG_CHECK_ACTIVE_VF,
> + NBL_CHAN_MSG_GET_PRODUCT_FLEX_CAP,
> + NBL_CHAN_MSG_ALLOC_KTLS_TX_INDEX,
> + NBL_CHAN_MSG_FREE_KTLS_TX_INDEX,
> + NBL_CHAN_MSG_CFG_KTLS_TX_KEYMAT,
> + NBL_CHAN_MSG_ALLOC_KTLS_RX_INDEX,
> + NBL_CHAN_MSG_FREE_KTLS_RX_INDEX,
> + NBL_CHAN_MSG_CFG_KTLS_RX_KEYMAT,
> + NBL_CHAN_MSG_CFG_KTLS_RX_RECORD,
> + NBL_CHAN_MSG_ADD_KTLS_RX_FLOW,
> + NBL_CHAN_MSG_DEL_KTLS_RX_FLOW,
> + NBL_CHAN_MSG_ALLOC_IPSEC_TX_INDEX,
> + NBL_CHAN_MSG_FREE_IPSEC_TX_INDEX,
> + NBL_CHAN_MSG_ALLOC_IPSEC_RX_INDEX,
> + NBL_CHAN_MSG_FREE_IPSEC_RX_INDEX,
> + NBL_CHAN_MSG_CFG_IPSEC_TX_SAD,
> + NBL_CHAN_MSG_CFG_IPSEC_RX_SAD,
> + NBL_CHAN_MSG_ADD_IPSEC_TX_FLOW,
> + NBL_CHAN_MSG_DEL_IPSEC_TX_FLOW,
> + NBL_CHAN_MSG_ADD_IPSEC_RX_FLOW,
> + NBL_CHAN_MSG_DEL_IPSEC_RX_FLOW,
> + NBL_CHAN_MSG_NOTIFY_IPSEC_HARD_EXPIRE,
> + NBL_CHAN_MSG_GET_MBX_IRQ_NUM,
> + NBL_CHAN_MSG_CLEAR_FLOW,
> + NBL_CHAN_MSG_CLEAR_QUEUE,
> + NBL_CHAN_MSG_GET_ETH_ID,
> + NBL_CHAN_MSG_SET_OFFLOAD_STATUS,
> + NBL_CHAN_MSG_INIT_OFLD,
> + NBL_CHAN_MSG_INIT_CMDQ,
> + NBL_CHAN_MSG_DESTROY_CMDQ,
> + NBL_CHAN_MSG_RESET_CMDQ,
> + NBL_CHAN_MSG_INIT_FLOW,
> + NBL_CHAN_MSG_DEINIT_FLOW,
> + NBL_CHAN_MSG_OFFLOAD_FLOW_RULE,
> + NBL_CHAN_MSG_GET_ACL_SWITCH,
> + NBL_CHAN_MSG_GET_VSI_GLOBAL_QUEUE_ID,
> + NBL_CHAN_MSG_INIT_REP,
> + NBL_CHAN_MSG_GET_LINE_RATE_INFO,
> + NBL_CHAN_MSG_REGISTER_NET_REP,
> + NBL_CHAN_MSG_UNREGISTER_NET_REP,
> + NBL_CHAN_MSG_REGISTER_ETH_REP,
> + NBL_CHAN_MSG_UNREGISTER_ETH_REP,
> + NBL_CHAN_MSG_REGISTER_UPCALL_PORT,
> + NBL_CHAN_MSG_UNREGISTER_UPCALL_PORT,
> + NBL_CHAN_MSG_GET_PORT_STATE,
> + NBL_CHAN_MSG_SET_PORT_ADVERTISING,
> + NBL_CHAN_MSG_GET_MODULE_INFO,
> + NBL_CHAN_MSG_GET_MODULE_EEPROM,
> + NBL_CHAN_MSG_GET_LINK_STATE,
> + NBL_CHAN_MSG_NOTIFY_LINK_STATE,
> + NBL_CHAN_MSG_GET_QUEUE_CXT,
> + NBL_CHAN_MSG_CFG_LOG,
> + NBL_CHAN_MSG_INIT_VDPAQ,
> + NBL_CHAN_MSG_DESTROY_VDPAQ,
> + NBL_CHAN_MSG_GET_UPCALL_PORT,
> + NBL_CHAN_MSG_NOTIFY_ETH_REP_LINK_STATE,
> + NBL_CHAN_MSG_SET_ETH_MAC_ADDR,
> + NBL_CHAN_MSG_GET_FUNCTION_ID,
> + NBL_CHAN_MSG_GET_CHIP_TEMPERATURE,
> + NBL_CHAN_MSG_DISABLE_HW_FLOW,
> + NBL_CHAN_MSG_ENABLE_HW_FLOW,
> + NBL_CHAN_MSG_SET_UPCALL_RULE,
> + NBL_CHAN_MSG_UNSET_UPCALL_RULE,
> + NBL_CHAN_MSG_GET_REG_DUMP,
> + NBL_CHAN_MSG_GET_REG_DUMP_LEN,
> + NBL_CHAN_MSG_CFG_LAG_HASH_ALGORITHM,
> + NBL_CHAN_MSG_CFG_LAG_MEMBER_FWD,
> + NBL_CHAN_MSG_CFG_LAG_MEMBER_LIST,
> + NBL_CHAN_MSG_CFG_LAG_MEMBER_UP_ATTR,
> + NBL_CHAN_MSG_ADD_LAG_FLOW,
> + NBL_CHAN_MSG_DEL_LAG_FLOW,
> + NBL_CHAN_MSG_SWITCHDEV_INIT_CMDQ,
> + NBL_CHAN_MSG_SWITCHDEV_DEINIT_CMDQ,
> + NBL_CHAN_MSG_SET_TC_FLOW_INFO,
> + NBL_CHAN_MSG_UNSET_TC_FLOW_INFO,
> + NBL_CHAN_MSG_INIT_ACL,
> + NBL_CHAN_MSG_UNINIT_ACL,
> + NBL_CHAN_MSG_CFG_LAG_MCC,
> + NBL_CHAN_MSG_REGISTER_VSI2Q,
> + NBL_CHAN_MSG_SETUP_Q2VSI,
> + NBL_CHAN_MSG_REMOVE_Q2VSI,
> + NBL_CHAN_MSG_SETUP_RSS,
> + NBL_CHAN_MSG_REMOVE_RSS,
> + NBL_CHAN_MSG_GET_REP_QUEUE_INFO,
> + NBL_CHAN_MSG_CTRL_PORT_LED,
> + NBL_CHAN_MSG_NWAY_RESET,
> + NBL_CHAN_MSG_SET_INTL_SUPPRESS_LEVEL,
> + NBL_CHAN_MSG_GET_ETH_STATS,
> + NBL_CHAN_MSG_GET_MODULE_TEMPERATURE,
> + NBL_CHAN_MSG_GET_BOARD_INFO,
> + NBL_CHAN_MSG_GET_P4_USED,
> + NBL_CHAN_MSG_GET_VF_BASE_VSI_ID,
> + NBL_CHAN_MSG_ADD_LLDP_FLOW,
> + NBL_CHAN_MSG_DEL_LLDP_FLOW,
> + NBL_CHAN_MSG_CFG_ETH_BOND_INFO,
> + NBL_CHAN_MSG_CFG_DUPPKT_MCC,
> + NBL_CHAN_MSG_ADD_ND_UPCALL_FLOW,
> + NBL_CHAN_MSG_DEL_ND_UPCALL_FLOW,
> + NBL_CHAN_MSG_GET_BOARD_ID,
> + NBL_CHAN_MSG_SET_SHAPING_DPORT_VLD,
> + NBL_CHAN_MSG_SET_DPORT_FC_TH_VLD,
> + NBL_CHAN_MSG_REGISTER_RDMA_BOND,
> + NBL_CHAN_MSG_UNREGISTER_RDMA_BOND,
> + NBL_CHAN_MSG_RESTORE_NETDEV_QUEUE,
> + NBL_CHAN_MSG_RESTART_NETDEV_QUEUE,
> + NBL_CHAN_MSG_RESTORE_HW_QUEUE,
> + NBL_CHAN_MSG_KEEP_ALIVE,
> + NBL_CHAN_MSG_GET_BASE_MAC_ADDR,
> + NBL_CHAN_MSG_CFG_BOND_SHAPING,
> + NBL_CHAN_MSG_CFG_BGID_BACK_PRESSURE,
> + NBL_CHAN_MSG_ALLOC_KT_BLOCK,
> + NBL_CHAN_MSG_FREE_KT_BLOCK,
> + NBL_CHAN_MSG_GET_USER_QUEUE_INFO,
> + NBL_CHAN_MSG_GET_ETH_BOND_INFO,
> + NBL_CHAN_MSG_CLEAR_ACCEL_FLOW,
> + NBL_CHAN_MSG_SET_BRIDGE_MODE,
> + NBL_CHAN_MSG_GET_VF_FUNCTION_ID,
> + NBL_CHAN_MSG_NOTIFY_LINK_FORCED,
> + NBL_CHAN_MSG_SET_PMD_DEBUG,
> + NBL_CHAN_MSG_REGISTER_FUNC_MAC,
> + NBL_CHAN_MSG_SET_TX_RATE,
> + NBL_CHAN_MSG_REGISTER_FUNC_LINK_FORCED,
> + NBL_CHAN_MSG_GET_LINK_FORCED,
> + NBL_CHAN_MSG_REGISTER_FUNC_VLAN,
> + NBL_CHAN_MSG_GET_FD_FLOW,
> + NBL_CHAN_MSG_GET_FD_FLOW_CNT,
> + NBL_CHAN_MSG_GET_FD_FLOW_ALL,
> + NBL_CHAN_MSG_GET_FD_FLOW_MAX,
> + NBL_CHAN_MSG_REPLACE_FD_FLOW,
> + NBL_CHAN_MSG_REMOVE_FD_FLOW,
> + NBL_CHAN_MSG_CFG_FD_FLOW_STATE,
> + NBL_CHAN_MSG_REGISTER_FUNC_RATE,
> + NBL_CHAN_MSG_NOTIFY_VLAN,
> + NBL_CHAN_MSG_GET_XDP_QUEUE_INFO,
> + NBL_CHAN_MSG_STOP_ABNORMAL_SW_QUEUE,
> + NBL_CHAN_MSG_STOP_ABNORMAL_HW_QUEUE,
> + NBL_CHAN_MSG_NOTIFY_RESET_EVENT,
> + NBL_CHAN_MSG_ACK_RESET_EVENT,
> + NBL_CHAN_MSG_GET_VF_VSI_ID,
> + NBL_CHAN_MSG_CONFIGURE_QOS,
> + NBL_CHAN_MSG_GET_PFC_BUFFER_SIZE,
> + NBL_CHAN_MSG_SET_PFC_BUFFER_SIZE,
> + NBL_CHAN_MSG_GET_VF_STATS,
> + NBL_CHAN_MSG_REGISTER_FUNC_TRUST,
> + NBL_CHAN_MSG_NOTIFY_TRUST,
> + NBL_CHAN_MSG_CHECK_VF_IS_ACTIVE,
> + NBL_CHAN_MSG_GET_ETH_ABNORMAL_STATS,
> + NBL_CHAN_MSG_GET_ETH_CTRL_STATS,
> + NBL_CHAN_MSG_GET_PAUSE_STATS,
> + NBL_CHAN_MSG_GET_ETH_MAC_STATS,
> + NBL_CHAN_MSG_GET_FEC_STATS,
> + NBL_CHAN_MSG_CFG_MULTI_MCAST_RULE,
> + NBL_CHAN_MSG_GET_LINK_DOWN_COUNT,
> + NBL_CHAN_MSG_GET_LINK_STATUS_OPCODE,
> + NBL_CHAN_MSG_GET_RMON_STATS,
> + NBL_CHAN_MSG_REGISTER_PF_NAME,
> + NBL_CHAN_MSG_GET_PF_NAME,
> + NBL_CHAN_MSG_CONFIGURE_RDMA_BW,
> + NBL_CHAN_MSG_SET_RATE_LIMIT,
> + NBL_CHAN_MSG_SET_TC_WGT,
> + NBL_CHAN_MSG_REMOVE_QUEUE,
> + NBL_CHAN_MSG_GET_MIRROR_TABLE_ID,
> + NBL_CHAN_MSG_CONFIGURE_MIRROR,
> + NBL_CHAN_MSG_CONFIGURE_MIRROR_TABLE,
> + NBL_CHAN_MSG_CLEAR_MIRROR_CFG,
> + NBL_CHAN_MSG_MIRROR_OUTPUTPORT_NOTIFY,
> + NBL_CHAN_MSG_CHECK_FLOWTABLE_SPEC,
> + NBL_CHAN_MSG_CHECK_VF_IS_VDPA,
> + NBL_CHAN_MSG_GET_VDPA_VF_STATS,
> + NBL_CHAN_MSG_SET_RX_RATE,
> + NBL_CHAN_MSG_GET_UVN_PKT_DROP_STATS,
> + NBL_CHAN_MSG_GET_USTORE_PKT_DROP_STATS,
> + NBL_CHAN_MSG_GET_USTORE_TOTAL_PKT_DROP_STATS,
> + NBL_CHAN_MSG_SET_WOL,
> + NBL_CHAN_MSG_INIT_VF_MSIX_MAP,
> + NBL_CHAN_MSG_GET_ST_NAME,
> + NBL_CHAN_MSG_MTU_SET = 501,
> + NBL_CHAN_MSG_SET_RXFH_INDIR = 506,
> + NBL_CHAN_MSG_SET_RXFH_RSS_ALG_SEL = 508,
> + /* mailbox msg end */
> + NBL_CHAN_MSG_MAILBOX_MAX,
> +};
> +
This is a big enum. Is there a possibility to group them by functionality?
> +struct nbl_chan_param_cfg_msix_map {
> + u16 num_net_msix;
> + u16 num_others_msix;
> + u16 msix_mask_en;
> +};
> +
> +struct nbl_chan_param_enable_mailbox_irq {
> + u16 vector_id;
> + bool enable_msix;
> +};
> +
> +struct nbl_chan_param_get_vsi_id {
> + u16 vsi_id;
> + u16 type;
> +};
> +
> +struct nbl_chan_param_get_eth_id {
> + u16 vsi_id;
> + u8 eth_mode;
> + u8 eth_id;
> + u8 logic_eth_id;
> +};
> +
> enum nbl_channel_type {
> NBL_CHAN_TYPE_MAILBOX,
> NBL_CHAN_TYPE_MAX
^ permalink raw reply
* Re: [PATCH v10 net-next 02/11] net/nebula-matrix: add our driver architecture
From: Mohsin Bashir @ 2026-04-02 9:02 UTC (permalink / raw)
To: illusion.wang, dimon.zhao, alvin.wang, sam.chen, netdev
Cc: andrew+netdev, corbet, kuba, linux-doc, lorenzo, pabeni, horms,
vadim.fedorenko, lukas.bulwahn, edumazet, enelsonmoore, skhan,
hkallweit1, jani.nikula, open list
In-Reply-To: <20260401022318.28550-3-illusion.wang@nebula-matrix.com>
On 3/31/26 7:23 PM, illusion.wang wrote:
> our driver architecture:
> Hardware (HW), Channel, Resource, Dispatch, and Device Layer
> Struct Initialization/Deinitialization, and Operation Set Registration/
> Unregistration
>
> Our driver architecture is relatively complex because the code is highly
> reusable and designed to support multiple features. Additionally, the
> codebase supports multiple chip variants, each with distinct
> hardware-software interactions.
> To ensure compatibility, our architecture is divided into the following
> layers:
>
> 1. Dev Layer (Device Layer)
> The top-level business logic layer where all operations are
> device-centric. Every operation is performed relative to the device
> context. The intergration of base functions encompasses:
> management(ctrl only for leonis pf0), network(net_dev,this time not
> contained),common.
>
> 2. Dispatch Layer
> The distribution from services to specific data operations is mainly
> divided into two types: direct pass-through and handling by the
> management PF. It shields the upper layer from the differences in
> specific underlying locations.
> It describes the processing locations and paths of the services.
>
> 3. Resource Layer
> Handles tasks dispatched from Dispatch Layer. These tasks fall into two
> categories:
> 3.1 Hardware control
> The Resource Layer further invokes the HW Layer when hardware access is
> needed, as only the HW Layer has OS-level privileges.
> 3.2 Software resource management
> Operations like packet statistics collection that don't require hardware
> access.
>
> 4. HW Layer (Hardware Layer)
> Serves the Resource Layer by interacting with different hardware
> chipsets.Writes to hardware registers to drive the hardware based on
> Resource Layer directives.
>
> 5. Channel Layer
>
> Handle communication between PF0(has ctrl func) and other PF,and provide
> basic interaction channels.
>
> 6. Common Layer
> Provides fundamental services
>
> Signed-off-by: illusion.wang <illusion.wang@nebula-matrix.com>
> ---
> .../net/ethernet/nebula-matrix/nbl/Makefile | 7 +-
> .../nbl/nbl_channel/nbl_channel.c | 82 ++++++++
> .../nbl/nbl_channel/nbl_channel.h | 29 +++
> .../net/ethernet/nebula-matrix/nbl/nbl_core.h | 43 +++++
> .../nebula-matrix/nbl/nbl_core/nbl_dev.c | 56 ++++++
> .../nebula-matrix/nbl/nbl_core/nbl_dev.h | 27 +++
> .../nebula-matrix/nbl/nbl_core/nbl_dispatch.c | 75 ++++++++
> .../nebula-matrix/nbl/nbl_core/nbl_dispatch.h | 25 +++
> .../nbl/nbl_hw/nbl_hw_leonis/nbl_hw_leonis.c | 140 ++++++++++++++
> .../nbl/nbl_hw/nbl_hw_leonis/nbl_hw_leonis.h | 14 ++
> .../nbl_hw_leonis/nbl_resource_leonis.c | 83 ++++++++
> .../nbl_hw_leonis/nbl_resource_leonis.h | 10 +
> .../nebula-matrix/nbl/nbl_hw/nbl_hw_reg.h | 68 +++++++
> .../nebula-matrix/nbl/nbl_hw/nbl_resource.h | 30 +++
> .../nbl/nbl_include/nbl_def_channel.h | 26 +++
> .../nbl/nbl_include/nbl_def_common.h | 35 ++++
> .../nbl/nbl_include/nbl_def_dev.h | 16 ++
> .../nbl/nbl_include/nbl_def_dispatch.h | 29 +++
> .../nbl/nbl_include/nbl_def_hw.h | 22 +++
> .../nbl/nbl_include/nbl_def_resource.h | 20 ++
> .../nbl/nbl_include/nbl_include.h | 11 ++
> .../nbl/nbl_include/nbl_product_base.h | 18 ++
> .../net/ethernet/nebula-matrix/nbl/nbl_main.c | 179 ++++++++++++++++++
> 23 files changed, 1044 insertions(+), 1 deletion(-)
> create mode 100644 drivers/net/ethernet/nebula-matrix/nbl/nbl_channel/nbl_channel.c
> create mode 100644 drivers/net/ethernet/nebula-matrix/nbl/nbl_channel/nbl_channel.h
> create mode 100644 drivers/net/ethernet/nebula-matrix/nbl/nbl_core/nbl_dev.c
> create mode 100644 drivers/net/ethernet/nebula-matrix/nbl/nbl_core/nbl_dev.h
> create mode 100644 drivers/net/ethernet/nebula-matrix/nbl/nbl_core/nbl_dispatch.c
> create mode 100644 drivers/net/ethernet/nebula-matrix/nbl/nbl_core/nbl_dispatch.h
> create mode 100644 drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_hw_leonis/nbl_hw_leonis.c
> create mode 100644 drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_hw_leonis/nbl_hw_leonis.h
> create mode 100644 drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_hw_leonis/nbl_resource_leonis.c
> create mode 100644 drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_hw_leonis/nbl_resource_leonis.h
> create mode 100644 drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_hw_reg.h
> create mode 100644 drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_resource.h
> create mode 100644 drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_def_channel.h
> create mode 100644 drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_def_common.h
> create mode 100644 drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_def_dev.h
> create mode 100644 drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_def_dispatch.h
> create mode 100644 drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_def_hw.h
> create mode 100644 drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_def_resource.h
> create mode 100644 drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_product_base.h
>
> diff --git a/drivers/net/ethernet/nebula-matrix/nbl/Makefile b/drivers/net/ethernet/nebula-matrix/nbl/Makefile
> index b90fba239401..271605920396 100644
> --- a/drivers/net/ethernet/nebula-matrix/nbl/Makefile
> +++ b/drivers/net/ethernet/nebula-matrix/nbl/Makefile
> @@ -3,4 +3,9 @@
>
> obj-$(CONFIG_NBL) := nbl.o
>
> -nbl-objs += nbl_main.o
> +nbl-objs += nbl_channel/nbl_channel.o \
> + nbl_hw/nbl_hw_leonis/nbl_hw_leonis.o \
> + nbl_hw/nbl_hw_leonis/nbl_resource_leonis.o \
> + nbl_core/nbl_dispatch.o \
> + nbl_core/nbl_dev.o \
> + nbl_main.o
> diff --git a/drivers/net/ethernet/nebula-matrix/nbl/nbl_channel/nbl_channel.c b/drivers/net/ethernet/nebula-matrix/nbl/nbl_channel/nbl_channel.c
> new file mode 100644
> index 000000000000..9f4603870cb4
> --- /dev/null
> +++ b/drivers/net/ethernet/nebula-matrix/nbl/nbl_channel/nbl_channel.c
> @@ -0,0 +1,82 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (c) 2025 Nebula Matrix Limited.
> + */
> +
> +#include <linux/device.h>
> +#include <linux/pci.h>
> +#include "nbl_channel.h"
> +
> +static struct nbl_channel_ops chan_ops = {
> +};
> +
> +static struct nbl_channel_mgt *
> +nbl_chan_setup_chan_mgt(struct nbl_adapter *adapter)
> +{
> + struct nbl_hw_ops_tbl *hw_ops_tbl = adapter->intf.hw_ops_tbl;
> + struct nbl_common_info *common = &adapter->common;
> + struct device *dev = &adapter->pdev->dev;
> + struct nbl_chan_info *mailbox;
> + struct nbl_channel_mgt *chan_mgt;
Reverse xmas violation here
> +
> + chan_mgt = devm_kzalloc(dev, sizeof(*chan_mgt), GFP_KERNEL);
> + if (!chan_mgt)
> + return ERR_PTR(-ENOMEM);
> +
> + chan_mgt->common = common;
> + chan_mgt->hw_ops_tbl = hw_ops_tbl;
> +
> + mailbox = devm_kzalloc(dev, sizeof(*mailbox), GFP_KERNEL);
> + if (!mailbox)
> + return ERR_PTR(-ENOMEM);
Here, if mailbox allocation fails, we return without freeing chan_mgt
resulting in a leak.
> + mailbox->chan_type = NBL_CHAN_TYPE_MAILBOX;
> + chan_mgt->chan_info[NBL_CHAN_TYPE_MAILBOX] = mailbox;
> +
> + return chan_mgt;
> +}
> +
> +static struct nbl_channel_ops_tbl *
> +nbl_chan_setup_ops(struct device *dev, struct nbl_channel_mgt *chan_mgt)
> +{
> + struct nbl_channel_ops_tbl *chan_ops_tbl;
> +
> + chan_ops_tbl = devm_kzalloc(dev, sizeof(*chan_ops_tbl), GFP_KERNEL);
> + if (!chan_ops_tbl)
> + return ERR_PTR(-ENOMEM);
> +
> + chan_ops_tbl->ops = &chan_ops;
> + chan_ops_tbl->priv = chan_mgt;
> +
> + return chan_ops_tbl;
> +}
> +
> +int nbl_chan_init_common(struct nbl_adapter *adap)
> +{
> + struct nbl_channel_ops_tbl *chan_ops_tbl;
> + struct device *dev = &adap->pdev->dev;
> + struct nbl_channel_mgt *chan_mgt;
> + int ret;
> +
> + chan_mgt = nbl_chan_setup_chan_mgt(adap);
> + if (IS_ERR(chan_mgt)) {
> + ret = PTR_ERR(chan_mgt);
> + goto setup_mgt_fail;
> + }
> + adap->core.chan_mgt = chan_mgt;
> +
> + chan_ops_tbl = nbl_chan_setup_ops(dev, chan_mgt);
> + if (IS_ERR(chan_ops_tbl)) {
> + ret = PTR_ERR(chan_ops_tbl);
> + goto setup_ops_fail;
> + }
> + adap->intf.channel_ops_tbl = chan_ops_tbl;
> + return 0;
> +
> +setup_ops_fail:
> +setup_mgt_fail:
> + return ret;
> +}
Why do we need two labels here? Looks like some piece of code will use
two different clean-up paths in the later patches. Better to add related
label then?
> +
> +void nbl_chan_remove_common(struct nbl_adapter *adap)
> +{
> +}
> diff --git a/drivers/net/ethernet/nebula-matrix/nbl/nbl_channel/nbl_channel.h b/drivers/net/ethernet/nebula-matrix/nbl/nbl_channel/nbl_channel.h
> new file mode 100644
> index 000000000000..637912d1e806
> --- /dev/null
> +++ b/drivers/net/ethernet/nebula-matrix/nbl/nbl_channel/nbl_channel.h
> @@ -0,0 +1,29 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (c) 2025 Nebula Matrix Limited.
> + */
> +
> +#ifndef _NBL_CHANNEL_H_
> +#define _NBL_CHANNEL_H_
> +
> +#include <linux/types.h>
> +
> +#include "../nbl_include/nbl_include.h"
> +#include "../nbl_include/nbl_product_base.h"
> +#include "../nbl_include/nbl_def_channel.h"
> +#include "../nbl_include/nbl_def_hw.h"
> +#include "../nbl_include/nbl_def_common.h"
> +#include "../nbl_core.h"
> +
> +struct nbl_chan_info {
> + u8 chan_type;
> +};
> +
> +struct nbl_channel_mgt {
> + struct nbl_common_info *common;
> + struct nbl_hw_ops_tbl *hw_ops_tbl;
> + struct nbl_chan_info *chan_info[NBL_CHAN_TYPE_MAX];
> + struct nbl_hash_tbl_mgt *handle_hash_tbl;
> +};
> +
> +#endif
> diff --git a/drivers/net/ethernet/nebula-matrix/nbl/nbl_core.h b/drivers/net/ethernet/nebula-matrix/nbl/nbl_core.h
> index c525114297b4..aa4d24d04e2c 100644
> --- a/drivers/net/ethernet/nebula-matrix/nbl/nbl_core.h
> +++ b/drivers/net/ethernet/nebula-matrix/nbl/nbl_core.h
> @@ -6,6 +6,20 @@
> #ifndef _NBL_CORE_H_
> #define _NBL_CORE_H_
>
> +#include <linux/pci.h>
> +#include "nbl_include/nbl_def_common.h"
> +#include "nbl_include/nbl_include.h"
> +
> +struct nbl_hw_mgt;
> +struct nbl_hw_ops_tbl;
> +struct nbl_resource_mgt;
> +struct nbl_resource_ops_tbl;
> +struct nbl_dispatch_mgt;
> +struct nbl_dispatch_ops_tbl;
> +struct nbl_channel_ops_tbl;
> +struct nbl_channel_mgt;
> +struct nbl_dev_mgt;
> +
> enum {
> NBL_CAP_HAS_CTRL_BIT,
> NBL_CAP_HAS_NET_BIT,
> @@ -13,4 +27,33 @@ enum {
> NBL_CAP_IS_LEONIS_BIT,
> };
>
> +struct nbl_interface {
> + struct nbl_hw_ops_tbl *hw_ops_tbl;
> + struct nbl_resource_ops_tbl *resource_ops_tbl;
> + struct nbl_dispatch_ops_tbl *dispatch_ops_tbl;
> + struct nbl_channel_ops_tbl *channel_ops_tbl;
> +};
> +
> +struct nbl_core {
> + struct nbl_hw_mgt *hw_mgt;
> + struct nbl_resource_mgt *res_mgt;
> + struct nbl_dispatch_mgt *disp_mgt;
> + struct nbl_dev_mgt *dev_mgt;
> + struct nbl_channel_mgt *chan_mgt;
> +};
> +
> +struct nbl_adapter {
> + struct pci_dev *pdev;
> + struct nbl_core core;
> + struct nbl_interface intf;
> + struct nbl_common_info common;
> + struct nbl_product_base_ops *product_base_ops;
> +};
> +
> +struct nbl_adapter *nbl_core_init(struct pci_dev *pdev,
> + struct nbl_init_param *param);
> +void nbl_core_remove(struct nbl_adapter *adapter);
> +int nbl_core_start(struct nbl_adapter *adapter);
> +void nbl_core_stop(struct nbl_adapter *adapter);
> +
> #endif
> diff --git a/drivers/net/ethernet/nebula-matrix/nbl/nbl_core/nbl_dev.c b/drivers/net/ethernet/nebula-matrix/nbl/nbl_core/nbl_dev.c
> new file mode 100644
> index 000000000000..5deb21e35f8e
> --- /dev/null
> +++ b/drivers/net/ethernet/nebula-matrix/nbl/nbl_core/nbl_dev.c
> @@ -0,0 +1,56 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (c) 2025 Nebula Matrix Limited.
> + */
> +#include <linux/device.h>
> +#include <linux/pci.h>
> +#include "nbl_dev.h"
> +
> +static struct nbl_dev_mgt *nbl_dev_setup_dev_mgt(struct nbl_common_info *common)
> +{
> + struct nbl_dev_mgt *dev_mgt;
> +
> + dev_mgt = devm_kzalloc(common->dev, sizeof(*dev_mgt), GFP_KERNEL);
> + if (!dev_mgt)
> + return ERR_PTR(-ENOMEM);
> +
> + dev_mgt->common = common;
> + return dev_mgt;
> +}
> +
> +int nbl_dev_init(struct nbl_adapter *adapter)
> +{
> + struct nbl_common_info *common = &adapter->common;
> + struct nbl_dispatch_ops_tbl *disp_ops_tbl =
> + adapter->intf.dispatch_ops_tbl;
> + struct nbl_channel_ops_tbl *chan_ops_tbl =
> + adapter->intf.channel_ops_tbl;
> + struct nbl_dev_mgt *dev_mgt;
> + int ret;
> +
> + dev_mgt = nbl_dev_setup_dev_mgt(common);
> + if (IS_ERR(dev_mgt)) {
> + ret = PTR_ERR(dev_mgt);
> + return ret;
> + }
> +
> + dev_mgt->disp_ops_tbl = disp_ops_tbl;
> + dev_mgt->chan_ops_tbl = chan_ops_tbl;
> + adapter->core.dev_mgt = dev_mgt;
> +
> + return 0;
> +}
> +
> +void nbl_dev_remove(struct nbl_adapter *adapter)
> +{
> +}
> +
> +/* ---------- Dev start process ---------- */
> +int nbl_dev_start(struct nbl_adapter *adapter)
> +{
> + return 0;
> +}
> +
> +void nbl_dev_stop(struct nbl_adapter *adapter)
> +{
> +}
> diff --git a/drivers/net/ethernet/nebula-matrix/nbl/nbl_core/nbl_dev.h b/drivers/net/ethernet/nebula-matrix/nbl/nbl_core/nbl_dev.h
> new file mode 100644
> index 000000000000..9b71092b99a0
> --- /dev/null
> +++ b/drivers/net/ethernet/nebula-matrix/nbl/nbl_core/nbl_dev.h
> @@ -0,0 +1,27 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (c) 2025 Nebula Matrix Limited.
> + */
> +
> +#ifndef _NBL_DEV_H_
> +#define _NBL_DEV_H_
> +
> +#include <linux/types.h>
> +
> +#include "../nbl_include/nbl_include.h"
> +#include "../nbl_include/nbl_product_base.h"
> +#include "../nbl_include/nbl_def_channel.h"
> +#include "../nbl_include/nbl_def_hw.h"
> +#include "../nbl_include/nbl_def_resource.h"
> +#include "../nbl_include/nbl_def_dispatch.h"
> +#include "../nbl_include/nbl_def_dev.h"
> +#include "../nbl_include/nbl_def_common.h"
> +#include "../nbl_core.h"
> +
> +struct nbl_dev_mgt {
> + struct nbl_common_info *common;
> + struct nbl_dispatch_ops_tbl *disp_ops_tbl;
> + struct nbl_channel_ops_tbl *chan_ops_tbl;
> +};
> +
> +#endif
> diff --git a/drivers/net/ethernet/nebula-matrix/nbl/nbl_core/nbl_dispatch.c b/drivers/net/ethernet/nebula-matrix/nbl/nbl_core/nbl_dispatch.c
> new file mode 100644
> index 000000000000..8b031f6e963f
> --- /dev/null
> +++ b/drivers/net/ethernet/nebula-matrix/nbl/nbl_core/nbl_dispatch.c
> @@ -0,0 +1,75 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (c) 2025 Nebula Matrix Limited.
> + */
> +#include <linux/device.h>
> +#include <linux/pci.h>
> +#include "nbl_dispatch.h"
> +
> +static struct nbl_dispatch_mgt *
> +nbl_disp_setup_disp_mgt(struct nbl_common_info *common)
> +{
> + struct nbl_dispatch_mgt *disp_mgt;
> + struct device *dev = common->dev;
> +
> + disp_mgt = devm_kzalloc(dev, sizeof(*disp_mgt), GFP_KERNEL);
> + if (!disp_mgt)
> + return ERR_PTR(-ENOMEM);
> +
> + disp_mgt->common = common;
> + return disp_mgt;
> +}
> +
> +static struct nbl_dispatch_ops_tbl *
> +nbl_disp_setup_ops(struct device *dev, struct nbl_dispatch_mgt *disp_mgt)
> +{
> + struct nbl_dispatch_ops_tbl *disp_ops_tbl;
> + struct nbl_dispatch_ops *disp_ops;
> +
> + disp_ops_tbl = devm_kzalloc(dev, sizeof(*disp_ops_tbl), GFP_KERNEL);
> + if (!disp_ops_tbl)
> + return ERR_PTR(-ENOMEM);
> +
> + disp_ops =
> + devm_kzalloc(dev, sizeof(*disp_ops), GFP_KERNEL);
> + if (!disp_ops)
> + return ERR_PTR(-ENOMEM);
> +
> + disp_ops_tbl->ops = disp_ops;
> + disp_ops_tbl->priv = disp_mgt;
> +
> + return disp_ops_tbl;
> +}
> +
> +int nbl_disp_init(struct nbl_adapter *adapter)
> +{
> + struct nbl_common_info *common = &adapter->common;
> + struct nbl_dispatch_ops_tbl *disp_ops_tbl;
> + struct nbl_resource_ops_tbl *res_ops_tbl =
> + adapter->intf.resource_ops_tbl;
> + struct nbl_channel_ops_tbl *chan_ops_tbl =
> + adapter->intf.channel_ops_tbl;
> + struct device *dev = &adapter->pdev->dev;
> + struct nbl_dispatch_mgt *disp_mgt;
> + int ret;
> +
> + disp_mgt = nbl_disp_setup_disp_mgt(common);
> + if (IS_ERR(disp_mgt)) {
> + ret = PTR_ERR(disp_mgt);
> + return ret;
> + }
> +
> + disp_ops_tbl = nbl_disp_setup_ops(dev, disp_mgt);
> + if (IS_ERR(disp_ops_tbl)) {
> + ret = PTR_ERR(disp_ops_tbl);
> + return ret;
> + }
> +
> + disp_mgt->res_ops_tbl = res_ops_tbl;
> + disp_mgt->chan_ops_tbl = chan_ops_tbl;
> + disp_mgt->disp_ops_tbl = disp_ops_tbl;
> + adapter->core.disp_mgt = disp_mgt;
> + adapter->intf.dispatch_ops_tbl = disp_ops_tbl;
> +
> + return 0;
> +}
> diff --git a/drivers/net/ethernet/nebula-matrix/nbl/nbl_core/nbl_dispatch.h b/drivers/net/ethernet/nebula-matrix/nbl/nbl_core/nbl_dispatch.h
> new file mode 100644
> index 000000000000..fa7f4597febe
> --- /dev/null
> +++ b/drivers/net/ethernet/nebula-matrix/nbl/nbl_core/nbl_dispatch.h
> @@ -0,0 +1,25 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (c) 2025 Nebula Matrix Limited.
> + */
> +
> +#ifndef _NBL_DISPATCH_H_
> +#define _NBL_DISPATCH_H_
> +#include "../nbl_include/nbl_include.h"
> +#include "../nbl_include/nbl_product_base.h"
> +#include "../nbl_include/nbl_def_channel.h"
> +#include "../nbl_include/nbl_def_hw.h"
> +#include "../nbl_include/nbl_def_resource.h"
> +#include "../nbl_include/nbl_def_dispatch.h"
> +#include "../nbl_include/nbl_def_common.h"
> +#include "../nbl_core.h"
> +
> +struct nbl_dispatch_mgt {
> + struct nbl_common_info *common;
> + struct nbl_resource_ops_tbl *res_ops_tbl;
> + struct nbl_channel_ops_tbl *chan_ops_tbl;
> + struct nbl_dispatch_ops_tbl *disp_ops_tbl;
> + DECLARE_BITMAP(ctrl_lvl, NBL_DISP_CTRL_LVL_MAX);
> +};
> +
> +#endif
> diff --git a/drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_hw_leonis/nbl_hw_leonis.c b/drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_hw_leonis/nbl_hw_leonis.c
> new file mode 100644
> index 000000000000..6dd8d3a087dd
> --- /dev/null
> +++ b/drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_hw_leonis/nbl_hw_leonis.c
> @@ -0,0 +1,140 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (c) 2025 Nebula Matrix Limited.
> + */
> +#include <linux/device.h>
> +#include <linux/pci.h>
> +#include <linux/bits.h>
> +#include <linux/io.h>
> +#include <linux/spinlock.h>
> +#include "nbl_hw_leonis.h"
> +
> +static struct nbl_hw_ops hw_ops = {
> +};
> +
> +/* Structure starts here, adding an op should not modify anything below */
> +static struct nbl_hw_mgt *nbl_hw_setup_hw_mgt(struct nbl_common_info *common)
> +{
> + struct device *dev = common->dev;
> + struct nbl_hw_mgt *hw_mgt;
> +
> + hw_mgt = devm_kzalloc(dev, sizeof(*hw_mgt), GFP_KERNEL);
> + if (!hw_mgt)
> + return ERR_PTR(-ENOMEM);
> +
> + hw_mgt->common = common;
> +
> + return hw_mgt;
> +}
> +
> +static struct nbl_hw_ops_tbl *nbl_hw_setup_ops(struct nbl_common_info *common,
> + struct nbl_hw_mgt *hw_mgt)
> +{
> + struct nbl_hw_ops_tbl *hw_ops_tbl;
> + struct device *dev;
> +
> + dev = common->dev;
> + hw_ops_tbl =
> + devm_kzalloc(dev, sizeof(*hw_ops_tbl), GFP_KERNEL);
> + if (!hw_ops_tbl)
> + return ERR_PTR(-ENOMEM);
> +
> + hw_ops_tbl->ops = &hw_ops;
> + hw_ops_tbl->priv = hw_mgt;
> +
> + return hw_ops_tbl;
> +}
> +
> +int nbl_hw_init_leonis(struct nbl_adapter *adapter)
> +{
> + struct nbl_common_info *common = &adapter->common;
> + struct pci_dev *pdev = common->pdev;
> + struct nbl_hw_ops_tbl *hw_ops_tbl;
> + struct nbl_hw_mgt *hw_mgt;
> + int bar_mask;
> + int ret;
> +
> + hw_mgt = nbl_hw_setup_hw_mgt(common);
> + if (IS_ERR(hw_mgt)) {
> + ret = PTR_ERR(hw_mgt);
> + goto setup_mgt_fail;
> + }
> + bar_mask = BIT(NBL_MEMORY_BAR) | BIT(NBL_MAILBOX_BAR);
> + ret = pci_request_selected_regions(pdev, bar_mask, NBL_DRIVER_NAME);
> + if (ret) {
> + dev_err(&pdev->dev,
> + "Request memory bar and mailbox bar failed, err = %d\n",
> + ret);
> + goto request_bar_region_fail;
> + }
> +
> + if (common->has_ctrl) {
> + hw_mgt->hw_addr =
> + ioremap(pci_resource_start(pdev, NBL_MEMORY_BAR),
> + pci_resource_len(pdev, NBL_MEMORY_BAR) -
> + NBL_RDMA_NOTIFY_OFF);
> + if (!hw_mgt->hw_addr) {
> + dev_err(&pdev->dev, "Memory bar ioremap failed\n");
> + ret = -EIO;
> + goto ioremap_err;
> + }
> + hw_mgt->hw_size = pci_resource_len(pdev, NBL_MEMORY_BAR) -
> + NBL_RDMA_NOTIFY_OFF;
> + } else {
> + hw_mgt->hw_addr =
> + ioremap(pci_resource_start(pdev, NBL_MEMORY_BAR),
> + NBL_RDMA_NOTIFY_OFF);
> + if (!hw_mgt->hw_addr) {
> + dev_err(&pdev->dev, "Memory bar ioremap failed\n");
> + ret = -EIO;
> + goto ioremap_err;
> + }
> + hw_mgt->hw_size = NBL_RDMA_NOTIFY_OFF;
> + }
> +
> + hw_mgt->notify_offset = 0;
> + hw_mgt->mailbox_bar_hw_addr = pci_ioremap_bar(pdev, NBL_MAILBOX_BAR);
> + if (!hw_mgt->mailbox_bar_hw_addr) {
> + dev_err(&pdev->dev, "Mailbox bar ioremap failed\n");
> + ret = -EIO;
> + goto mailbox_ioremap_err;
> + }
> +
> + spin_lock_init(&hw_mgt->reg_lock);
> + adapter->core.hw_mgt = hw_mgt;
> +
> + hw_ops_tbl = nbl_hw_setup_ops(common, hw_mgt);
> + if (IS_ERR(hw_ops_tbl)) {
> + ret = PTR_ERR(hw_ops_tbl);
> + goto setup_ops_fail;
> + }
> + adapter->intf.hw_ops_tbl = hw_ops_tbl;
> +
> + return 0;
> +
> +setup_ops_fail:
> + iounmap(hw_mgt->mailbox_bar_hw_addr);
> +mailbox_ioremap_err:
> + iounmap(hw_mgt->hw_addr);
> +ioremap_err:
> + pci_release_selected_regions(pdev, bar_mask);
> +request_bar_region_fail:
> +setup_mgt_fail:
> + return ret;
> +}
> +
> +void nbl_hw_remove_leonis(struct nbl_adapter *adapter)
> +{
> + int bar_mask = BIT(NBL_MEMORY_BAR) | BIT(NBL_MAILBOX_BAR);
> + struct nbl_common_info *common = &adapter->common;
> + struct nbl_hw_mgt *hw_mgt = adapter->core.hw_mgt;
> + u8 __iomem *hw_addr = hw_mgt->hw_addr;
> + struct pci_dev *pdev = common->pdev;
> + u8 __iomem *mailbox_bar_hw_addr;
> +
> + mailbox_bar_hw_addr = hw_mgt->mailbox_bar_hw_addr;
> +
> + iounmap(mailbox_bar_hw_addr);
> + iounmap(hw_addr);
> + pci_release_selected_regions(pdev, bar_mask);
> +}
> diff --git a/drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_hw_leonis/nbl_hw_leonis.h b/drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_hw_leonis/nbl_hw_leonis.h
> new file mode 100644
> index 000000000000..77c67b67ba31
> --- /dev/null
> +++ b/drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_hw_leonis/nbl_hw_leonis.h
> @@ -0,0 +1,14 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (c) 2025 Nebula Matrix Limited.
> + */
> +
> +#ifndef _NBL_HW_LEONIS_H_
> +#define _NBL_HW_LEONIS_H_
> +
> +#include <linux/types.h>
> +
> +#include "../../nbl_include/nbl_include.h"
> +#include "../nbl_hw_reg.h"
> +
> +#endif
> diff --git a/drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_hw_leonis/nbl_resource_leonis.c b/drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_hw_leonis/nbl_resource_leonis.c
> new file mode 100644
> index 000000000000..355f7ea66b88
> --- /dev/null
> +++ b/drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_hw_leonis/nbl_resource_leonis.c
> @@ -0,0 +1,83 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (c) 2025 Nebula Matrix Limited.
> + */
> +#include <linux/device.h>
> +#include <linux/pci.h>
> +#include "nbl_resource_leonis.h"
> +
> +static struct nbl_resource_ops res_ops = {
> +};
> +
> +static struct nbl_resource_mgt *
> +nbl_res_setup_res_mgt(struct nbl_common_info *common)
> +{
> + struct nbl_resource_info *resource_info;
> + struct nbl_resource_mgt *res_mgt;
> + struct device *dev = common->dev;
> +
> + res_mgt = devm_kzalloc(dev, sizeof(*res_mgt), GFP_KERNEL);
> + if (!res_mgt)
> + return ERR_PTR(-ENOMEM);
> + res_mgt->common = common;
> +
> + resource_info =
> + devm_kzalloc(dev, sizeof(*resource_info), GFP_KERNEL);
> + if (!resource_info)
> + return ERR_PTR(-ENOMEM);
> + res_mgt->resource_info = resource_info;
> +
> + return res_mgt;
> +}
> +
> +static struct nbl_resource_ops_tbl *
> +nbl_res_setup_ops(struct device *dev, struct nbl_resource_mgt *res_mgt)
> +{
> + struct nbl_resource_ops_tbl *res_ops_tbl;
> +
> + res_ops_tbl = devm_kzalloc(dev, sizeof(*res_ops_tbl), GFP_KERNEL);
> + if (!res_ops_tbl)
> + return ERR_PTR(-ENOMEM);
> +
> + res_ops_tbl->ops = &res_ops;
> + res_ops_tbl->priv = res_mgt;
> +
> + return res_ops_tbl;
> +}
> +
> +static int nbl_res_start(struct nbl_resource_mgt *res_mgt)
> +{
> + return 0;
> +}
> +
> +int nbl_res_init_leonis(struct nbl_adapter *adap)
> +{
> + struct nbl_channel_ops_tbl *chan_ops_tbl = adap->intf.channel_ops_tbl;
> + struct nbl_hw_ops_tbl *hw_ops_tbl = adap->intf.hw_ops_tbl;
> + struct nbl_common_info *common = &adap->common;
> + struct nbl_resource_ops_tbl *res_ops_tbl;
> + struct device *dev = &adap->pdev->dev;
> + struct nbl_resource_mgt *res_mgt;
> + int ret;
> +
> + res_mgt = nbl_res_setup_res_mgt(common);
> + if (IS_ERR(res_mgt)) {
> + ret = PTR_ERR(res_mgt);
> + return ret;
> + }
> + res_mgt->chan_ops_tbl = chan_ops_tbl;
> + res_mgt->hw_ops_tbl = hw_ops_tbl;
> +
> + ret = nbl_res_start(res_mgt);
> + if (ret)
> + return ret;
> + adap->core.res_mgt = res_mgt;
> +
> + res_ops_tbl = nbl_res_setup_ops(dev, res_mgt);
> + if (IS_ERR(res_ops_tbl)) {
> + ret = PTR_ERR(res_ops_tbl);
> + return ret;
> + }
> + adap->intf.resource_ops_tbl = res_ops_tbl;
> + return 0;
> +}
> diff --git a/drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_hw_leonis/nbl_resource_leonis.h b/drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_hw_leonis/nbl_resource_leonis.h
> new file mode 100644
> index 000000000000..4e61a5c141e5
> --- /dev/null
> +++ b/drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_hw_leonis/nbl_resource_leonis.h
> @@ -0,0 +1,10 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (c) 2025 Nebula Matrix Limited.
> + */
> +
> +#ifndef _NBL_RESOURCE_LEONIS_H_
> +#define _NBL_RESOURCE_LEONIS_H_
> +
> +#include "../nbl_resource.h"
> +#endif
> diff --git a/drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_hw_reg.h b/drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_hw_reg.h
> new file mode 100644
> index 000000000000..46e58b4e73dc
> --- /dev/null
> +++ b/drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_hw_reg.h
> @@ -0,0 +1,68 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (c) 2025 Nebula Matrix Limited.
> + */
> +
> +#ifndef _NBL_HW_REG_H_
> +#define _NBL_HW_REG_H_
> +
> +#include <linux/types.h>
> +
> +#include "../nbl_include/nbl_product_base.h"
> +#include "../nbl_include/nbl_def_channel.h"
> +#include "../nbl_include/nbl_def_hw.h"
> +#include "../nbl_include/nbl_def_common.h"
> +#include "../nbl_core.h"
> +
> +#define NBL_MEMORY_BAR 0
> +#define NBL_MAILBOX_BAR 2
> +#define NBL_RDMA_NOTIFY_OFF 8192
> +#define NBL_HW_DUMMY_REG 0x1300904
> +
> +struct nbl_hw_mgt {
> + struct nbl_common_info *common;
> + u8 __iomem *hw_addr;
> + u8 __iomem *mailbox_bar_hw_addr;
> + u64 notify_offset;
> + u32 version;
> + u32 hw_size;
> + spinlock_t reg_lock; /* Protect reg access */
> +};
> +
> +static inline u32 rd32(u8 __iomem *addr, u64 reg)
> +{
> + return readl(addr + reg);
> +}
> +
> +static inline void wr32(u8 __iomem *addr, u64 reg, u32 value)
> +{
> + writel(value, addr + reg);
> +}
> +
> +static inline void nbl_hw_wr32(struct nbl_hw_mgt *hw_mgt, u64 reg, u32 value)
> +{
> + /* Used for emu, make sure that we won't write too frequently */
> + wr32(hw_mgt->hw_addr, reg, value);
> +}
> +
> +static inline u32 nbl_hw_rd32(struct nbl_hw_mgt *hw_mgt, u64 reg)
> +{
> + return rd32(hw_mgt->hw_addr, reg);
> +}
> +
> +static inline void nbl_mbx_wr32(struct nbl_hw_mgt *hw_mgt, u64 reg, u32 value)
> +{
> + writel(value, hw_mgt->mailbox_bar_hw_addr + reg);
> +}
> +
> +static inline void nbl_flush_writes(struct nbl_hw_mgt *hw_mgt)
> +{
> + nbl_hw_rd32(hw_mgt, NBL_HW_DUMMY_REG);
> +}
> +
> +static inline u32 nbl_mbx_rd32(struct nbl_hw_mgt *hw_mgt, u64 reg)
> +{
> + return readl(hw_mgt->mailbox_bar_hw_addr + reg);
> +}
> +
> +#endif
> diff --git a/drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_resource.h b/drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_resource.h
> new file mode 100644
> index 000000000000..e08b6237da32
> --- /dev/null
> +++ b/drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_resource.h
> @@ -0,0 +1,30 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (c) 2025 Nebula Matrix Limited.
> + */
> +
> +#ifndef _NBL_RESOURCE_H_
> +#define _NBL_RESOURCE_H_
> +
> +#include <linux/types.h>
> +
> +#include "../nbl_include/nbl_include.h"
> +#include "../nbl_include/nbl_product_base.h"
> +#include "../nbl_include/nbl_def_channel.h"
> +#include "../nbl_include/nbl_def_hw.h"
> +#include "../nbl_include/nbl_def_resource.h"
> +#include "../nbl_include/nbl_def_common.h"
> +#include "../nbl_core.h"
> +
> +struct nbl_resource_info {
> +};
> +
> +struct nbl_resource_mgt {
> + struct nbl_common_info *common;
> + struct nbl_resource_info *resource_info;
> + struct nbl_channel_ops_tbl *chan_ops_tbl;
> + struct nbl_hw_ops_tbl *hw_ops_tbl;
> + struct nbl_interrupt_mgt *intr_mgt;
> +};
> +
> +#endif
> diff --git a/drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_def_channel.h b/drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_def_channel.h
> new file mode 100644
> index 000000000000..ff03a53b9f5d
> --- /dev/null
> +++ b/drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_def_channel.h
> @@ -0,0 +1,26 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (c) 2025 Nebula Matrix Limited.
> + */
> +
> +#ifndef _NBL_DEF_CHANNEL_H_
> +#define _NBL_DEF_CHANNEL_H_
> +
> +struct nbl_channel_mgt;
> +struct nbl_adapter;
> +enum nbl_channel_type {
> + NBL_CHAN_TYPE_MAILBOX,
> + NBL_CHAN_TYPE_MAX
> +};
> +
> +struct nbl_channel_ops {
> +};
> +
> +struct nbl_channel_ops_tbl {
> + struct nbl_channel_ops *ops;
> + struct nbl_channel_mgt *priv;
> +};
> +
> +int nbl_chan_init_common(struct nbl_adapter *adapter);
> +void nbl_chan_remove_common(struct nbl_adapter *adapter);
> +#endif
> diff --git a/drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_def_common.h b/drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_def_common.h
> new file mode 100644
> index 000000000000..cc73d6a91f3c
> --- /dev/null
> +++ b/drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_def_common.h
> @@ -0,0 +1,35 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (c) 2025 Nebula Matrix Limited.
> + */
> +
> +#ifndef _NBL_DEF_COMMON_H_
> +#define _NBL_DEF_COMMON_H_
> +
> +#include <linux/types.h>
> +#include <linux/pci.h>
> +#include <linux/device.h>
> +#include "nbl_include.h"
> +
> +struct nbl_common_info {
> + struct pci_dev *pdev;
> + struct device *dev;
> + u32 msg_enable;
> + u16 vsi_id;
> + u8 eth_id;
> + u8 logic_eth_id;
> + u8 eth_mode;
> +
> + u8 function;
> + u8 devid;
> + u8 bus;
> + u8 hw_bus;
> + u16 mgt_pf;
> +
> + bool pci_using_dac;
> + enum nbl_product_type product_type;
> + u8 has_ctrl;
> + u8 has_net;
> +};
> +
> +#endif
> diff --git a/drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_def_dev.h b/drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_def_dev.h
> new file mode 100644
> index 000000000000..32e6cce38d39
> --- /dev/null
> +++ b/drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_def_dev.h
> @@ -0,0 +1,16 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (c) 2025 Nebula Matrix Limited.
> + */
> +
> +#ifndef _NBL_DEF_DEV_H_
> +#define _NBL_DEF_DEV_H_
> +
> +struct nbl_adapter;
> +
> +int nbl_dev_init(struct nbl_adapter *adapter);
> +void nbl_dev_remove(struct nbl_adapter *adapter);
> +int nbl_dev_start(struct nbl_adapter *adapter);
> +void nbl_dev_stop(struct nbl_adapter *adapter);
> +
> +#endif
> diff --git a/drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_def_dispatch.h b/drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_def_dispatch.h
> new file mode 100644
> index 000000000000..2230c728079e
> --- /dev/null
> +++ b/drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_def_dispatch.h
> @@ -0,0 +1,29 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (c) 2025 Nebula Matrix Limited.
> + */
> +
> +#ifndef _NBL_DEF_DISPATCH_H_
> +#define _NBL_DEF_DISPATCH_H_
> +
> +struct nbl_dispatch_mgt;
> +struct nbl_adapter;
> +enum {
> + NBL_DISP_CTRL_LVL_NEVER = 0,
> + NBL_DISP_CTRL_LVL_MGT,
> + NBL_DISP_CTRL_LVL_NET,
> + NBL_DISP_CTRL_LVL_ALWAYS,
> + NBL_DISP_CTRL_LVL_MAX,
> +};
> +
> +struct nbl_dispatch_ops {
> +};
> +
> +struct nbl_dispatch_ops_tbl {
> + struct nbl_dispatch_ops *ops;
> + struct nbl_dispatch_mgt *priv;
> +};
> +
> +int nbl_disp_init(struct nbl_adapter *adapter);
> +
> +#endif
> diff --git a/drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_def_hw.h b/drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_def_hw.h
> new file mode 100644
> index 000000000000..168504b30973
> --- /dev/null
> +++ b/drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_def_hw.h
> @@ -0,0 +1,22 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (c) 2025 Nebula Matrix Limited.
> + */
> +
> +#ifndef _NBL_DEF_HW_H_
> +#define _NBL_DEF_HW_H_
> +
> +struct nbl_hw_mgt;
> +struct nbl_adapter;
> +struct nbl_hw_ops {
> +};
> +
> +struct nbl_hw_ops_tbl {
> + struct nbl_hw_ops *ops;
> + struct nbl_hw_mgt *priv;
> +};
> +
> +int nbl_hw_init_leonis(struct nbl_adapter *adapter);
> +void nbl_hw_remove_leonis(struct nbl_adapter *adapter);
> +
> +#endif
> diff --git a/drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_def_resource.h b/drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_def_resource.h
> new file mode 100644
> index 000000000000..16add7e25eae
> --- /dev/null
> +++ b/drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_def_resource.h
> @@ -0,0 +1,20 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (c) 2025 Nebula Matrix Limited.
> + */
> +
> +#ifndef _NBL_DEF_RESOURCE_H_
> +#define _NBL_DEF_RESOURCE_H_
> +
> +struct nbl_resource_mgt;
> +struct nbl_adapter;
> +struct nbl_resource_ops {
> +};
> +
> +struct nbl_resource_ops_tbl {
> + struct nbl_resource_ops *ops;
> + struct nbl_resource_mgt *priv;
> +};
> +
> +int nbl_res_init_leonis(struct nbl_adapter *adapter);
> +#endif
> diff --git a/drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_include.h b/drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_include.h
> index 1046e6517b15..50f30f756bf3 100644
> --- a/drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_include.h
> +++ b/drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_include.h
> @@ -11,6 +11,11 @@
> /* ------ Basic definitions ------- */
> #define NBL_DRIVER_NAME "nbl"
>
> +enum nbl_product_type {
> + NBL_LEONIS_TYPE,
> + NBL_PRODUCT_MAX,
> +};
> +
> struct nbl_func_caps {
> u32 has_ctrl:1;
> u32 has_net:1;
> @@ -18,4 +23,10 @@ struct nbl_func_caps {
> u32 rsv:29;
> };
>
> +struct nbl_init_param {
> + struct nbl_func_caps caps;
> + enum nbl_product_type product_type;
> + bool pci_using_dac;
> +};
> +
> #endif
> diff --git a/drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_product_base.h b/drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_product_base.h
> new file mode 100644
> index 000000000000..4368d4f57a03
> --- /dev/null
> +++ b/drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_product_base.h
> @@ -0,0 +1,18 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (c) 2025 Nebula Matrix Limited.
> + */
> +
> +#ifndef _NBL_DEF_PRODUCT_BASE_H_
> +#define _NBL_DEF_PRODUCT_BASE_H_
> +
> +struct nbl_adapter;
> +struct nbl_product_base_ops {
> + int (*hw_init)(struct nbl_adapter *p);
> + void (*hw_remove)(struct nbl_adapter *p);
> + int (*res_init)(struct nbl_adapter *p);
> + int (*chan_init)(struct nbl_adapter *p);
> + void (*chan_remove)(struct nbl_adapter *p);
> +};
> +
> +#endif
> diff --git a/drivers/net/ethernet/nebula-matrix/nbl/nbl_main.c b/drivers/net/ethernet/nebula-matrix/nbl/nbl_main.c
> index 10c3536b327b..db035136e09c 100644
> --- a/drivers/net/ethernet/nebula-matrix/nbl/nbl_main.c
> +++ b/drivers/net/ethernet/nebula-matrix/nbl/nbl_main.c
> @@ -6,17 +6,196 @@
> #include <linux/device.h>
> #include <linux/pci.h>
> #include <linux/module.h>
> +#include <linux/bits.h>
> #include "nbl_include/nbl_include.h"
> +#include "nbl_include/nbl_product_base.h"
> +#include "nbl_include/nbl_def_channel.h"
> +#include "nbl_include/nbl_def_hw.h"
> +#include "nbl_include/nbl_def_resource.h"
> +#include "nbl_include/nbl_def_dispatch.h"
> +#include "nbl_include/nbl_def_dev.h"
> +#include "nbl_include/nbl_def_common.h"
> #include "nbl_core.h"
>
> +static struct nbl_product_base_ops nbl_product_base_ops[NBL_PRODUCT_MAX] = {
> + {
> + .hw_init = nbl_hw_init_leonis,
> + .hw_remove = nbl_hw_remove_leonis,
> + .res_init = nbl_res_init_leonis,
> + .chan_init = nbl_chan_init_common,
> + .chan_remove = nbl_chan_remove_common,
> + },
> +};
> +
> +int nbl_core_start(struct nbl_adapter *adapter)
> +{
> + return nbl_dev_start(adapter);
> +}
> +
> +void nbl_core_stop(struct nbl_adapter *adapter)
> +{
> + nbl_dev_stop(adapter);
> +}
> +
> +static struct nbl_product_base_ops *
> +nbl_core_setup_product_ops(struct nbl_adapter *adapter,
> + struct nbl_init_param *param)
> +{
> + adapter->product_base_ops = &nbl_product_base_ops[param->product_type];
> + return adapter->product_base_ops;
> +}
> +
> +struct nbl_adapter *nbl_core_init(struct pci_dev *pdev,
> + struct nbl_init_param *param)
> +{
> + struct nbl_product_base_ops *product_base_ops;
> + struct nbl_common_info *common;
> + struct nbl_adapter *adapter;
> + int ret;
> +
> + adapter = devm_kzalloc(&pdev->dev, sizeof(*adapter), GFP_KERNEL);
> + if (!adapter)
> + return NULL;
> +
> + adapter->pdev = pdev;
> + common = &adapter->common;
> +
> + common->pdev = pdev;
> + common->dev = &pdev->dev;
> + common->has_ctrl = param->caps.has_ctrl;
> + common->has_net = param->caps.has_net;
> + common->pci_using_dac = param->pci_using_dac;
> + common->function = PCI_FUNC(pdev->devfn);
> + common->devid = PCI_SLOT(pdev->devfn);
> + common->bus = pdev->bus->number;
> + common->product_type = param->product_type;
> +
> + product_base_ops = nbl_core_setup_product_ops(adapter, param);
> +
> + /*
> + *every product's hw/chan/res layer has a great difference,
> + *so call their own init ops
> + */
> + ret = product_base_ops->hw_init(adapter);
> + if (ret)
> + goto hw_init_fail;
> +
> + ret = product_base_ops->chan_init(adapter);
> + if (ret)
> + goto chan_init_fail;
> +
> + ret = product_base_ops->res_init(adapter);
> + if (ret)
> + goto res_init_fail;
> +
> + ret = nbl_disp_init(adapter);
> + if (ret)
> + goto res_init_fail;
> +
> + ret = nbl_dev_init(adapter);
> + if (ret)
> + goto res_init_fail;
> + return adapter;
> +
All the potential failures after res_init() do not need any other cleanup?
> +res_init_fail:
> + product_base_ops->chan_remove(adapter);
> +chan_init_fail:
> + product_base_ops->hw_remove(adapter);
> +hw_init_fail:
> + return NULL;
> +}
> +
> +void nbl_core_remove(struct nbl_adapter *adapter)
> +{
> + struct nbl_product_base_ops *product_base_ops;
> +
> + product_base_ops = adapter->product_base_ops;
> + nbl_dev_remove(adapter);
> + product_base_ops->chan_remove(adapter);
> + product_base_ops->hw_remove(adapter);
> +}
> +
> +static void nbl_get_func_param(struct pci_dev *pdev, kernel_ulong_t driver_data,
> + struct nbl_init_param *param)
> +{
> + param->caps.has_ctrl = !!(driver_data & BIT(NBL_CAP_HAS_CTRL_BIT));
> + param->caps.has_net = !!(driver_data & BIT(NBL_CAP_HAS_NET_BIT));
> + param->caps.is_nic = !!(driver_data & BIT(NBL_CAP_IS_NIC_BIT));
> +
> + if (!!(driver_data & BIT(NBL_CAP_IS_LEONIS_BIT)))
> + param->product_type = NBL_LEONIS_TYPE;
> +
> + /*
> + * Leonis only PF0 has ctrl capability, but PF0's pcie device_id
> + * is same with other PF.So handle it special.
> + */
> + if (param->product_type == NBL_LEONIS_TYPE &&
> + (PCI_FUNC(pdev->devfn) == 0))
> + param->caps.has_ctrl = 1;
> +}
> +
> static int nbl_probe(struct pci_dev *pdev,
> const struct pci_device_id *id)
> {
> + struct nbl_init_param param = { { 0 } };
> + struct device *dev = &pdev->dev;
> + struct nbl_adapter *adapter;
> + int err;
> +
> + if (pci_enable_device(pdev)) {
> + dev_err(&pdev->dev, "Failed to enable PCI device\n");
> + return -ENODEV;
> + }
> +
> + param.pci_using_dac = true;
> + nbl_get_func_param(pdev, id->driver_data, ¶m);
> +
> + err = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64));
> + if (err) {
> + dev_dbg(dev, "Configure DMA 64 bit mask failed, err = %d\n",
> + err);
> + param.pci_using_dac = false;
> + err = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
> + if (err) {
> + dev_err(dev,
> + "Configure DMA 32 bit mask failed, err = %d\n",
> + err);
> + goto configure_dma_err;
> + }
> + }
> + pci_set_master(pdev);
> + pci_save_state(pdev);
> + adapter = nbl_core_init(pdev, ¶m);
> + if (!adapter) {
> + dev_err(dev, "Nbl adapter init fail\n");
> + err = -ENOMEM;
> + goto adapter_init_err;
> + }
> + pci_set_drvdata(pdev, adapter);
> + err = nbl_core_start(adapter);
> + if (err)
> + goto core_start_err;
> return 0;
> +core_start_err:
> + nbl_core_remove(adapter);
> +adapter_init_err:
> + pci_clear_master(pdev);
> +configure_dma_err:
> + pci_disable_device(pdev);
> + return err;
> }
>
> static void nbl_remove(struct pci_dev *pdev)
> {
> + struct nbl_adapter *adapter = pci_get_drvdata(pdev);
> +
> + pci_disable_sriov(pdev);
> +
> + nbl_core_stop(adapter);
> + nbl_core_remove(adapter);
> +
> + pci_clear_master(pdev);
> + pci_disable_device(pdev);
> }
>
> /*
^ permalink raw reply
* [PATCH net-next] net-timestamp: take track of the skb when wait_for_space occurs
From: Jason Xing @ 2026-04-02 8:58 UTC (permalink / raw)
To: davem, edumazet, kuba, pabeni, horms, willemb
Cc: netdev, Jason Xing, Yushan Zhou
From: Jason Xing <kernelxing@tencent.com>
Tag the skb in tcp_sendmsg_locked() when wait_for_space occurs even
though it might not carry the last byte of the sendmsg.
If we don't do so, we might be faced with no single timestamp that
can be received by application from the error queue. The following steps
reproduce this:
1) skb A is the current last skb before entering wait_for_space process
2) tcp_push() pushes A without any tag
3) A is transmitted from TCP to driver without putting any skb carring
timestamps in the error queue, like SCHED, DRV/HARDWARE.
4) sk_stream_wait_memory() sleeps for a while and then returns with an
error code. Note that the socket lock is released.
5) skb A finally gets acked and removed from the rtx queue.
6) continue with the rest of tcp_sendmsg_locked(): it will jump to(goto)
'do_error' label and then 'out' label.
7) at this moment, skb A turns out to be the last one in this send
syscall, and miss the following tcp_tx_timestamp() opportunity before
the final tcp_push
8) application receives no timestamps this time
The original commit ad02c4f54782 ("tcp: provide timestamps for partial writes")
says it is best effort. Now it's time to cover the only potential point
to avoid missing record.
The side effect is obvious that we might record more than one time for a
single send syscall since the skb that we keep track of in this scenario
might not be the last one. But tracing more than one skb is not a bad
thing since there is an emerging/promissing trend to do a detailed
packet granularity monitor.
Thanks to the great ID, namely, tskey, application that is responsible
for the collect/sort of timestamps leverages it to put that record in
between two consecutive send syscalls correctly.
Signed-off-by: Yushan Zhou <katrinzhou@tencent.com>
Signed-off-by: Jason Xing <kernelxing@tencent.com>
---
net/ipv4/tcp.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
index 516087c622ad..2db80d75cfa4 100644
--- a/net/ipv4/tcp.c
+++ b/net/ipv4/tcp.c
@@ -1411,9 +1411,11 @@ int tcp_sendmsg_locked(struct sock *sk, struct msghdr *msg, size_t size)
wait_for_space:
set_bit(SOCK_NOSPACE, &sk->sk_socket->flags);
tcp_remove_empty_skb(sk);
- if (copied)
+ if (copied) {
+ tcp_tx_timestamp(sk, &sockc);
tcp_push(sk, flags & ~MSG_MORE, mss_now,
TCP_NAGLE_PUSH, size_goal);
+ }
err = sk_stream_wait_memory(sk, &timeo);
if (err != 0)
--
2.41.3
^ permalink raw reply related
* Re: [PATCH v9 net-next 6/6] octeontx2-af: npc: Support for custom KPU profile from filesystem
From: Paolo Abeni @ 2026-04-02 8:54 UTC (permalink / raw)
To: Ratheesh Kannoth, netdev, linux-kernel, linux-rdma
Cc: sgoutham, andrew+netdev, davem, edumazet, kuba, donald.hunter,
horms, jiri, chuck.lever, matttbe, cjubran, saeedm, leon, tariqt,
mbloch, dtatulea
In-Reply-To: <20260330053105.2722453-7-rkannoth@marvell.com>
On 3/30/26 7:31 AM, Ratheesh Kannoth wrote:
> @@ -1635,9 +1651,9 @@ int npc_cn20k_apply_custom_kpu(struct rvu *rvu,
> }
>
> /* Verify if profile fits the HW */
> - if (fw->kpus > profile->kpus) {
> - dev_warn(rvu->dev, "Not enough KPUs: %d > %ld\n", fw->kpus,
> - profile->kpus);
> + if (fw->kpus > rvu->hw->npc_kpus) {
> + dev_warn(rvu->dev, "Not enough KPUs: %d > %d\n", fw->kpus,
> + rvu->hw->npc_kpus);
> return -EINVAL;
> }
AI review says:
---
The bounds check was changed from profile->kpus (which equals
ARRAY_SIZE(npc_kpu_profiles) = 16) to rvu->hw->npc_kpus (a 5-bit
hardware field, max value 31). Can this cause an out-of-bounds write?
In the subsequent loop at lines 1658-1685 (visible earlier in the
function), the code writes to profile->kpu[kpu].cam[entry] and
profile->kpu[kpu].action[entry], where profile->kpu points to the static
global npc_kpu_profiles[] array that has exactly 16 elements.
If cn20k hardware reports npc_kpus > 16 and the firmware provides
fw->kpus > 16, the new check passes but the loop iterates beyond the
array bounds, corrupting adjacent memory.
---
Also there are several remarks for sashiko, some of them looks valid:
https://sashiko.dev/#/patchset/20260330053105.2722453-1-rkannoth%40marvell.com
please a look.
Thanks,
Paolo
^ permalink raw reply
* Re: [v9,net-next,5/6] octeontx2-af: npc: cn20k: dynamically allocate and free default MCAM entries
From: Paolo Abeni @ 2026-04-02 8:48 UTC (permalink / raw)
To: rkannoth
Cc: jiri, cjubran, edumazet, mbloch, kuba, horms, sgoutham, dtatulea,
pabeni, saeedm, matttbe, chuck.lever, tariqt, linux-rdma,
andrew+netdev, donald.hunter, netdev, leon, davem, linux-kernel
In-Reply-To: <20260330053105.2722453-6-rkannoth@marvell.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.
For local reproduction steps see https://netdev-ai.bots.linux.dev/ai-local.html
---
octeontx2-af: npc: cn20k: dynamically allocate and free default MCAM entries
Improve MCAM utilization by tying default (broadcast, multicast,
promisc, ucast) entry lifetime to NIX LF usage. On NIX LF alloc,
allocate default MCAM entries if missing; on NIX LF free, release
them so they return to the pool.
> diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c
> index ef5b081162eb..1f2128c61ade 100644
> --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c
> +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c
[ ... ]
> @@ -1566,8 +1567,8 @@ int rvu_mbox_handler_nix_lf_alloc(struct rvu *rvu,
>
> /* Alloc NIX RQ HW context memory and config the base */
> hwctx_size = 1UL << ((ctx_cfg >> 4) & 0xF);
> - err = qmem_alloc(rvu->dev, &pfvf->rq_ctx, req->rq_cnt, hwctx_size);
> - if (err)
> + rc = qmem_alloc(rvu->dev, &pfvf->rq_ctx, req->rq_cnt, hwctx_size);
> + if (rc)
> goto free_mem;
>
> pfvf->rq_bmap = kcalloc(req->rq_cnt, sizeof(long), GFP_KERNEL);
> @@ -1583,8 +1584,8 @@ int rvu_mbox_handler_nix_lf_alloc(struct rvu *rvu,
>
> /* Alloc NIX SQ HW context memory and config the base */
> hwctx_size = 1UL << (ctx_cfg & 0xF);
> - err = qmem_alloc(rvu->dev, &pfvf->sq_ctx, req->sq_cnt, hwctx_size);
> - if (err)
> + rc = qmem_alloc(rvu->dev, &pfvf->sq_ctx, req->sq_cnt, hwctx_size);
> + if (rc)
> goto free_mem;
>
> pfvf->sq_bmap = kcalloc(req->sq_cnt, sizeof(long), GFP_KERNEL);
> @@ -1599,8 +1600,8 @@ int rvu_mbox_handler_nix_lf_alloc(struct rvu *rvu,
>
> /* Alloc NIX CQ HW context memory and config the base */
> hwctx_size = 1UL << ((ctx_cfg >> 8) & 0xF);
> - err = qmem_alloc(rvu->dev, &pfvf->cq_ctx, req->cq_cnt, hwctx_size);
> - if (err)
> + rc = qmem_alloc(rvu->dev, &pfvf->cq_ctx, req->cq_cnt, hwctx_size);
> + if (rc)
> goto free_mem;
>
> pfvf->cq_bmap = kcalloc(req->cq_cnt, sizeof(long), GFP_KERNEL);
[ ... ]
> @@ -1699,9 +1706,12 @@ int rvu_mbox_handler_nix_lf_alloc(struct rvu *rvu,
>
> goto exit;
>
> +free_dft:
> + if (is_cn20k(rvu->pdev))
> + npc_cn20k_dft_rules_free(rvu, pcifunc);
> +
> free_mem:
> nix_ctx_free(rvu, pfvf);
> - rc = -ENOMEM;
>
> exit:
> /* Set macaddr of this PF/VF */
Does the removal of `rc = -ENOMEM;` cause the function to return success
when kcalloc() fails?
Looking at the kcalloc calls for rq_bmap, sq_bmap, and cq_bmap:
drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c:rvu_mbox_handler_nix_lf_alloc() {
...
rc = qmem_alloc(rvu->dev, &pfvf->rq_ctx, req->rq_cnt, hwctx_size);
if (rc)
goto free_mem;
pfvf->rq_bmap = kcalloc(req->rq_cnt, sizeof(long), GFP_KERNEL);
if (!pfvf->rq_bmap)
goto free_mem;
...
}
These three kcalloc failure paths reach free_mem with rc == 0 because the
preceding qmem_alloc() succeeded and set rc = 0. Previously, the code set
rc = -ENOMEM at the free_mem label, ensuring that any path reaching it
would return an error.
With the removal of this assignment, don't these paths now fall through
to exit: and return rc == 0, falsely indicating success?
The caller would believe NIX LF allocation succeeded with a partially
torn-down pfvf structure (NULL bitmaps, freed contexts), potentially
leading to NULL pointer dereferences on subsequent NIX LF operations.
Should the kcalloc failure paths set rc = -ENOMEM before jumping to
free_mem?
^ permalink raw reply
* Re: [PATCH v8 net-next 6/6] octeontx2-af: npc: Support for custom KPU profile from filesystem
From: Paolo Abeni @ 2026-04-02 8:45 UTC (permalink / raw)
To: Ratheesh Kannoth, Simon Horman
Cc: netdev, linux-kernel, sgoutham, davem, edumazet, kuba,
donald.hunter, jiri, chuck.lever, matttbe, cjubran, shshitrit,
dtatulea, tariqt
In-Reply-To: <actCysjPKrnzExJx@rkannoth-OptiPlex-7090>
On 3/31/26 5:43 AM, Ratheesh Kannoth wrote:
> On 2026-03-27 at 19:00:36, Simon Horman (horms@kernel.org) wrote:
>> AI review flags that:
>>
>> npc_load_kpu_profile_from_fs() calls npc_apply_custom_kpu(),
>> which returns early if the following condition is met.
>>
>> if (fw->kpus > profile->kpus)
>>
>> Does npc_prepare_default_kpu() need to be called before
>> npc_load_kpu_profile_from_fs() to initialise profile->kpus,
>> which is 0 by default due to profile being allocated using devm_kzalloc()?
> simon,
>
> Thank you very much for your time.
>
> The v9 AI review has raised the following concern again:
> https://netdev-ai.bots.linux.dev/ai-review.html?id=f29b81fe-bb3f-4902-85de-8271effbb41c
>
> "In the filesystem loading path, does profile->kpus need to be initialized
> before calling npc_load_kpu_profile_from_fs()?
>
> Simon Horman raised this concern in v8 review on lore:
> https://lore.kernel.org/netdev/20260327133036.GE567789@horms.kernel.org/"
>
> would like to clarify that npc_prepare_default_kpu(rvu, profile) is invoked
> very early in the flow, and therefore applies to all execution paths
> (default, filesystem loading, and firmware loading). Additionally,
> if the filesystem loading fails, npc_prepare_default_kpu() is invoked again to
> ensure that all values are reset to their defaults before attempting to load from firmware.
>
> Could you please advise on the best way to address the AI review comment?
> Specifically, would you recommend:
>
> 1. Adding a clarifying comment in the code, or
> 2. Invoking npc_prepare_default_kpu() again just before npc_load_kpu_profile_from_fs()
>
> to make this behavior clearer?
In this case, replying in the relevant thread is enough - AFAICS the AI
is allucinating quite a bit with this comment.
Sometimes adding an explanatory comment nearby the relevant code would
be better/more clear, but I think not in this specific case.
Thanks,
Paolo
^ permalink raw reply
* Re: [PATCH net v1] net: skb: fix cross-cache free of KFENCE-allocated skb head
From: Jiayuan Chen @ 2026-04-02 8:38 UTC (permalink / raw)
To: Eric Dumazet
Cc: netdev, Antonius, David S. Miller, Jakub Kicinski, Paolo Abeni,
Simon Horman, Jason Xing, Kuniyuki Iwashima, Michal Luczaj,
Mina Almasry, Eric Biggers, Toke Høiland-Jørgensen,
Soheil Hassas Yeganeh, Alexander Duyck, linux-kernel, bpf
In-Reply-To: <CANn89iLLwk9G6K8r=hOxsQQ2AsW751E+2pOoud9c-R+R-3i6hg@mail.gmail.com>
On 4/2/26 4:03 PM, Eric Dumazet wrote:
> On Wed, Apr 1, 2026 at 9:15 PM Jiayuan Chen <jiayuan.chen@linux.dev> wrote:
>> If we no longer care about the cost of accessing a struct page in the
>> free path, which the original commit was trying to avoid, this is
>> indeed the simplest fix — kfree() correctly handles objects via
>> virt_to_slab.
> We only have SLUB in modern kernels, kmem_cache_free() needs to touch it.
>
> Using is_kfence_address() in net/core/skbuff.c is pushing too hard in
> my opinion.
Thanks, I understand.
^ permalink raw reply
* Re: "Dead loop on virtual device" error without softirq-BKL on PREEMPT_RT
From: Sebastian Andrzej Siewior @ 2026-04-02 8:31 UTC (permalink / raw)
To: Daniel Vacek
Cc: edumazet, kuba, linux-kernel, linux-rt-devel, netdev, spasswolf,
tglx, Aaron Tomlin
In-Reply-To: <CAPjX3Ffz22M_ADE1xPqHOCyni1wLwc+reLTVyOrJ+gqNYY6vwg@mail.gmail.com>
On 2026-04-02 09:50:35 [+0200], Daniel Vacek wrote:
> My idea was that the non-preemptible one can record `current` task
> instead of the CPU to detect the deadlock. And that would also work
> for the preemptible case (it would actually match the lock owner
> approach as you did for the PREEMPT_RT case).
> One code for both configurations, no special-casing. I'd argue that's
> a better result. Am I missing something?
>
> The size of the netdev_queue structure would grow by 8 bytes for !RT
> case, but that's not a big deal, IMO. For RT case it would just fill
> the hole.
We have xmit_lock_owner as int. If you replace it with task_struct *
then on 64bit the size of the struct netdev_queue will remain unchanged
as it fills the hole before the following long.
Then you could record `current' as the lock owner in both cases. This
should work.
> --nX
Sebastian
^ permalink raw reply
* [PATCH net-next v6 2/2] r8152: Add support for the RTL8157 hardware
From: Birger Koblitz @ 2026-04-02 8:28 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni
Cc: linux-usb, netdev, linux-kernel, Chih Kai Hsu, Birger Koblitz
In-Reply-To: <20260402-rtl8157_next-v6-0-a9b77c0931ef@birger-koblitz.de>
The RTL8157 uses a different packet descriptor format compared to the
previous generation of chips. Add support for this format by adding a
descriptor format structure into the r8152 structure and corresponding
desc_ops functions which abstract the vlan-tag, tx/rx len and
tx/rx checksum algorithms.
Also, add support for the SRAM access interface of the RTL8157 and
the ADV indirect access interface and PHY setup.
For initialization of the RTL8157, combine the existing RTL8156B and
RTL8156 init functions and add RTL8157-specific functinality in order
to improve code readability and maintainability.
r8156_init() is now called with RTL_VER_10 and RTL_VER_11 for the RTL8156,
with RTL_VER_12, RTL_VER_13 and RTL_VER_15 for the RTL8156B and with
RTL_VER_16 for the RTL8157 and checks the version for chip-specific code.
Also add USB power control functions for the RTL8157.
Add support for the USB device ID of Realtek RTL8157-based adapters. Detect
the RTL8157 as RTL_VER_16 and set it up.
Signed-off-by: Birger Koblitz <mail@birger-koblitz.de>
---
drivers/net/usb/r8152.c | 929 ++++++++++++++++++++++++++++++++++++++----------
1 file changed, 745 insertions(+), 184 deletions(-)
diff --git a/drivers/net/usb/r8152.c b/drivers/net/usb/r8152.c
index 15527f3cb5478df3450d5e53b5a277fca1460e44..dab08f8983b426f6ac35261b3d7ceb14da751b58 100644
--- a/drivers/net/usb/r8152.c
+++ b/drivers/net/usb/r8152.c
@@ -123,6 +123,7 @@
#define USB_CSR_DUMMY1 0xb464
#define USB_CSR_DUMMY2 0xb466
#define USB_DEV_STAT 0xb808
+#define USB_U2P3_V2_CTRL 0xc2c0
#define USB_CONNECT_TIMER 0xcbf8
#define USB_MSC_TIMER 0xcbfc
#define USB_BURST_SIZE 0xcfc0
@@ -156,6 +157,9 @@
#define USB_U1U2_TIMER 0xd4da
#define USB_FW_TASK 0xd4e8 /* RTL8153B */
#define USB_RX_AGGR_NUM 0xd4ee
+#define USB_ADV_ADDR 0xd5d6
+#define USB_ADV_DATA 0xd5d8
+#define USB_ADV_CMD 0xd5dc
#define USB_UPS_CTRL 0xd800
#define USB_POWER_CUT 0xd80a
#define USB_MISC_0 0xd81a
@@ -213,6 +217,8 @@
#define OCP_PHY_PATCH_STAT 0xb800
#define OCP_PHY_PATCH_CMD 0xb820
#define OCP_PHY_LOCK 0xb82e
+#define OCP_SRAM2_ADDR 0xb87c
+#define OCP_SRAM2_DATA 0xb87e
#define OCP_ADC_IOFFSET 0xbcfc
#define OCP_ADC_CFG 0xbc06
#define OCP_SYSCLK_CFG 0xc416
@@ -490,6 +496,12 @@
/* USB_RX_AGGR_NUM */
#define RX_AGGR_NUM_MASK 0x1ff
+/* USB_ADV_CMD */
+#define ADV_CMD_BMU 0
+#define ADV_CMD_BUSY BIT(0)
+#define ADV_CMD_WR BIT(1)
+#define ADV_CMD_IP BIT(2)
+
/* USB_UPS_CTRL */
#define POWER_CUT 0x0100
@@ -529,11 +541,15 @@
#define CDC_ECM_EN BIT(3)
#define RX_AGG_DISABLE 0x0010
#define RX_ZERO_EN 0x0080
+#define RX_DESC_16B 0x0400
/* USB_U2P3_CTRL */
#define U2P3_ENABLE 0x0001
#define RX_DETECT8 BIT(3)
+/* USB_U2P3_V2_CTRL */
+#define U2P3_V2_ENABLE BIT(29)
+
/* USB_POWER_CUT */
#define PWR_EN 0x0001
#define PHASE2_EN 0x0008
@@ -746,8 +762,6 @@ enum rtl_register_content {
#define RTL8152_MAX_TX 4
#define RTL8152_MAX_RX 10
#define INTBUFSIZE 2
-#define TX_ALIGN 4
-#define RX_ALIGN 8
#define RTL8152_RX_MAX_PENDING 4096
#define RTL8152_RXFG_HEADSZ 256
@@ -759,7 +773,6 @@ enum rtl_register_content {
#define RTL8152_TX_TIMEOUT (5 * HZ)
#define mtu_to_size(m) ((m) + VLAN_ETH_HLEN + ETH_FCS_LEN)
#define size_to_mtu(s) ((s) - VLAN_ETH_HLEN - ETH_FCS_LEN)
-#define rx_reserved_size(x) (mtu_to_size(x) + sizeof(struct rx_desc) + RX_ALIGN)
/* rtl8152 flags */
enum rtl8152_flags {
@@ -844,6 +857,40 @@ struct tx_desc {
#define TX_VLAN_TAG BIT(16)
};
+struct rx_desc_v2 {
+ __le32 opts1;
+#define RX_LEN_MASK_2 0xfffe0000
+#define rx_v2_get_len(x) (((x) & RX_LEN_MASK_2) >> 17)
+#define RX_VLAN_TAG_2 BIT(3)
+#define RX_VER_MASK 0x3
+
+ __le32 opts2;
+
+ __le32 opts3;
+#define IPF_2 BIT(26) /* IP checksum fail */
+#define UDPF_2 BIT(25) /* UDP checksum fail */
+#define TCPF_2 BIT(24) /* TCP checksum fail */
+#define RD_IPV6_CS_2 BIT(15)
+#define RD_IPV4_CS_2 BIT(14)
+#define RD_UDP_CS_2 BIT(11)
+#define RD_TCP_CS_2 BIT(10)
+
+ __le32 opts4;
+};
+
+struct tx_desc_v2 {
+ __le32 opts1;
+
+ __le32 opts2;
+#define TCPHO_MAX_2 0x3ffU
+
+ __le32 opts3;
+#define tx_v2_set_len(x) ((x) << 4)
+
+ __le32 opts4;
+#define TX_SIG (0x15 << 27)
+};
+
struct r8152;
struct rx_agg {
@@ -917,6 +964,19 @@ struct r8152 {
u32 ctap_short_off:1;
} ups_info;
+ struct desc_info {
+ void (*vlan_tag)(void *desc, struct sk_buff *skb);
+ u8 align;
+ u8 size;
+ } rx_desc, tx_desc;
+
+ struct desc_ops {
+ void (*tx_len)(struct r8152 *tp, void *desc, u32 len);
+ u32 (*rx_len)(struct r8152 *tp, void *desc);
+ u8 (*rx_csum)(struct r8152 *tp, void *desc);
+ int (*tx_csum)(struct r8152 *tp, void *desc, struct sk_buff *skb, u32 len);
+ } desc_ops;
+
#define RTL_VER_SIZE 32
struct rtl_fw {
@@ -1181,6 +1241,7 @@ enum rtl_version {
RTL_VER_13,
RTL_VER_14,
RTL_VER_15,
+ RTL_VER_16,
RTL_VER_MAX
};
@@ -1206,7 +1267,7 @@ enum tx_csum_stat {
static const int multicast_filter_limit = 32;
static unsigned int agg_buf_sz = 16384;
-#define RTL_LIMITED_TSO_SIZE (size_to_mtu(agg_buf_sz) - sizeof(struct tx_desc))
+#define RTL_LIMITED_TSO_SIZE (size_to_mtu(agg_buf_sz) - tp->tx_desc.size)
/* If register access fails then we block access and issue a reset. If this
* happens too many times in a row without a successful access then we stop
@@ -1617,6 +1678,122 @@ static inline int r8152_mdio_read(struct r8152 *tp, u32 reg_addr)
return ocp_reg_read(tp, OCP_BASE_MII + reg_addr * 2);
}
+static int wait_cmd_ready(struct r8152 *tp, u16 cmd)
+{
+ return poll_timeout_us(u16 ocp_data = ocp_read_word(tp, MCU_TYPE_USB, cmd),
+ !(ocp_data & ADV_CMD_BUSY), 2000, 20000, false);
+}
+
+static int ocp_adv_read(struct r8152 *tp, u16 cmd, u16 addr, u32 *data)
+{
+ int ret;
+
+ ret = wait_cmd_ready(tp, USB_ADV_CMD);
+ if (ret < 0)
+ goto out;
+
+ ocp_write_word(tp, MCU_TYPE_USB, USB_ADV_ADDR, addr);
+
+ cmd |= ADV_CMD_BUSY;
+ ocp_write_word(tp, MCU_TYPE_USB, USB_ADV_CMD, cmd);
+
+ ret = wait_cmd_ready(tp, USB_ADV_CMD);
+ if (ret < 0)
+ goto out;
+
+ *data = ocp_read_dword(tp, MCU_TYPE_USB, USB_ADV_DATA);
+
+out:
+ return ret;
+}
+
+static int ocp_adv_write(struct r8152 *tp, u16 cmd, u16 addr, u32 data)
+{
+ int ret;
+
+ ret = wait_cmd_ready(tp, USB_ADV_CMD);
+ if (ret < 0)
+ goto out;
+
+ cmd |= ADV_CMD_WR;
+ ocp_write_dword(tp, MCU_TYPE_USB, USB_ADV_DATA, data);
+
+ ocp_write_word(tp, MCU_TYPE_USB, USB_ADV_ADDR, addr);
+
+ cmd |= ADV_CMD_BUSY;
+ ocp_write_word(tp, MCU_TYPE_USB, USB_ADV_CMD, cmd);
+
+out:
+ return ret;
+}
+
+static int rtl_bmu_read(struct r8152 *tp, u16 addr, u32 *data)
+{
+ return ocp_adv_read(tp, ADV_CMD_BMU, addr, data);
+}
+
+static int rtl_bmu_write(struct r8152 *tp, u16 addr, u32 data)
+{
+ return ocp_adv_write(tp, ADV_CMD_BMU, addr, data);
+}
+
+static int rtl_bmu_w0w1(struct r8152 *tp, u16 addr, u32 clear, u32 set)
+{
+ u32 bmu;
+ int ret;
+
+ ret = rtl_bmu_read(tp, addr, &bmu);
+ if (ret < 0)
+ goto out;
+
+ bmu = (bmu & ~clear) | set;
+ ret = rtl_bmu_write(tp, addr, bmu);
+
+out:
+ return ret;
+}
+
+static int rtl_bmu_clr_bits(struct r8152 *tp, u16 addr, u32 clear)
+{
+ return rtl_bmu_w0w1(tp, addr, clear, 0);
+}
+
+static int rtl_ip_read(struct r8152 *tp, u16 addr, u32 *data)
+{
+ return ocp_adv_read(tp, ADV_CMD_IP, addr, data);
+}
+
+static int rtl_ip_write(struct r8152 *tp, u16 addr, u32 data)
+{
+ return ocp_adv_write(tp, ADV_CMD_IP, addr, data);
+}
+
+static int rtl_ip_w0w1(struct r8152 *tp, u16 addr, u32 clear, u32 set)
+{
+ int ret;
+ u32 ip;
+
+ ret = rtl_ip_read(tp, addr, &ip);
+ if (ret < 0)
+ goto out;
+
+ ip = (ip & ~clear) | set;
+ ret = rtl_ip_write(tp, addr, ip);
+
+out:
+ return ret;
+}
+
+static int rtl_ip_clr_bits(struct r8152 *tp, u16 addr, u32 clear)
+{
+ return rtl_ip_w0w1(tp, addr, clear, 0);
+}
+
+static int rtl_ip_set_bits(struct r8152 *tp, u16 addr, u32 set)
+{
+ return rtl_ip_w0w1(tp, addr, 0, set);
+}
+
static void sram_write(struct r8152 *tp, u16 addr, u16 data)
{
ocp_reg_write(tp, OCP_SRAM_ADDR, addr);
@@ -1629,6 +1806,20 @@ static u16 sram_read(struct r8152 *tp, u16 addr)
return ocp_reg_read(tp, OCP_SRAM_DATA);
}
+static u16 sram2_read(struct r8152 *tp, u16 addr)
+{
+ ocp_reg_write(tp, OCP_SRAM2_ADDR, addr);
+ return ocp_reg_read(tp, OCP_SRAM2_DATA);
+}
+
+static void sram2_write_w0w1(struct r8152 *tp, u16 addr, u16 clear, u16 set)
+{
+ u16 data = sram2_read(tp, addr);
+
+ data = (data & ~clear) | set;
+ ocp_reg_write(tp, OCP_SRAM2_DATA, data);
+}
+
static int read_mii_word(struct net_device *netdev, int phy_id, int reg)
{
struct r8152 *tp = netdev_priv(netdev);
@@ -2159,14 +2350,14 @@ static void intr_callback(struct urb *urb)
}
}
-static inline void *rx_agg_align(void *data)
+static void *rx_agg_align(struct r8152 *tp, void *data)
{
- return (void *)ALIGN((uintptr_t)data, RX_ALIGN);
+ return (void *)ALIGN((uintptr_t)data, tp->rx_desc.align);
}
-static inline void *tx_agg_align(void *data)
+static void *tx_agg_align(struct r8152 *tp, void *data)
{
- return (void *)ALIGN((uintptr_t)data, TX_ALIGN);
+ return (void *)ALIGN((uintptr_t)data, tp->tx_desc.align);
}
static void free_rx_agg(struct r8152 *tp, struct rx_agg *agg)
@@ -2284,9 +2475,9 @@ static int alloc_all_mem(struct r8152 *tp)
if (!buf)
goto err1;
- if (buf != tx_agg_align(buf)) {
+ if (buf != tx_agg_align(tp, buf)) {
kfree(buf);
- buf = kmalloc_node(agg_buf_sz + TX_ALIGN, GFP_KERNEL,
+ buf = kmalloc_node(agg_buf_sz + tp->tx_desc.align, GFP_KERNEL,
node);
if (!buf)
goto err1;
@@ -2302,7 +2493,7 @@ static int alloc_all_mem(struct r8152 *tp)
tp->tx_info[i].context = tp;
tp->tx_info[i].urb = urb;
tp->tx_info[i].buffer = buf;
- tp->tx_info[i].head = tx_agg_align(buf);
+ tp->tx_info[i].head = tx_agg_align(tp, buf);
list_add_tail(&tp->tx_info[i].list, &tp->tx_free);
}
@@ -2389,8 +2580,17 @@ static void r8152_csum_workaround(struct r8152 *tp, struct sk_buff *skb,
}
}
-static inline void rtl_tx_vlan_tag(struct tx_desc *desc, struct sk_buff *skb)
+static void r8152_tx_len(struct r8152 *tp, void *tx_desc, u32 len)
{
+ struct tx_desc *desc = tx_desc;
+
+ desc->opts1 |= cpu_to_le32(len);
+}
+
+static void r8152_tx_vlan_tag(void *d, struct sk_buff *skb)
+{
+ struct tx_desc *desc = d;
+
if (skb_vlan_tag_present(skb)) {
u32 opts2;
@@ -2399,8 +2599,10 @@ static inline void rtl_tx_vlan_tag(struct tx_desc *desc, struct sk_buff *skb)
}
}
-static inline void rtl_rx_vlan_tag(struct rx_desc *desc, struct sk_buff *skb)
+static void r8152_rx_vlan_tag(void *d, struct sk_buff *skb)
{
+ struct rx_desc *desc = d;
+
u32 opts2 = le32_to_cpu(desc->opts2);
if (opts2 & RX_VLAN_TAG)
@@ -2408,10 +2610,11 @@ static inline void rtl_rx_vlan_tag(struct rx_desc *desc, struct sk_buff *skb)
swab16(opts2 & 0xffff));
}
-static int r8152_tx_csum(struct r8152 *tp, struct tx_desc *desc,
+static int r8152_tx_csum(struct r8152 *tp, void *d,
struct sk_buff *skb, u32 len)
{
u32 mss = skb_shinfo(skb)->gso_size;
+ struct tx_desc *desc = d;
u32 opts1, opts2 = 0;
int ret = TX_CSUM_SUCCESS;
@@ -2496,6 +2699,74 @@ static int r8152_tx_csum(struct r8152 *tp, struct tx_desc *desc,
return ret;
}
+static u32 r8152_rx_len(struct r8152 *tp, void *d)
+{
+ struct rx_desc *desc = d;
+
+ return le32_to_cpu(desc->opts1) & RX_LEN_MASK;
+}
+
+static u32 r8157_rx_len(struct r8152 *tp, void *d)
+{
+ struct rx_desc_v2 *desc = d;
+
+ return rx_v2_get_len(le32_to_cpu(desc->opts1));
+}
+
+static void r8157_rx_vlan_tag(void *desc, struct sk_buff *skb)
+{
+ struct rx_desc_v2 *d = desc;
+ u32 opts1;
+
+ opts1 = le32_to_cpu(d->opts1);
+ if (opts1 & RX_VLAN_TAG_2) {
+ u32 opts2 = le32_to_cpu(d->opts2);
+
+ __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q),
+ swab16((opts2 >> 16) & 0xffff));
+ }
+}
+
+static int r8157_tx_csum(struct r8152 *tp, void *tx_desc, struct sk_buff *skb, u32 len)
+{
+ u32 mss = skb_shinfo(skb)->gso_size;
+
+ if (!mss && skb->ip_summed == CHECKSUM_PARTIAL) {
+ u32 transport_offset = (u32)skb_transport_offset(skb);
+
+ if (transport_offset > TCPHO_MAX_2) {
+ netif_warn(tp, tx_err, tp->netdev,
+ "Invalid transport offset 0x%x\n",
+ transport_offset);
+ return TX_CSUM_NONE;
+ }
+ }
+
+ return r8152_tx_csum(tp, tx_desc, skb, len);
+}
+
+static void r8157_tx_len(struct r8152 *tp, void *tx_desc, u32 len)
+{
+ struct tx_desc_v2 *desc = tx_desc;
+
+ desc->opts3 = cpu_to_le32(tx_v2_set_len(len));
+ desc->opts4 = cpu_to_le32(TX_SIG);
+}
+
+static int rtl_tx_csum(struct r8152 *tp, void *desc, struct sk_buff *skb,
+ u32 len)
+{
+ int ret = TX_CSUM_SUCCESS;
+
+ WARN_ON_ONCE(len > TX_LEN_MAX);
+
+ ret = tp->desc_ops.tx_csum(tp, desc, skb, len);
+ if (!ret)
+ tp->desc_ops.tx_len(tp, desc, len);
+
+ return ret;
+}
+
static int r8152_tx_agg_fill(struct r8152 *tp, struct tx_agg *agg)
{
struct sk_buff_head skb_head, *tx_queue = &tp->tx_queue;
@@ -2512,33 +2783,33 @@ static int r8152_tx_agg_fill(struct r8152 *tp, struct tx_agg *agg)
agg->skb_len = 0;
remain = agg_buf_sz;
- while (remain >= ETH_ZLEN + sizeof(struct tx_desc)) {
- struct tx_desc *tx_desc;
+ while (remain >= ETH_ZLEN + tp->tx_desc.size) {
struct sk_buff *skb;
unsigned int len;
+ void *tx_desc;
skb = __skb_dequeue(&skb_head);
if (!skb)
break;
- len = skb->len + sizeof(*tx_desc);
+ len = skb->len + tp->tx_desc.size;
if (len > remain) {
__skb_queue_head(&skb_head, skb);
break;
}
- tx_data = tx_agg_align(tx_data);
- tx_desc = (struct tx_desc *)tx_data;
+ tx_data = tx_agg_align(tp, tx_data);
+ tx_desc = (void *)tx_data;
- if (r8152_tx_csum(tp, tx_desc, skb, skb->len)) {
+ if (rtl_tx_csum(tp, tx_desc, skb, skb->len)) {
r8152_csum_workaround(tp, skb, &skb_head);
continue;
}
- rtl_tx_vlan_tag(tx_desc, skb);
+ tp->tx_desc.vlan_tag(tx_desc, skb);
- tx_data += sizeof(*tx_desc);
+ tx_data += tp->tx_desc.size;
len = skb->len;
if (skb_copy_bits(skb, 0, tx_data, len) < 0) {
@@ -2546,7 +2817,7 @@ static int r8152_tx_agg_fill(struct r8152 *tp, struct tx_agg *agg)
stats->tx_dropped++;
dev_kfree_skb_any(skb);
- tx_data -= sizeof(*tx_desc);
+ tx_data -= tp->tx_desc.size;
continue;
}
@@ -2556,7 +2827,7 @@ static int r8152_tx_agg_fill(struct r8152 *tp, struct tx_agg *agg)
dev_kfree_skb_any(skb);
- remain = agg_buf_sz - (int)(tx_agg_align(tx_data) - agg->head);
+ remain = agg_buf_sz - (int)(tx_agg_align(tp, tx_data) - agg->head);
if (tp->dell_tb_rx_agg_bug)
break;
@@ -2594,8 +2865,9 @@ static int r8152_tx_agg_fill(struct r8152 *tp, struct tx_agg *agg)
return ret;
}
-static u8 r8152_rx_csum(struct r8152 *tp, struct rx_desc *rx_desc)
+static u8 r8152_rx_csum(struct r8152 *tp, void *d)
{
+ struct rx_desc *rx_desc = d;
u8 checksum = CHECKSUM_NONE;
u32 opts2, opts3;
@@ -2623,6 +2895,30 @@ static u8 r8152_rx_csum(struct r8152 *tp, struct rx_desc *rx_desc)
return checksum;
}
+static u8 r8157_rx_csum(struct r8152 *tp, void *desc)
+{
+ struct rx_desc_v2 *d = desc;
+ u8 checksum = CHECKSUM_NONE;
+ u32 opts3;
+
+ if (!(tp->netdev->features & NETIF_F_RXCSUM))
+ goto return_result;
+
+ opts3 = le32_to_cpu(d->opts3);
+
+ if ((opts3 & (RD_IPV4_CS_2 | IPF_2)) == (RD_IPV4_CS_2 | IPF_2)) {
+ checksum = CHECKSUM_NONE;
+ } else if (opts3 & (RD_IPV4_CS_2 | RD_IPV6_CS_2)) {
+ if ((opts3 & (RD_UDP_CS_2 | UDPF_2)) == RD_UDP_CS_2)
+ checksum = CHECKSUM_UNNECESSARY;
+ else if ((opts3 & (RD_TCP_CS_2 | TCPF_2)) == RD_TCP_CS_2)
+ checksum = CHECKSUM_UNNECESSARY;
+ }
+
+return_result:
+ return checksum;
+}
+
static inline bool rx_count_exceed(struct r8152 *tp)
{
return atomic_read(&tp->rx_count) > RTL8152_MAX_RX;
@@ -2698,10 +2994,10 @@ static int rx_bottom(struct r8152 *tp, int budget)
spin_unlock_irqrestore(&tp->rx_lock, flags);
list_for_each_safe(cursor, next, &rx_queue) {
- struct rx_desc *rx_desc;
struct rx_agg *agg, *agg_free;
int len_used = 0;
struct urb *urb;
+ void *rx_desc;
u8 *rx_data;
/* A bulk transfer of USB may contain may packets, so the
@@ -2724,7 +3020,7 @@ static int rx_bottom(struct r8152 *tp, int budget)
rx_desc = agg->buffer;
rx_data = agg->buffer;
- len_used += sizeof(struct rx_desc);
+ len_used += tp->rx_desc.size;
while (urb->actual_length > len_used) {
struct net_device *netdev = tp->netdev;
@@ -2735,7 +3031,7 @@ static int rx_bottom(struct r8152 *tp, int budget)
WARN_ON_ONCE(skb_queue_len(&tp->rx_queue) >= 1000);
- pkt_len = le32_to_cpu(rx_desc->opts1) & RX_LEN_MASK;
+ pkt_len = tp->desc_ops.rx_len(tp, rx_desc);
if (pkt_len < ETH_ZLEN)
break;
@@ -2745,7 +3041,7 @@ static int rx_bottom(struct r8152 *tp, int budget)
pkt_len -= ETH_FCS_LEN;
len = pkt_len;
- rx_data += sizeof(struct rx_desc);
+ rx_data += tp->rx_desc.size;
if (!agg_free || tp->rx_copybreak > len)
use_frags = false;
@@ -2776,8 +3072,8 @@ static int rx_bottom(struct r8152 *tp, int budget)
goto find_next_rx;
}
- skb->ip_summed = r8152_rx_csum(tp, rx_desc);
- rtl_rx_vlan_tag(rx_desc, skb);
+ skb->ip_summed = tp->desc_ops.rx_csum(tp, rx_desc);
+ tp->rx_desc.vlan_tag(rx_desc, skb);
if (use_frags) {
if (rx_frag_head_sz) {
@@ -2814,10 +3110,10 @@ static int rx_bottom(struct r8152 *tp, int budget)
}
find_next_rx:
- rx_data = rx_agg_align(rx_data + len + ETH_FCS_LEN);
- rx_desc = (struct rx_desc *)rx_data;
+ rx_data = rx_agg_align(tp, rx_data + len + ETH_FCS_LEN);
+ rx_desc = rx_data;
len_used = agg_offset(agg, rx_data);
- len_used += sizeof(struct rx_desc);
+ len_used += tp->rx_desc.size;
}
WARN_ON(!agg_free && page_count(agg->page) > 1);
@@ -3060,13 +3356,19 @@ static netdev_features_t
rtl8152_features_check(struct sk_buff *skb, struct net_device *dev,
netdev_features_t features)
{
+ struct r8152 *tp = netdev_priv(dev);
u32 mss = skb_shinfo(skb)->gso_size;
- int max_offset = mss ? GTTCPHO_MAX : TCPHO_MAX;
+ int max_offset;
+
+ if (tp->version < RTL_VER_16)
+ max_offset = mss ? GTTCPHO_MAX : TCPHO_MAX;
+ else
+ max_offset = mss ? GTTCPHO_MAX : TCPHO_MAX_2;
if ((mss || skb->ip_summed == CHECKSUM_PARTIAL) &&
skb_transport_offset(skb) > max_offset)
features &= ~(NETIF_F_CSUM_MASK | NETIF_F_GSO_MASK);
- else if ((skb->len + sizeof(struct tx_desc)) > agg_buf_sz)
+ else if ((skb->len + tp->tx_desc.size) > agg_buf_sz)
features &= ~NETIF_F_GSO_MASK;
return features;
@@ -3104,31 +3406,26 @@ static void r8152b_reset_packet_filter(struct r8152 *tp)
static void rtl8152_nic_reset(struct r8152 *tp)
{
- int i;
-
switch (tp->version) {
case RTL_TEST_01:
case RTL_VER_10:
case RTL_VER_11:
ocp_byte_clr_bits(tp, MCU_TYPE_PLA, PLA_CR, CR_TE);
-
- ocp_word_clr_bits(tp, MCU_TYPE_USB, USB_BMU_RESET,
- BMU_RESET_EP_IN);
-
+ ocp_word_clr_bits(tp, MCU_TYPE_USB, USB_BMU_RESET, BMU_RESET_EP_IN);
ocp_word_set_bits(tp, MCU_TYPE_USB, USB_USB_CTRL, CDC_ECM_EN);
-
ocp_byte_clr_bits(tp, MCU_TYPE_PLA, PLA_CR, CR_RE);
-
- ocp_word_set_bits(tp, MCU_TYPE_USB, USB_BMU_RESET,
- BMU_RESET_EP_IN);
-
+ ocp_word_set_bits(tp, MCU_TYPE_USB, USB_BMU_RESET, BMU_RESET_EP_IN);
ocp_word_clr_bits(tp, MCU_TYPE_USB, USB_USB_CTRL, CDC_ECM_EN);
break;
+ case RTL_VER_16:
+ ocp_byte_clr_bits(tp, MCU_TYPE_PLA, PLA_CR, CR_RE | CR_TE);
+ break;
+
default:
ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CR, CR_RST);
- for (i = 0; i < 1000; i++) {
+ for (int i = 0; i < 1000; i++) {
if (test_bit(RTL8152_INACCESSIBLE, &tp->flags))
break;
if (!(ocp_read_byte(tp, MCU_TYPE_PLA, PLA_CR) & CR_RST))
@@ -3141,7 +3438,7 @@ static void rtl8152_nic_reset(struct r8152 *tp)
static void set_tx_qlen(struct r8152 *tp)
{
- tp->tx_qlen = agg_buf_sz / (mtu_to_size(tp->netdev->mtu) + sizeof(struct tx_desc));
+ tp->tx_qlen = agg_buf_sz / (mtu_to_size(tp->netdev->mtu) + tp->tx_desc.size);
}
static inline u16 rtl8152_get_speed(struct r8152 *tp)
@@ -3345,6 +3642,7 @@ static void r8153_set_rx_early_timeout(struct r8152 *tp)
case RTL_VER_12:
case RTL_VER_13:
case RTL_VER_15:
+ case RTL_VER_16:
ocp_write_word(tp, MCU_TYPE_USB, USB_RX_EARLY_TIMEOUT,
640 / 8);
ocp_write_word(tp, MCU_TYPE_USB, USB_RX_EXTRA_AGGR_TMR,
@@ -3356,9 +3654,14 @@ static void r8153_set_rx_early_timeout(struct r8152 *tp)
}
}
+static u32 rx_reserved_size(struct r8152 *tp, u32 mtu)
+{
+ return mtu_to_size(mtu) + tp->rx_desc.size + tp->rx_desc.align;
+}
+
static void r8153_set_rx_early_size(struct r8152 *tp)
{
- u32 ocp_data = tp->rx_buf_sz - rx_reserved_size(tp->netdev->mtu);
+ u32 ocp_data = tp->rx_buf_sz - rx_reserved_size(tp, tp->netdev->mtu);
switch (tp->version) {
case RTL_VER_03:
@@ -3383,6 +3686,10 @@ static void r8153_set_rx_early_size(struct r8152 *tp)
ocp_write_word(tp, MCU_TYPE_USB, USB_RX_EARLY_SIZE,
ocp_data / 8);
break;
+ case RTL_VER_16:
+ ocp_write_word(tp, MCU_TYPE_USB, USB_RX_EARLY_SIZE,
+ ocp_data / 16);
+ break;
default:
WARN_ON_ONCE(1);
break;
@@ -3494,6 +3801,7 @@ static void rtl_rx_vlan_en(struct r8152 *tp, bool enable)
case RTL_VER_12:
case RTL_VER_13:
case RTL_VER_15:
+ case RTL_VER_16:
default:
if (enable)
ocp_word_set_bits(tp, MCU_TYPE_PLA, PLA_RCR1,
@@ -3651,6 +3959,14 @@ static void r8153_u2p3en(struct r8152 *tp, bool enable)
ocp_word_clr_bits(tp, MCU_TYPE_USB, USB_U2P3_CTRL, U2P3_ENABLE);
}
+static int r8157_u2p3en(struct r8152 *tp, bool enable)
+{
+ if (enable)
+ return rtl_ip_set_bits(tp, USB_U2P3_V2_CTRL, U2P3_V2_ENABLE);
+ else
+ return rtl_ip_clr_bits(tp, USB_U2P3_V2_CTRL, U2P3_V2_ENABLE);
+}
+
static void r8153b_ups_flags(struct r8152 *tp)
{
u32 ups_flags = 0;
@@ -3996,6 +4312,18 @@ static void r8153b_power_cut_en(struct r8152 *tp, bool enable)
ocp_word_clr_bits(tp, MCU_TYPE_USB, USB_MISC_0, PCUT_STATUS);
}
+static void r8157_power_cut_en(struct r8152 *tp, bool enable)
+{
+ if (enable) {
+ ocp_word_set_bits(tp, MCU_TYPE_USB, USB_POWER_CUT, PWR_EN | PHASE2_EN);
+ ocp_byte_set_bits(tp, MCU_TYPE_USB, USB_MISC_2, BIT(1));
+ } else {
+ ocp_word_clr_bits(tp, MCU_TYPE_USB, USB_POWER_CUT, PWR_EN);
+ ocp_word_clr_bits(tp, MCU_TYPE_USB, USB_MISC_0, PCUT_STATUS);
+ ocp_byte_clr_bits(tp, MCU_TYPE_USB, USB_MISC_2, BIT(1));
+ }
+}
+
static void r8153_queue_wake(struct r8152 *tp, bool enable)
{
if (enable)
@@ -4112,6 +4440,22 @@ static void rtl8156_runtime_enable(struct r8152 *tp, bool enable)
}
}
+static void rtl8157_runtime_enable(struct r8152 *tp, bool enable)
+{
+ if (enable) {
+ r8153_queue_wake(tp, true);
+ r8153b_u1u2en(tp, false);
+ r8157_u2p3en(tp, false);
+ rtl_runtime_suspend_enable(tp, true);
+ } else {
+ r8153_queue_wake(tp, false);
+ rtl_runtime_suspend_enable(tp, false);
+ r8157_u2p3en(tp, true);
+ if (tp->udev->speed >= USB_SPEED_SUPER)
+ r8153b_u1u2en(tp, true);
+ }
+}
+
static void r8153_teredo_off(struct r8152 *tp)
{
switch (tp->version) {
@@ -4136,6 +4480,7 @@ static void r8153_teredo_off(struct r8152 *tp)
case RTL_VER_13:
case RTL_VER_14:
case RTL_VER_15:
+ case RTL_VER_16:
default:
/* The bit 0 ~ 7 are relative with teredo settings. They are
* W1C (write 1 to clear), so set all 1 to disable it.
@@ -4189,6 +4534,7 @@ static void rtl_clear_bp(struct r8152 *tp, u16 type)
bp_num = 8;
break;
case RTL_VER_14:
+ case RTL_VER_16:
default:
ocp_write_word(tp, type, USB_BP2_EN, 0);
bp_num = 16;
@@ -4296,6 +4642,7 @@ static bool rtl8152_is_fw_phy_speed_up_ok(struct r8152 *tp, struct fw_phy_speed_
case RTL_VER_11:
case RTL_VER_12:
case RTL_VER_14:
+ case RTL_VER_16:
goto out;
case RTL_VER_13:
case RTL_VER_15:
@@ -5457,6 +5804,7 @@ static void rtl_eee_enable(struct r8152 *tp, bool enable)
case RTL_VER_12:
case RTL_VER_13:
case RTL_VER_15:
+ case RTL_VER_16:
if (enable) {
r8156_eee_en(tp, true);
ocp_reg_write(tp, OCP_EEE_ADV, tp->eee_adv);
@@ -6041,15 +6389,24 @@ static int rtl8156_enable(struct r8152 *tp)
if (test_bit(RTL8152_INACCESSIBLE, &tp->flags))
return -ENODEV;
- r8156_fc_parameter(tp);
+ if (tp->version < RTL_VER_12)
+ r8156_fc_parameter(tp);
+
set_tx_qlen(tp);
rtl_set_eee_plus(tp);
+
+ if (tp->version >= RTL_VER_12 && tp->version <= RTL_VER_16)
+ ocp_word_clr_bits(tp, MCU_TYPE_USB, USB_RX_AGGR_NUM, RX_AGGR_NUM_MASK);
+
r8153_set_rx_early_timeout(tp);
r8153_set_rx_early_size(tp);
speed = rtl8152_get_speed(tp);
rtl_set_ifg(tp, speed);
+ if (tp->version >= RTL_VER_16)
+ return rtl_enable(tp);
+
if (speed & _2500bps)
ocp_word_clr_bits(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL4,
IDLE_SPDWN_EN);
@@ -6057,10 +6414,12 @@ static int rtl8156_enable(struct r8152 *tp)
ocp_word_set_bits(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL4,
IDLE_SPDWN_EN);
- if (speed & _1000bps)
- ocp_write_word(tp, MCU_TYPE_PLA, PLA_EEE_TXTWSYS, 0x11);
- else if (speed & _500bps)
- ocp_write_word(tp, MCU_TYPE_PLA, PLA_EEE_TXTWSYS, 0x3d);
+ if (tp->version < RTL_VER_12) {
+ if (speed & _1000bps)
+ ocp_write_word(tp, MCU_TYPE_PLA, PLA_EEE_TXTWSYS, 0x11);
+ else if (speed & _500bps)
+ ocp_write_word(tp, MCU_TYPE_PLA, PLA_EEE_TXTWSYS, 0x3d);
+ }
if (tp->udev->speed == USB_SPEED_HIGH) {
/* USB 0xb45e[3:0] l1_nyet_hird */
@@ -6085,45 +6444,6 @@ static void rtl8156_disable(struct r8152 *tp)
rtl8153_disable(tp);
}
-static int rtl8156b_enable(struct r8152 *tp)
-{
- u16 speed;
-
- if (test_bit(RTL8152_INACCESSIBLE, &tp->flags))
- return -ENODEV;
-
- set_tx_qlen(tp);
- rtl_set_eee_plus(tp);
-
- ocp_word_clr_bits(tp, MCU_TYPE_USB, USB_RX_AGGR_NUM, RX_AGGR_NUM_MASK);
-
- r8153_set_rx_early_timeout(tp);
- r8153_set_rx_early_size(tp);
-
- speed = rtl8152_get_speed(tp);
- rtl_set_ifg(tp, speed);
-
- if (speed & _2500bps)
- ocp_word_clr_bits(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL4,
- IDLE_SPDWN_EN);
- else
- ocp_word_set_bits(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL4,
- IDLE_SPDWN_EN);
-
- if (tp->udev->speed == USB_SPEED_HIGH) {
- if (is_flow_control(speed))
- ocp_word_w0w1(tp, MCU_TYPE_USB, USB_L1_CTRL, 0xf, 0xf);
- else
- ocp_word_w0w1(tp, MCU_TYPE_USB, USB_L1_CTRL, 0xf, 0x1);
- }
-
- ocp_word_clr_bits(tp, MCU_TYPE_USB, USB_FW_TASK, FC_PATCH_TASK);
- usleep_range(1000, 2000);
- ocp_word_set_bits(tp, MCU_TYPE_USB, USB_FW_TASK, FC_PATCH_TASK);
-
- return rtl_enable(tp);
-}
-
static int rtl8152_set_speed(struct r8152 *tp, u8 autoneg, u32 speed, u8 duplex,
u32 advertising)
{
@@ -6466,7 +6786,7 @@ static void rtl8156_change_mtu(struct r8152 *tp)
/* TX share fifo free credit full threshold */
ocp_write_word(tp, MCU_TYPE_PLA, PLA_TXFIFO_CTRL, 512 / 64);
ocp_write_word(tp, MCU_TYPE_PLA, PLA_TXFIFO_FULL,
- ALIGN(rx_max_size + sizeof(struct tx_desc), 1024) / 16);
+ ALIGN(rx_max_size + tp->tx_desc.size, 1024) / 16);
}
static void rtl8156_up(struct r8152 *tp)
@@ -6475,7 +6795,8 @@ static void rtl8156_up(struct r8152 *tp)
return;
r8153b_u1u2en(tp, false);
- r8153_u2p3en(tp, false);
+ if (tp->version != RTL_VER_16)
+ r8153_u2p3en(tp, false);
r8153_aldps_en(tp, false);
rxdy_gated_en(tp, true);
@@ -6488,6 +6809,9 @@ static void rtl8156_up(struct r8152 *tp)
ocp_byte_clr_bits(tp, MCU_TYPE_PLA, PLA_OOB_CTRL, NOW_IS_OOB);
+ if (tp->version == RTL_VER_16)
+ ocp_word_clr_bits(tp, MCU_TYPE_PLA, PLA_RCR1, BIT(3));
+
ocp_word_clr_bits(tp, MCU_TYPE_PLA, PLA_SFF_STS_7, MCU_BORW_EN);
rtl_rx_vlan_en(tp, tp->netdev->features & NETIF_F_HW_VLAN_CTAG_RX);
@@ -6511,8 +6835,11 @@ static void rtl8156_up(struct r8152 *tp)
ocp_word_clr_bits(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL3,
PLA_MCU_SPDWN_EN);
- ocp_word_clr_bits(tp, MCU_TYPE_USB, USB_SPEED_OPTION,
- RG_PWRDN_EN | ALL_SPEED_OFF);
+ ocp_word_clr_bits(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL3, PLA_MCU_SPDWN_EN);
+
+ if (tp->version != RTL_VER_16)
+ ocp_word_clr_bits(tp, MCU_TYPE_USB, USB_SPEED_OPTION,
+ RG_PWRDN_EN | ALL_SPEED_OFF);
ocp_write_dword(tp, MCU_TYPE_USB, USB_RX_BUF_TH, 0x00600400);
@@ -6522,9 +6849,10 @@ static void rtl8156_up(struct r8152 *tp)
}
r8153_aldps_en(tp, true);
- r8153_u2p3en(tp, true);
+ if (tp->version != RTL_VER_16)
+ r8153_u2p3en(tp, true);
- if (tp->udev->speed >= USB_SPEED_SUPER)
+ if (tp->version != RTL_VER_16 && tp->udev->speed >= USB_SPEED_SUPER)
r8153b_u1u2en(tp, true);
}
@@ -6539,8 +6867,10 @@ static void rtl8156_down(struct r8152 *tp)
PLA_MCU_SPDWN_EN);
r8153b_u1u2en(tp, false);
- r8153_u2p3en(tp, false);
- r8153b_power_cut_en(tp, false);
+ if (tp->version != RTL_VER_16) {
+ r8153_u2p3en(tp, false);
+ r8153b_power_cut_en(tp, false);
+ }
r8153_aldps_en(tp, false);
ocp_byte_clr_bits(tp, MCU_TYPE_PLA, PLA_OOB_CTRL, NOW_IS_OOB);
@@ -7664,106 +7994,241 @@ static void r8156b_hw_phy_cfg(struct r8152 *tp)
set_bit(PHY_RESET, &tp->flags);
}
-static void r8156_init(struct r8152 *tp)
+static void r8157_hw_phy_cfg(struct r8152 *tp)
{
+ u32 ocp_data;
u16 data;
- int i;
-
- if (test_bit(RTL8152_INACCESSIBLE, &tp->flags))
- return;
-
- ocp_byte_clr_bits(tp, MCU_TYPE_USB, USB_ECM_OP, EN_ALL_SPEED);
-
- ocp_write_word(tp, MCU_TYPE_USB, USB_SPEED_OPTION, 0);
+ int ret;
- ocp_word_set_bits(tp, MCU_TYPE_USB, USB_ECM_OPTION, BYPASS_MAC_RESET);
+ r8156b_wait_loading_flash(tp);
- r8153b_u1u2en(tp, false);
-
- for (i = 0; i < 500; i++) {
- if (ocp_read_word(tp, MCU_TYPE_PLA, PLA_BOOT_CTRL) &
- AUTOLOAD_DONE)
- break;
-
- msleep(20);
- if (test_bit(RTL8152_INACCESSIBLE, &tp->flags))
- return;
+ ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_MISC_0);
+ if (ocp_data & PCUT_STATUS) {
+ ocp_data &= ~PCUT_STATUS;
+ ocp_write_word(tp, MCU_TYPE_USB, USB_MISC_0, ocp_data);
}
data = r8153_phy_status(tp, 0);
- if (data == PHY_STAT_EXT_INIT)
+ switch (data) {
+ case PHY_STAT_EXT_INIT:
+ ocp_reg_clr_bits(tp, 0xa466, BIT(0));
ocp_reg_clr_bits(tp, 0xa468, BIT(3) | BIT(1));
+ break;
+ case PHY_STAT_LAN_ON:
+ case PHY_STAT_PWRDN:
+ default:
+ break;
+ }
- r8152_mdio_test_and_clr_bit(tp, MII_BMCR, BMCR_PDOWN);
+ data = r8152_mdio_read(tp, MII_BMCR);
+ if (data & BMCR_PDOWN) {
+ data &= ~BMCR_PDOWN;
+ r8152_mdio_write(tp, MII_BMCR, data);
+ }
- data = r8153_phy_status(tp, PHY_STAT_LAN_ON);
- WARN_ON_ONCE(data != PHY_STAT_LAN_ON);
+ r8153_aldps_en(tp, false);
+ rtl_eee_enable(tp, false);
- r8153_u2p3en(tp, false);
+ ret = r8153_phy_status(tp, PHY_STAT_LAN_ON);
+ if (ret < 0)
+ return;
+ WARN_ON_ONCE(ret != PHY_STAT_LAN_ON);
- /* MSC timer = 0xfff * 8ms = 32760 ms */
- ocp_write_word(tp, MCU_TYPE_USB, USB_MSC_TIMER, 0x0fff);
+ /* PFM mode */
+ ocp_word_clr_bits(tp, MCU_TYPE_PLA, PLA_PHY_PWR, PFM_PWM_SWITCH);
- /* U1/U2/L1 idle timer. 500 us */
- ocp_write_word(tp, MCU_TYPE_USB, USB_U1U2_TIMER, 500);
+ /* Advanced Power Saving parameter */
+ ocp_reg_set_bits(tp, 0xa430, BIT(0) | BIT(1));
- r8153b_power_cut_en(tp, false);
- r8156_ups_en(tp, false);
- r8153_queue_wake(tp, false);
- rtl_runtime_suspend_enable(tp, false);
+ /* aldpsce force mode */
+ ocp_reg_clr_bits(tp, 0xa44a, BIT(2));
- if (tp->udev->speed >= USB_SPEED_SUPER)
- r8153b_u1u2en(tp, true);
+ switch (tp->version) {
+ case RTL_VER_16:
+ /* XG_INRX parameter */
+ sram_write_w0w1(tp, 0x8183, 0xff00, 0x5900);
+ ocp_reg_set_bits(tp, 0xa654, BIT(11));
+ ocp_reg_set_bits(tp, 0xb648, BIT(14));
+ ocp_reg_clr_bits(tp, 0xad2c, BIT(15));
+ ocp_reg_set_bits(tp, 0xad94, BIT(5));
+ ocp_reg_set_bits(tp, 0xada0, BIT(1));
+ ocp_reg_w0w1(tp, 0xae06, 0xfc00, 0x7c00);
+ sram2_write_w0w1(tp, 0x8647, 0xff00, 0xe600);
+ sram2_write_w0w1(tp, 0x8036, 0xff00, 0x3000);
+ sram2_write_w0w1(tp, 0x8078, 0xff00, 0x3000);
+
+ /* green mode */
+ sram2_write_w0w1(tp, 0x89e9, 0xff00, 0);
+ sram2_write_w0w1(tp, 0x8ffd, 0xff00, 0x0100);
+ sram2_write_w0w1(tp, 0x8ffe, 0xff00, 0x0200);
+ sram2_write_w0w1(tp, 0x8fff, 0xff00, 0x0400);
+
+ /* recognize AQC/Bcom function */
+ sram_write_w0w1(tp, 0x8018, 0xff00, 0x7700);
+ ocp_reg_write(tp, OCP_SRAM_ADDR, 0x8f9c);
+ ocp_reg_write(tp, OCP_SRAM_DATA, 0x0005);
+ ocp_reg_write(tp, OCP_SRAM_DATA, 0x0000);
+ ocp_reg_write(tp, OCP_SRAM_DATA, 0x00ed);
+ ocp_reg_write(tp, OCP_SRAM_DATA, 0x0502);
+ ocp_reg_write(tp, OCP_SRAM_DATA, 0x0b00);
+ ocp_reg_write(tp, OCP_SRAM_DATA, 0xd401);
+ sram_write_w0w1(tp, 0x8fa8, 0xff00, 0x2900);
+
+ /* RFI_corr_thd 5g */
+ sram2_write_w0w1(tp, 0x814b, 0xff00, 0x1100);
+ sram2_write_w0w1(tp, 0x814d, 0xff00, 0x1100);
+ sram2_write_w0w1(tp, 0x814f, 0xff00, 0x0b00);
+ sram2_write_w0w1(tp, 0x8142, 0xff00, 0x0100);
+ sram2_write_w0w1(tp, 0x8144, 0xff00, 0x0100);
+ sram2_write_w0w1(tp, 0x8150, 0xff00, 0x0100);
+
+ /* RFI_corr_thd 2p5g */
+ sram2_write_w0w1(tp, 0x8118, 0xff00, 0x0700);
+ sram2_write_w0w1(tp, 0x811a, 0xff00, 0x0700);
+ sram2_write_w0w1(tp, 0x811c, 0xff00, 0x0500);
+ sram2_write_w0w1(tp, 0x810f, 0xff00, 0x0100);
+ sram2_write_w0w1(tp, 0x8111, 0xff00, 0x0100);
+ sram2_write_w0w1(tp, 0x811d, 0xff00, 0x0100);
+
+ /* RFI parameter */
+ ocp_reg_clr_bits(tp, 0xad1c, BIT(8));
+ ocp_reg_w0w1(tp, 0xade8, 0xffc0, 0x1400);
+ sram2_write_w0w1(tp, 0x864b, 0xff00, 0x9d00);
+ sram2_write_w0w1(tp, 0x862c, 0xff00, 0x1200);
+ ocp_reg_write(tp, OCP_SRAM_ADDR, 0x8566);
+ ocp_reg_write(tp, OCP_SRAM_DATA, 0x003f);
+ ocp_reg_write(tp, OCP_SRAM_DATA, 0x3f02);
+ ocp_reg_write(tp, OCP_SRAM_DATA, 0x023c);
+ ocp_reg_write(tp, OCP_SRAM_DATA, 0x3b0a);
+ ocp_reg_write(tp, OCP_SRAM_DATA, 0x1c00);
+ ocp_reg_write(tp, OCP_SRAM_DATA, 0x0000);
+ ocp_reg_write(tp, OCP_SRAM_DATA, 0x0000);
+ ocp_reg_write(tp, OCP_SRAM_DATA, 0x0000);
+ ocp_reg_write(tp, OCP_SRAM_DATA, 0x0000);
+
+ /* RFI-color noise gen parameter 5g */
+ ocp_reg_set_bits(tp, 0xad9c, BIT(5));
+ sram2_write_w0w1(tp, 0x8122, 0xff00, 0x0c00);
+ ocp_reg_write(tp, OCP_SRAM2_ADDR, 0x82c8);
+ ocp_reg_write(tp, OCP_SRAM2_DATA, 0x03ed);
+ ocp_reg_write(tp, OCP_SRAM2_DATA, 0x03ff);
+ ocp_reg_write(tp, OCP_SRAM2_DATA, 0x0009);
+ ocp_reg_write(tp, OCP_SRAM2_DATA, 0x03fe);
+ ocp_reg_write(tp, OCP_SRAM2_DATA, 0x000b);
+ ocp_reg_write(tp, OCP_SRAM2_DATA, 0x0021);
+ ocp_reg_write(tp, OCP_SRAM2_DATA, 0x03f7);
+ ocp_reg_write(tp, OCP_SRAM2_DATA, 0x03b8);
+ ocp_reg_write(tp, OCP_SRAM2_DATA, 0x03e0);
+ ocp_reg_write(tp, OCP_SRAM2_DATA, 0x0049);
+ ocp_reg_write(tp, OCP_SRAM2_DATA, 0x0049);
+ ocp_reg_write(tp, OCP_SRAM2_DATA, 0x03e0);
+ ocp_reg_write(tp, OCP_SRAM2_DATA, 0x03b8);
+ ocp_reg_write(tp, OCP_SRAM2_DATA, 0x03f7);
+ ocp_reg_write(tp, OCP_SRAM2_DATA, 0x0021);
+ ocp_reg_write(tp, OCP_SRAM2_DATA, 0x000b);
+ ocp_reg_write(tp, OCP_SRAM2_DATA, 0x03fe);
+ ocp_reg_write(tp, OCP_SRAM2_DATA, 0x0009);
+ ocp_reg_write(tp, OCP_SRAM2_DATA, 0x03ff);
+ ocp_reg_write(tp, OCP_SRAM2_DATA, 0x03ed);
+
+ /* RFI-color noise gen parameter 2p5g */
+ sram2_write_w0w1(tp, 0x80ef, 0xff00, 0x0c00);
+ ocp_reg_write(tp, OCP_SRAM2_ADDR, 0x82a0);
+ ocp_reg_write(tp, OCP_SRAM2_DATA, 0x000e);
+ ocp_reg_write(tp, OCP_SRAM2_DATA, 0x03fe);
+ ocp_reg_write(tp, OCP_SRAM2_DATA, 0x03ed);
+ ocp_reg_write(tp, OCP_SRAM2_DATA, 0x0006);
+ ocp_reg_write(tp, OCP_SRAM2_DATA, 0x001a);
+ ocp_reg_write(tp, OCP_SRAM2_DATA, 0x03f1);
+ ocp_reg_write(tp, OCP_SRAM2_DATA, 0x03d8);
+ ocp_reg_write(tp, OCP_SRAM2_DATA, 0x0023);
+ ocp_reg_write(tp, OCP_SRAM2_DATA, 0x0054);
+ ocp_reg_write(tp, OCP_SRAM2_DATA, 0x0322);
+ ocp_reg_write(tp, OCP_SRAM2_DATA, 0x00dd);
+ ocp_reg_write(tp, OCP_SRAM2_DATA, 0x03ab);
+ ocp_reg_write(tp, OCP_SRAM2_DATA, 0x03dc);
+ ocp_reg_write(tp, OCP_SRAM2_DATA, 0x0027);
+ ocp_reg_write(tp, OCP_SRAM2_DATA, 0x000e);
+ ocp_reg_write(tp, OCP_SRAM2_DATA, 0x03e5);
+ ocp_reg_write(tp, OCP_SRAM2_DATA, 0x03f9);
+ ocp_reg_write(tp, OCP_SRAM2_DATA, 0x0012);
+ ocp_reg_write(tp, OCP_SRAM2_DATA, 0x0001);
+ ocp_reg_write(tp, OCP_SRAM2_DATA, 0x03f1);
+
+ /* modify thermal speed down threshold */
+ ocp_reg_w0w1(tp, 0xb54c, 0xffc0, 0x3700);
+
+ /* XG compatibility modification */
+ ocp_reg_set_bits(tp, 0xb648, BIT(6));
+ sram2_write_w0w1(tp, 0x8082, 0xff00, 0x5d00);
+ sram2_write_w0w1(tp, 0x807c, 0xff00, 0x5000);
+ sram2_write_w0w1(tp, 0x809d, 0xff00, 0x5000);
+ break;
+ default:
+ break;
+ }
- usb_enable_lpm(tp->udev);
+ if (rtl_phy_patch_request(tp, true, true))
+ return;
- r8156_mac_clk_spd(tp, true);
+ ocp_word_set_bits(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL4, EEE_SPDWN_EN);
- ocp_word_clr_bits(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL3,
- PLA_MCU_SPDWN_EN);
+ ocp_reg_w0w1(tp, OCP_DOWN_SPEED, EN_EEE_100 | EN_EEE_1000, EN_10M_CLKDIV);
- if (rtl8152_get_speed(tp) & LINK_STATUS)
- ocp_word_set_bits(tp, MCU_TYPE_PLA, PLA_EXTRA_STATUS,
- CUR_LINK_OK | POLL_LINK_CHG);
- else
- ocp_word_w0w1(tp, MCU_TYPE_PLA, PLA_EXTRA_STATUS, CUR_LINK_OK,
- POLL_LINK_CHG);
+ tp->ups_info._10m_ckdiv = true;
+ tp->ups_info.eee_plloff_100 = false;
+ tp->ups_info.eee_plloff_giga = false;
- set_bit(GREEN_ETHERNET, &tp->flags);
+ ocp_reg_set_bits(tp, OCP_POWER_CFG, EEE_CLKDIV_EN);
+ tp->ups_info.eee_ckdiv = true;
- /* rx aggregation */
- ocp_word_clr_bits(tp, MCU_TYPE_USB, USB_USB_CTRL,
- RX_AGG_DISABLE | RX_ZERO_EN);
+ rtl_phy_patch_request(tp, false, true);
- ocp_byte_set_bits(tp, MCU_TYPE_USB, USB_BMU_CONFIG, ACT_ODMA);
+ rtl_green_en(tp, test_bit(GREEN_ETHERNET, &tp->flags));
- r8156_mdio_force_mode(tp);
- rtl_tally_reset(tp);
+ ocp_reg_clr_bits(tp, 0xa428, BIT(9));
+ ocp_reg_clr_bits(tp, 0xa5ea, BIT(0) | BIT(1));
+ tp->ups_info.lite_mode = 0;
- tp->coalesce = 15000; /* 15 us */
+ if (tp->eee_en)
+ rtl_eee_enable(tp, true);
+
+ r8153_aldps_en(tp, true);
+ r8152b_enable_fc(tp);
+
+ set_bit(PHY_RESET, &tp->flags);
}
-static void r8156b_init(struct r8152 *tp)
+static void r8156_init(struct r8152 *tp)
{
+ u32 ocp_data;
u16 data;
int i;
if (test_bit(RTL8152_INACCESSIBLE, &tp->flags))
return;
+ if (tp->version == RTL_VER_16) {
+ ocp_byte_set_bits(tp, MCU_TYPE_USB, 0xcffe, BIT(3));
+ ocp_byte_clr_bits(tp, MCU_TYPE_USB, 0xd3ca, BIT(0));
+ }
+
ocp_byte_clr_bits(tp, MCU_TYPE_USB, USB_ECM_OP, EN_ALL_SPEED);
- ocp_write_word(tp, MCU_TYPE_USB, USB_SPEED_OPTION, 0);
+ if (tp->version != RTL_VER_16)
+ ocp_write_word(tp, MCU_TYPE_USB, USB_SPEED_OPTION, 0);
ocp_word_set_bits(tp, MCU_TYPE_USB, USB_ECM_OPTION, BYPASS_MAC_RESET);
- ocp_word_set_bits(tp, MCU_TYPE_USB, USB_U2P3_CTRL, RX_DETECT8);
+ if (tp->version >= RTL_VER_12 && tp->version <= RTL_VER_15)
+ ocp_word_set_bits(tp, MCU_TYPE_USB, USB_U2P3_CTRL, RX_DETECT8);
r8153b_u1u2en(tp, false);
switch (tp->version) {
case RTL_VER_13:
case RTL_VER_15:
+ case RTL_VER_16:
r8156b_wait_loading_flash(tp);
break;
default:
@@ -7783,14 +8248,22 @@ static void r8156b_init(struct r8152 *tp)
data = r8153_phy_status(tp, 0);
if (data == PHY_STAT_EXT_INIT) {
ocp_reg_clr_bits(tp, 0xa468, BIT(3) | BIT(1));
- ocp_reg_clr_bits(tp, 0xa466, BIT(0));
+ if (tp->version >= RTL_VER_12)
+ ocp_reg_clr_bits(tp, 0xa466, BIT(0));
}
- r8152_mdio_test_and_clr_bit(tp, MII_BMCR, BMCR_PDOWN);
+ data = r8152_mdio_read(tp, MII_BMCR);
+ if (data & BMCR_PDOWN) {
+ data &= ~BMCR_PDOWN;
+ r8152_mdio_write(tp, MII_BMCR, data);
+ }
data = r8153_phy_status(tp, PHY_STAT_LAN_ON);
- r8153_u2p3en(tp, false);
+ if (tp->version == RTL_VER_16)
+ r8157_u2p3en(tp, false);
+ else
+ r8153_u2p3en(tp, false);
/* MSC timer = 0xfff * 8ms = 32760 ms */
ocp_write_word(tp, MCU_TYPE_USB, USB_MSC_TIMER, 0x0fff);
@@ -7798,7 +8271,11 @@ static void r8156b_init(struct r8152 *tp)
/* U1/U2/L1 idle timer. 500 us */
ocp_write_word(tp, MCU_TYPE_USB, USB_U1U2_TIMER, 500);
- r8153b_power_cut_en(tp, false);
+ if (tp->version == RTL_VER_16)
+ r8157_power_cut_en(tp, false);
+ else
+ r8153b_power_cut_en(tp, false);
+
r8156_ups_en(tp, false);
r8153_queue_wake(tp, false);
rtl_runtime_suspend_enable(tp, false);
@@ -7808,39 +8285,53 @@ static void r8156b_init(struct r8152 *tp)
usb_enable_lpm(tp->udev);
- ocp_word_clr_bits(tp, MCU_TYPE_PLA, PLA_RCR, SLOT_EN);
+ if (tp->version >= RTL_VER_12 && tp->version <= RTL_VER_15) {
+ ocp_word_clr_bits(tp, MCU_TYPE_PLA, PLA_RCR, SLOT_EN);
- ocp_word_set_bits(tp, MCU_TYPE_PLA, PLA_CPCR, FLOW_CTRL_EN);
+ ocp_word_set_bits(tp, MCU_TYPE_PLA, PLA_CPCR, FLOW_CTRL_EN);
- /* enable fc timer and set timer to 600 ms. */
- ocp_write_word(tp, MCU_TYPE_USB, USB_FC_TIMER,
- CTRL_TIMER_EN | (600 / 8));
+ /* enable fc timer and set timer to 600 ms. */
+ ocp_write_word(tp, MCU_TYPE_USB, USB_FC_TIMER, CTRL_TIMER_EN | (600 / 8));
- if (!(ocp_read_word(tp, MCU_TYPE_PLA, PLA_POL_GPIO_CTRL) & DACK_DET_EN))
- ocp_word_w0w1(tp, MCU_TYPE_USB, USB_FW_CTRL, AUTO_SPEEDUP,
- FLOW_CTRL_PATCH_2);
- else
- ocp_word_clr_bits(tp, MCU_TYPE_USB, USB_FW_CTRL, AUTO_SPEEDUP);
+ ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_FW_CTRL);
+ if (!(ocp_read_word(tp, MCU_TYPE_PLA, PLA_POL_GPIO_CTRL) & DACK_DET_EN))
+ ocp_data |= FLOW_CTRL_PATCH_2;
+ ocp_data &= ~AUTO_SPEEDUP;
+ ocp_write_word(tp, MCU_TYPE_USB, USB_FW_CTRL, ocp_data);
- ocp_word_set_bits(tp, MCU_TYPE_USB, USB_FW_TASK, FC_PATCH_TASK);
+ ocp_word_set_bits(tp, MCU_TYPE_USB, USB_FW_TASK, FC_PATCH_TASK);
+ }
r8156_mac_clk_spd(tp, true);
- ocp_word_clr_bits(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL3,
- PLA_MCU_SPDWN_EN);
+ if (tp->version != RTL_VER_16)
+ ocp_word_clr_bits(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL3, PLA_MCU_SPDWN_EN);
+ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_EXTRA_STATUS);
if (rtl8152_get_speed(tp) & LINK_STATUS)
- ocp_word_set_bits(tp, MCU_TYPE_PLA, PLA_EXTRA_STATUS,
- CUR_LINK_OK | POLL_LINK_CHG);
+ ocp_data |= CUR_LINK_OK;
else
- ocp_word_w0w1(tp, MCU_TYPE_PLA, PLA_EXTRA_STATUS, CUR_LINK_OK,
- POLL_LINK_CHG);
+ ocp_data &= ~CUR_LINK_OK;
+ ocp_data |= POLL_LINK_CHG;
+ ocp_write_word(tp, MCU_TYPE_PLA, PLA_EXTRA_STATUS, ocp_data);
set_bit(GREEN_ETHERNET, &tp->flags);
- /* rx aggregation */
- ocp_word_clr_bits(tp, MCU_TYPE_USB, USB_USB_CTRL,
- RX_AGG_DISABLE | RX_ZERO_EN);
+ /* rx aggregation / 16 bytes Rx descriptor */
+ if (tp->version == RTL_VER_16)
+ ocp_word_clr_bits(tp, MCU_TYPE_USB, USB_USB_CTRL, RX_AGG_DISABLE | RX_DESC_16B);
+ else
+ ocp_word_clr_bits(tp, MCU_TYPE_USB, USB_USB_CTRL, RX_AGG_DISABLE | RX_ZERO_EN);
+
+ if (tp->version < RTL_VER_12)
+ ocp_byte_set_bits(tp, MCU_TYPE_USB, USB_BMU_CONFIG, ACT_ODMA);
+
+ if (tp->version == RTL_VER_16) {
+ /* Disable Rx Zero Len */
+ rtl_bmu_clr_bits(tp, 0x2300, BIT(3));
+ /* TX descriptor Signature */
+ ocp_byte_clr_bits(tp, MCU_TYPE_USB, 0xd4ae, BIT(1));
+ }
r8156_mdio_force_mode(tp);
rtl_tally_reset(tp);
@@ -8990,6 +9481,11 @@ static void rtl8153_unload(struct r8152 *tp)
return;
r8153_power_cut_en(tp, false);
+
+ if (tp->version >= RTL_VER_16) {
+ /* Disable Interrupt Mitigation */
+ ocp_byte_clr_bits(tp, MCU_TYPE_USB, 0xcf04, BIT(0) | BIT(1) | BIT(2) | BIT(7));
+ }
}
static void rtl8153b_unload(struct r8152 *tp)
@@ -9000,6 +9496,38 @@ static void rtl8153b_unload(struct r8152 *tp)
r8153b_power_cut_en(tp, false);
}
+static int r8152_desc_init(struct r8152 *tp)
+{
+ tp->rx_desc.size = sizeof(struct rx_desc);
+ tp->rx_desc.align = 8;
+ tp->rx_desc.vlan_tag = r8152_rx_vlan_tag;
+ tp->desc_ops.rx_csum = r8152_rx_csum;
+ tp->desc_ops.rx_len = r8152_rx_len;
+ tp->tx_desc.size = sizeof(struct tx_desc);
+ tp->tx_desc.align = 4;
+ tp->tx_desc.vlan_tag = r8152_tx_vlan_tag;
+ tp->desc_ops.tx_csum = r8152_tx_csum;
+ tp->desc_ops.tx_len = r8152_tx_len;
+
+ return 0;
+}
+
+static int r8157_desc_init(struct r8152 *tp)
+{
+ tp->rx_desc.size = sizeof(struct rx_desc_v2);
+ tp->rx_desc.align = 16;
+ tp->rx_desc.vlan_tag = r8157_rx_vlan_tag;
+ tp->desc_ops.rx_csum = r8157_rx_csum;
+ tp->desc_ops.rx_len = r8157_rx_len;
+ tp->tx_desc.size = sizeof(struct tx_desc_v2);
+ tp->tx_desc.align = 16;
+ tp->tx_desc.vlan_tag = r8152_tx_vlan_tag;
+ tp->desc_ops.tx_csum = r8157_tx_csum;
+ tp->desc_ops.tx_len = r8157_tx_len;
+
+ return 0;
+}
+
static int rtl_ops_init(struct r8152 *tp)
{
struct rtl_ops *ops = &tp->rtl_ops;
@@ -9023,6 +9551,7 @@ static int rtl_ops_init(struct r8152 *tp)
tp->rx_buf_sz = 16 * 1024;
tp->eee_en = true;
tp->eee_adv = MDIO_EEE_100TX;
+ r8152_desc_init(tp);
break;
case RTL_VER_03:
@@ -9047,6 +9576,7 @@ static int rtl_ops_init(struct r8152 *tp)
tp->rx_buf_sz = 32 * 1024;
tp->eee_en = true;
tp->eee_adv = MDIO_EEE_1000T | MDIO_EEE_100TX;
+ r8152_desc_init(tp);
break;
case RTL_VER_08:
@@ -9066,6 +9596,7 @@ static int rtl_ops_init(struct r8152 *tp)
tp->rx_buf_sz = 32 * 1024;
tp->eee_en = true;
tp->eee_adv = MDIO_EEE_1000T | MDIO_EEE_100TX;
+ r8152_desc_init(tp);
break;
case RTL_VER_11:
@@ -9088,6 +9619,7 @@ static int rtl_ops_init(struct r8152 *tp)
ops->change_mtu = rtl8156_change_mtu;
tp->rx_buf_sz = 48 * 1024;
tp->support_2500full = 1;
+ r8152_desc_init(tp);
break;
case RTL_VER_12:
@@ -9098,8 +9630,8 @@ static int rtl_ops_init(struct r8152 *tp)
tp->eee_en = true;
tp->eee_adv = MDIO_EEE_1000T | MDIO_EEE_100TX;
tp->eee_adv2 = MDIO_EEE_2_5GT;
- ops->init = r8156b_init;
- ops->enable = rtl8156b_enable;
+ ops->init = r8156_init;
+ ops->enable = rtl8156_enable;
ops->disable = rtl8153_disable;
ops->up = rtl8156_up;
ops->down = rtl8156_down;
@@ -9111,6 +9643,7 @@ static int rtl_ops_init(struct r8152 *tp)
ops->autosuspend_en = rtl8156_runtime_enable;
ops->change_mtu = rtl8156_change_mtu;
tp->rx_buf_sz = 48 * 1024;
+ r8152_desc_init(tp);
break;
case RTL_VER_14:
@@ -9129,6 +9662,29 @@ static int rtl_ops_init(struct r8152 *tp)
tp->rx_buf_sz = 32 * 1024;
tp->eee_en = true;
tp->eee_adv = MDIO_EEE_1000T | MDIO_EEE_100TX;
+ r8152_desc_init(tp);
+ break;
+
+ case RTL_VER_16:
+ tp->eee_en = true;
+ tp->eee_adv = MDIO_EEE_1000T | MDIO_EEE_100TX;
+ tp->eee_adv2 = MDIO_EEE_2_5GT | MDIO_EEE_5GT;
+ ops->init = r8156_init;
+ ops->enable = rtl8156_enable;
+ ops->disable = rtl8153_disable;
+ ops->up = rtl8156_up;
+ ops->down = rtl8156_down;
+ ops->unload = rtl8153_unload;
+ ops->eee_get = r8153_get_eee;
+ ops->eee_set = r8152_set_eee;
+ ops->in_nway = rtl8153_in_nway;
+ ops->hw_phy_cfg = r8157_hw_phy_cfg;
+ ops->autosuspend_en = rtl8157_runtime_enable;
+ ops->change_mtu = rtl8156_change_mtu;
+ tp->rx_buf_sz = 32 * 1024;
+ tp->support_2500full = 1;
+ tp->support_5000full = 1;
+ r8157_desc_init(tp);
break;
default:
@@ -9281,6 +9837,9 @@ static u8 __rtl_get_hw_ver(struct usb_device *udev)
case 0x7420:
version = RTL_VER_15;
break;
+ case 0x1030:
+ version = RTL_VER_16;
+ break;
default:
version = RTL_VER_UNKNOWN;
dev_info(&udev->dev, "Unknown version 0x%04x\n", ocp_data);
@@ -9432,6 +9991,7 @@ static int rtl8152_probe_once(struct usb_interface *intf,
case RTL_VER_12:
case RTL_VER_13:
case RTL_VER_15:
+ case RTL_VER_16:
netdev->max_mtu = size_to_mtu(16 * 1024);
break;
case RTL_VER_01:
@@ -9591,6 +10151,7 @@ static const struct usb_device_id rtl8152_table[] = {
{ USB_DEVICE(VENDOR_ID_REALTEK, 0x8153) },
{ USB_DEVICE(VENDOR_ID_REALTEK, 0x8155) },
{ USB_DEVICE(VENDOR_ID_REALTEK, 0x8156) },
+ { USB_DEVICE(VENDOR_ID_REALTEK, 0x8157) },
/* Microsoft */
{ USB_DEVICE(VENDOR_ID_MICROSOFT, 0x07ab) },
--
2.47.3
^ 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