* Re: [PATCH v2 0/5] netfilter: nf_flow_table_path: L2 bridge offload
From: Daniel Pawlik @ 2026-07-01 6:11 UTC (permalink / raw)
To: Pablo Neira Ayuso
Cc: netfilter-devel, netdev, bridge, coreteam, linux-mediatek,
linux-arm-kernel
In-Reply-To: <akOCJI-2kAAwOQzz@chamomile>
Hi Florian, Pablo,
I'll leave it up to you - if `br_netfilter` isn't the right approach
in this case, then we can drop that series.
Before your reply, I wasn't familiar with Eric Woudstra's
"bridge-fastpath" series - thanks for the tip.
I'll take a look at it and try to build on those patches.
Thanks, and best regards,
Dan
wt., 30 cze 2026 o 10:45 Pablo Neira Ayuso <pablo@netfilter.org> napisał(a):
>
> Hi,
>
> On Tue, Jun 30, 2026 at 08:57:30AM +0200, Daniel Pawlik wrote:
> > This series adds L2 bridge offload support to nft_flow_offload, allowing
> > bridged IPv4/IPv6 flows to be accelerated by the flowtable fast path
> > without requiring L3 routing.
> >
> > Background
> > ----------
> > Hardware flow offload engines (e.g. MediaTek PPE) can accelerate bridged
> > traffic but require that nft_flow_offload detect and handle bridged flows
> > differently from routed ones: no routing table lookup, MAC addresses from
> > the Ethernet header, and VLAN context pre-populated from the bridge port.
> >
> > v2: Fix missing Returns: tags in kernel-doc comments for the three new
> > bridge helpers (br_fdb_has_forwarding_entry_rcu,
> > br_vlan_get_offload_info_rcu, br_vlan_is_enabled_rcu).
> >
> > Patches
> > -------
> > 1/5 net: export __dev_fill_forward_path
> > Refactors dev_fill_forward_path() to expose __dev_fill_forward_path()
> > which accepts a caller-supplied net_device_path_ctx, needed to
> > pre-populate VLAN state before the forward path walk.
> >
> > 2/5 net: bridge: add flow offload helpers
> > Adds br_fdb_has_forwarding_entry_rcu(), br_vlan_get_offload_info_rcu()
> > and br_vlan_is_enabled_rcu() to expose bridge state to nft_flow_offload
> > without requiring inclusion of net/bridge/br_private.h.
> >
> > 3/5 netfilter: nf_flow_table_path: add L2 bridge offload
> > Core of the series. Adds nft_flow_offload_is_bridging() detection,
> > nft_flow_route_bridging() which avoids nf_route() (fails for
> > bridged-only subnets), MAC/VLAN pre-population for bridged flows,
> > and a dst leak fix. nft_flow_route() becomes a thin dispatcher.
> >
> > 4/5 netfilter: nf_flow_table_path: handle DEV_PATH_MTK_WDMA in path info
> > Fixes zero-source-MAC in PPE entries when a bridged flow traverses
> > MT7996/MT7915 WiFi WDMA hardware.
> >
> > 5/5 netfilter: nf_flow_table_path: add VLAN passthrough support
> > Records VLAN encap info for passthrough-mode bridge ports so hardware
> > offload entries include the correct VLAN tag.
> >
> > Rebase note
> > -----------
> > Originally developed against OpenWrt pending-6.18 patches by Ryan Chen
> > <rchen14b@gmail.com> and Bo-Cun Chen <bc-bocun.chen@mediatek.com>.
> > Rebased to current upstream: path discovery infrastructure moved to
> > nf_flow_table_path.c in commit 93d7a7ed0734 ("netfilter: flowtable: move
> > path discovery infrastructure to its own file"), so all netfilter changes
> > now land in that file rather than nft_flow_offload.c.
> >
> > How to enable bridge offload
> > -----------------------------
> > 1. Load kmod-br-netfilter so that bridged IP traffic traverses the
> > netfilter forward chain.
> >
> > 2. Enable netfilter hooks on the bridge:
> > echo 1 > /sys/class/net/<br>/bridge/nf_call_iptables
> > echo 1 > /sys/class/net/<br>/bridge/nf_call_ip6tables
>
> This requires br_netfilter which is a no go.
>
> Sorry, but we should really target at the native nf_conntrack_bridge
> support.
>
> > 3. Register bridge member interfaces in the nft flowtable:
> > table inet filter {
> > flowtable f {
> > hook ingress priority filter
> > devices = { eth0, wlan0 }
> > }
> > chain forward {
> > type filter hook forward priority filter
> > meta l4proto { tcp, udp } flow add @f
> > }
> > }
>
> Yes, but br_netfilter makes no sense for nftables.
>
> br_netfilter was made to fill gap at the time ebtables was lagging a
> lot behind iptables in terms of features. And getting ebtables on pair
> with iptables in functionality was not feasible either, because it
> required many new extensions that were specific of the bridge family,
> which probably was not a big deal, but it also required to get
> the ebtables command line tool on pair with iptables userspace, which
> has received more development attention/effort that the bridge tool.
>
> All of this does not stand true anymore with nftables, where the
> bridge family capabilities are at pair with the inet families.
>
> I am looking now at the native flowtable bridge support, I will get
> back to you with updates.
^ permalink raw reply
* Re: [PATCH v3 net-next 1/1] tcp: Replace min_tso_segs() with tso_segs() CC callback
From: Alexei Starovoitov @ 2026-07-01 5:56 UTC (permalink / raw)
To: Chia-Yu Chang (Nokia), jolsa@kernel.org, yonghong.song@linux.dev,
song@kernel.org, linux-kselftest@vger.kernel.org,
memxor@gmail.com, shuah@kernel.org, martin.lau@linux.dev,
ast@kernel.org, daniel@iogearbox.net, andrii@kernel.org,
eddyz87@gmail.com, horms@kernel.org, dsahern@kernel.org,
bpf@vger.kernel.org, netdev@vger.kernel.org, pabeni@redhat.com,
jhs@mojatatu.com, kuba@kernel.org, stephen@networkplumber.org,
davem@davemloft.net, edumazet@google.com, andrew+netdev@lunn.ch,
donald.hunter@gmail.com, kuniyu@google.com, ij@kernel.org,
ncardwell@google.com, Koen De Schepper (Nokia),
g.white@cablelabs.com, ingemar.s.johansson@ericsson.com,
mirja.kuehlewind@ericsson.com, cheshire@apple.com, rs.ietf@gmx.at,
Jason_Livingood@comcast.com, vidhi_goel@apple.com
In-Reply-To: <PAXPR07MB7984443B70E713F016EAA550A3F62@PAXPR07MB7984.eurprd07.prod.outlook.com>
On Tue Jun 30, 2026 at 10:46 PM PDT, Chia-Yu Chang (Nokia) wrote:
>> > - /* override sysctl_tcp_min_tso_segs (optional) */
>> > - u32 (*min_tso_segs)(struct sock *sk);
>> > + /*
>> > + * Override tcp_tso_autosize (optional)
>> > + *
>> > + * If provided, this callback returns the final TSO segment number
>> > + * and will bypass tcp_tso_autosize() entirely. The implementation
>> > + * must derive an appropriate value and ensure the result is valid.
>> > + */
>> > + u32 (*tso_segs)(struct sock *sk, u32 mss_now);
>>
>> I don't like this interface change.
>> It introduces churn for no good reason.
>> At least I don't see why you cannot live with the existing api.
>
> Hi Alexei,
>
> This patch was part of TCP Prague preparation series: https://lore.kernel.org/all/20260611161504.228319-4-chia-yu.chang@nokia-bell-labs.com/
> Our original patch is to add an extra tso_segs, and after discussion it's recommended to replace exisiting min_tso_segs.
>
> This is needed because TCP Prague would set the exact TSO size rather than using autosizing from TCP.
> The TCP Prague itself is planned to be submitted after all preparation commits are accepted.
> You can find its current stauts: https://github.com/L4STeam/linux-net-next/blob/upstream_l4steam/net/ipv4/tcp_prague.c
You have to explain why Prague CC cannot rely on autosizing.
To me it sounds like a red flag. autosizing logic was there for a decade, if not more.
And now you're arguing that your CC logic is special and it deserves new API
and breakage of existing convention.
Maybe you should step back and reconsider.
^ permalink raw reply
* [PATCH 2/9] ax88179_178a: Add HW support for AX179A-based chips
From: Birger Koblitz @ 2026-07-01 5:42 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni
Cc: linux-usb, netdev, linux-kernel, Birger Koblitz
In-Reply-To: <20260701-ax88179a-v1-0-13685df67515@birger-koblitz.de>
This adds bindings and HW support for AX179A-based USB-Ethernet
controllers. The AX179A-family of chips consists of the
AX88279 (2.5GBit PHY)
AX88179A/B (1GBit PHY, B variant has wider temperature range)
AX772D/E (100Mbit PHY)
The controllers all have the same vendor and device ID
(0x0b95, 0x1790) and are distinguished by their BCD device versions,
which are
2.00 AX88179A/B
3.00 AX88772D/E
4.00 AX88279
For all chips, the driver calls the same ax88179a_bind() function
and the chips are then distinguished by the chip version and
BCD device ID. The AX179A-based chips all provide both a CDC-NCM
compatible USB interface, and a proprietary vendor interface. By default,
the proprietary vendor interface is not active and Linux will load the
CDC-NCM driver to support the devices. If the ax88179_178a module is
configured by the OS to have precedence over CDC-NCM, then this driver
will switch the device to use the vendor interface, and the device will
be controlled by the ax88179_178a driver when the device is probed again
after an automatic reset by the device.
Signed-off-by: Birger Koblitz <mail@birger-koblitz.de>
---
drivers/net/usb/ax88179_178a.c | 946 +++++++++++++++++++++++++++++++++++++++--
1 file changed, 908 insertions(+), 38 deletions(-)
diff --git a/drivers/net/usb/ax88179_178a.c b/drivers/net/usb/ax88179_178a.c
index 945c071dfd1d2f0816c779e1a401ac158adc8d99..c6945e131e63fd053a23bf14ab5d5948456ce4a5 100644
--- a/drivers/net/usb/ax88179_178a.c
+++ b/drivers/net/usb/ax88179_178a.c
@@ -13,6 +13,7 @@
#include <linux/usb/usbnet.h>
#include <uapi/linux/mdio.h>
#include <linux/mdio.h>
+#include <linux/if_vlan.h>
#define AX88179_PHY_ID 0x03
#define AX_EEPROM_LEN 0x100
@@ -32,17 +33,28 @@
#define AX_ACCESS_EEPROM 0x04
#define AX_ACCESS_EFUS 0x05
#define AX_RELOAD_EEPROM_EFUSE 0x06
+#define AX88179A_WAKEUP_SETTING 0x07
+#define AX_FW_MODE 0x08
+#define AX88179A_FLASH_READ 0x21
+#define AX88179A_FLASH_WRITE 0x24
+#define AX88179A_ACCESS_BL 0x2A
+#define AX88179A_PHY_POWER 0x31
+#define AX88179A_AUTODETACH 0xC0
#define AX_PAUSE_WATERLVL_LOW 0x54
#define AX_PAUSE_WATERLVL_HIGH 0x55
+#define AX_FW_MODE_179A 0x01
#define PHYSICAL_LINK_STATUS 0x02
#define AX_USB_SS 0x04
#define AX_USB_HS 0x02
+ #define AX_USB_FS 0x01
#define GENERAL_STATUS 0x03
/* Check AX88179 version. UA1:Bit2 = 0, UA2:Bit2 = 1 */
#define AX_SECLD 0x04
+#define AX_CHIP_STATUS 0x05
+
#define AX_SROM_ADDR 0x07
#define AX_SROM_CMD 0x0a
#define EEP_RD 0x04
@@ -62,6 +74,15 @@
#define AX_RX_CTL_PRO 0x0001
#define AX_RX_CTL_STOP 0x0000
+#define AX88179A_ETH_TX_GAP 0x0D
+
+#define AX88179A_BFM_DATA 0x0E
+ #define AX_TX_QUEUE_CFG 0x02
+ #define AX_TX_QUEUE_SET 0x08
+ #define AX_TX_Q1_AHB_FC_EN 0x10
+ #define AX_TX_Q2_AHB_FC_EN 0x20
+ #define AX_XGMII_EN 0x80
+
#define AX_NODE_ID 0x10
#define AX_MULFLTARY 0x16
@@ -111,7 +132,51 @@
#define AX_TXCOE_TCPV6 0x20
#define AX_TXCOE_UDPV6 0x40
+#define AX88179A_MAC_BM_INT_MASK 0x41
+#define AX88179A_MAC_BM_RX_DMA_CTL 0x43
+#define AX88179A_MAC_BM_TX_DMA_CTL 0x46
+
+#define AX88179A_MAC_RX_STATUS_CDC 0x6D
+ #define AX_LSOFC_WCNT_7_ACCESS 0x03
+ #define AX_GMII_CRC_APPEND 0x10
+
#define AX_LEDCTRL 0x73
+#define AX88179A_MAC_ARC_CTRL 0x9E
+#define AX88179A_MAC_SWP_CTRL 0xB1
+
+#define AX88179A_MAC_TX_PAUSE 0xB2
+
+#define AX88179A_MAC_CDC_DELAY_TX 0xB5
+
+#define AX88179A_MAC_PATH 0xB7
+ #define AX_MAC_RX_PATH_READY 0x01
+ #define AX_MAC_TX_PATH_READY 0x02
+
+#define AX88179A_NEW_PAUSE_CTRL 0xB8
+ #define AX_NEW_PAUSE_EN 0x01
+
+#define AX88179A_MAC_BULK_OUT_CTRL 0xB9
+ #define AX_MAC_EFF_EN 0x02
+
+#define AX88179A_MAC_RX_DATA_CDC_CNT 0xC0
+ #define AX_MAC_LSO_ERR_EN 0x04
+ #define AX_MAC_MIQFFCTRL_FORMAT 0x10
+ #define AX_MAC_MIQFFCTRL_DROP_CRC 0x20
+
+#define AX88179A_AUTODETACH_DELAY (5UL << 8)
+#define AX88179A_AUTODETACH_EN 1
+
+#define AX88179A_MAC_LSO_ENHANCE_CTRL 0xC3
+ #define AX_LSO_ENHANCE_EN 0x01
+
+#define AX88179A_MAC_TX_HDR_CKSUM 0xCC
+#define AX88179A_EP5_EHR 0xF9
+
+#define AX_PHY_POWER 0x02
+
+#define EPHY_LOW_POWER_EN 0x01
+#define S5_WOL_EN 0x04
+#define S5_WOL_LOW_POWER 0x20
#define GMII_PHY_PHYSR 0x11
#define GMII_PHY_PHYSR_SMASK 0xc000
@@ -164,8 +229,56 @@
#define GMII_PHY_PGSEL_PAGE3 0x0003
#define GMII_PHY_PGSEL_PAGE5 0x0005
+/* TX Descriptor */
+#define AX179A_TX_DESC_LEN_MASK 0x1FFFFF
+#define AX179A_TX_DESC_DROP_PADD BIT(28)
+#define AX179A_TX_DESC_VLAN BIT(29)
+#define AX179A_TX_DESC_MSS_MASK 0x7FFF
+#define AX179A_TX_DESC_MSS_SHIFT 0x20
+#define AX179A_TX_DESC_VLAN_MASK 0xFFFF
+#define AX179A_TX_DESC_VLAN_SHIFT 0x30
+
+/* RX Packet Descriptor */
+#define AX179A_RX_PD_L4_ERR BIT(0)
+#define AX179A_RX_PD_L3_ERR BIT(1)
+#define AX179A_RX_PD_L4_TYPE_MASK 0x1C
+#define AX179A_RX_PD_L4_UDP 0x04
+#define AX179A_RX_PD_L4_TCP 0x10
+#define AX179A_RX_PD_L3_TYPE_MASK 0x60
+#define AX179A_RX_PD_L3_IP 0x20
+#define AX179A_RX_PD_L3_IP6 0x40
+
+#define AX179A_RX_PD_VLAN BIT(10)
+#define AX179A_RX_PD_RX_OK BIT(11)
+#define AX179A_RX_PD_DROP BIT(31)
+#define AX179A_RX_PD_LEN_MASK 0x7FFF0000
+#define AX179A_RX_PD_LEN_SHIFT 0x10
+#define AX179A_RX_PD_VLAN_SHIFT 0x20
+
+/* RX Descriptor header */
+#define AX179A_RX_DH_PKT_CNT_MASK 0x1FFF
+#define AX179A_RX_DH_DESC_OFFSET_MASK 0xFFFFE000
+#define AX179A_RX_DH_DESC_OFFSET_SHIFT 0x0D
+
+#define AX179A_RX_HW_PAD 0x02
+
static int ax88179_reset(struct usbnet *dev);
+enum ax_ether_link_speed {
+ ETHER_LINK_NONE = 0,
+ ETHER_LINK_10 = 1,
+ ETHER_LINK_100 = 2,
+ ETHER_LINK_1000 = 3,
+ ETHER_LINK_2500 = 4,
+};
+
+enum ax_chip_version {
+ AX_VERSION_INVALID = 0x0,
+ AX_VERSION_AX88179 = 0x4,
+ AX_VERSION_AX88179A = 0x6, /* Also AX88772D */
+ AX_VERSION_AX88279 = 0x7,
+};
+
struct ax88179_data {
u8 eee_enabled;
u8 eee_active;
@@ -174,6 +287,16 @@ struct ax88179_data {
u32 wol_supported;
u32 wolopts;
u8 disconnecting;
+ u8 chip_version;
+ u8 fw_version[4];
+ u8 is_ax88772d;
+ u8 ip_align;
+ u8 link;
+ u8 speed;
+ u8 full_duplex;
+ u8 rx_checksum;
+ u8 eeprom_read_cmd;
+ u16 eeprom_block;
};
struct ax88179_int_data {
@@ -181,15 +304,48 @@ struct ax88179_int_data {
__le32 intdata2;
};
-static const struct {
+struct ax_bulkin_settings {
unsigned char ctrl, timer_l, timer_h, size, ifg;
-} AX88179_BULKIN_SIZE[] = {
+};
+
+static const struct ax_bulkin_settings AX88179_BULKIN_SIZE[] = {
{7, 0x4f, 0, 0x12, 0xff},
{7, 0x20, 3, 0x16, 0xff},
{7, 0xae, 7, 0x18, 0xff},
{7, 0xcc, 0x4c, 0x18, 8},
};
+static const struct ax_bulkin_settings AX88179A_BULKIN_SIZE[] = {
+ {5, 0x7B, 0x00, 0x17, 0x0F}, /* 1G, SS */
+ {5, 0xC0, 0x02, 0x06, 0x0F}, /* 1G, HS */
+ {7, 0xF0, 0x00, 0x0C, 0x0F}, /* 100M, Full, SS */
+ {6, 0x00, 0x00, 0x06, 0x0F}, /* 100M, Half, SS */
+ {5, 0xC0, 0x04, 0x06, 0x0F}, /* 100M, Full, HS */
+ {7, 0xC0, 0x04, 0x06, 0x0F}, /* 100M, Half, HS */
+ {7, 0x00, 0x00, 0x03, 0x3F}, /* FS */
+};
+
+static const struct ax_bulkin_settings AX88772D_BULKIN_SIZE[] = {
+ {0, 0x00, 0x00, 0x00, 0x00}, /* 1G, SS (unused) */
+ {0, 0x00, 0x00, 0x00, 0x00}, /* 1G, HS (unused) */
+ {0, 0x00, 0x00, 0x00, 0x00}, /* 100M, Full, SS (unused) */
+ {0, 0x00, 0x00, 0x00, 0x00}, /* 100M, Half, SS (unused) */
+ {5, 0xC0, 0x04, 0x06, 0x0F}, /* 100M, Full, HS */
+ {7, 0xC0, 0x04, 0x06, 0x0F}, /* 100M, Half, HS */
+ {7, 0x00, 0x00, 0x03, 0x3F}, /* FS */
+};
+
+static const struct ax_bulkin_settings AX88279_BULKIN_SIZE[] = {
+ {5, 0x10, 0x01, 0x11, 0x0F}, /* 2.5G */
+ {7, 0xB3, 0x01, 0x11, 0x0F}, /* 1G, SS */
+ {7, 0xC0, 0x02, 0x06, 0x0F}, /* 1G, HS */
+ {7, 0x80, 0x01, 0x03, 0x0F}, /* 100M, Full, SS */
+ {7, 0x80, 0x01, 0x03, 0x0F}, /* 100M, Half, SS */
+ {7, 0x80, 0x01, 0x03, 0x0F}, /* 100M, Full, HS */
+ {7, 0x80, 0x01, 0x03, 0x0F}, /* 100M, Half, HS */
+ {7, 0x00, 0x00, 0x03, 0x3F}, /* FS */
+};
+
static void ax88179_set_pm_mode(struct usbnet *dev, bool pm_mode)
{
struct ax88179_data *ax179_data = dev->driver_priv;
@@ -414,7 +570,6 @@ static int ax88179_suspend(struct usb_interface *intf, pm_message_t message)
usbnet_suspend(intf, message);
- /* Enable WoL */
if (priv->wolopts) {
ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_MONITOR_MOD,
1, 1, &tmp8);
@@ -425,6 +580,26 @@ static int ax88179_suspend(struct usb_interface *intf, pm_message_t message)
ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MONITOR_MOD,
1, 1, &tmp8);
+
+ if (priv->chip_version >= AX_VERSION_AX88179A) {
+ ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE, 2, 2, &tmp16);
+ tmp16 |= AX_MEDIUM_RECEIVE_EN;
+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE, 2, 2, &tmp16);
+ }
+
+ if (priv->chip_version == AX_VERSION_AX88279)
+ ax88179_write_cmd(dev, AX88179A_WAKEUP_SETTING, 8,
+ EPHY_LOW_POWER_EN | S5_WOL_EN
+ | S5_WOL_LOW_POWER | 0x8000, 0, NULL);
+
+ } else if (priv->chip_version == AX_VERSION_AX88279) {
+ ax88179_write_cmd(dev, AX88179A_WAKEUP_SETTING, 8, 0x8000, 0, NULL);
+ }
+
+ if (priv->chip_version >= AX_VERSION_AX88179A) {
+ ax88179_write_cmd(dev, AX88179A_WAKEUP_SETTING, 0, EPHY_LOW_POWER_EN, 0, NULL);
+ ax88179_set_pm_mode(dev, false);
+ return 0;
}
/* Disable RX path */
@@ -436,11 +611,11 @@ static int ax88179_suspend(struct usb_interface *intf, pm_message_t message)
/* Force bulk-in zero length */
ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL,
- 2, 2, &tmp16);
+ 2, 2, &tmp16);
tmp16 |= AX_PHYPWR_RSTCTL_BZ | AX_PHYPWR_RSTCTL_IPRL;
ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL,
- 2, 2, &tmp16);
+ 2, 2, &tmp16);
/* change clock */
tmp8 = 0;
@@ -456,12 +631,19 @@ static int ax88179_suspend(struct usb_interface *intf, pm_message_t message)
}
/* This function is used to enable the autodetach function. */
-/* This function is determined by offset 0x43 of EEPROM */
+/* This function is determined by offset 0x43 of EEPROM for the AX88179 */
static int ax88179_auto_detach(struct usbnet *dev)
{
+ struct ax88179_data *priv = dev->driver_priv;
u16 tmp16;
u8 tmp8;
+ if (priv->chip_version >= AX_VERSION_AX88179A) {
+ tmp16 = AX88179A_AUTODETACH_DELAY;
+ ax88179_write_cmd(dev, AX88179A_AUTODETACH, tmp16, 0, 0, NULL);
+ return 0;
+ }
+
if (ax88179_read_cmd(dev, AX_ACCESS_EEPROM, 0x43, 1, 2, &tmp16) < 0)
return 0;
@@ -484,11 +666,31 @@ static int ax88179_auto_detach(struct usbnet *dev)
static int ax88179_resume(struct usb_interface *intf)
{
struct usbnet *dev = usb_get_intfdata(intf);
+ struct ax88179_data *ax179_data;
+ u8 reg8;
+ ax179_data = dev->driver_priv;
ax88179_set_pm_mode(dev, true);
usbnet_link_change(dev, 0, 0);
+ if (ax179_data->chip_version >= AX_VERSION_AX88179A) {
+ ax88179_read_cmd(dev, AX88179A_PHY_POWER, 0, 0, 1, ®8);
+ if (!(reg8 & AX_PHY_POWER)) {
+ reg8 = AX_PHY_POWER;
+ ax88179_write_cmd(dev, AX88179A_PHY_POWER, 0, 0, 1, ®8);
+ msleep(250);
+ }
+ ax88179_write_cmd(dev, AX_FW_MODE, AX_FW_MODE_179A, 0, 0, NULL);
+
+ /* Now, that AX_FW_MODE_179A is enabled, the PHY needs a power-cycle.
+ * PHY-power is re-enabled in ax88179_reset()
+ */
+ reg8 = 0;
+ ax88179_write_cmd(dev, AX88179A_PHY_POWER, 0, 0, 1, ®8);
+ msleep(250);
+ }
+
ax88179_reset(dev);
ax88179_set_pm_mode(dev, false);
@@ -1293,6 +1495,17 @@ static int ax88179_bind(struct usbnet *dev, struct usb_interface *intf)
dev->driver_priv = ax179_data;
+ ret = ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_CHIP_STATUS,
+ 1, 1, &ax179_data->chip_version);
+ if (ret < 0)
+ goto err_nodev;
+
+ ax179_data->chip_version = (ax179_data->chip_version & 0xf0) >> 4;
+ ax179_data->is_ax88772d = 0;
+ ax179_data->ip_align = 1;
+ ax179_data->eeprom_read_cmd = AX_ACCESS_EEPROM;
+ ax179_data->eeprom_block = 2;
+
dev->net->netdev_ops = &ax88179_netdev_ops;
dev->net->ethtool_ops = &ax88179_ethtool_ops;
dev->net->needed_headroom = 8;
@@ -1317,6 +1530,120 @@ static int ax88179_bind(struct usbnet *dev, struct usb_interface *intf)
ax88179_reset(dev);
return 0;
+
+err_nodev:
+ kfree(ax179_data);
+ ax179_data = NULL;
+
+ return ret;
+}
+
+static int ax88179a_bind(struct usbnet *dev, struct usb_interface *intf)
+{
+ struct usb_device *udev = interface_to_usbdev(intf);
+ struct ax88179_data *ax179_data;
+ int ret;
+
+ /* Check if vendor configuration */
+ if (udev->actconfig->desc.bConfigurationValue != 1) {
+ netdev_info(dev->net, "Switching to vendor mode\n");
+ usb_driver_set_configuration(udev, 1);
+ return -ENODEV;
+ }
+
+ ret = usbnet_get_endpoints(dev, intf);
+ if (ret < 0)
+ return ret;
+
+ ax179_data = kzalloc_obj(*ax179_data);
+ if (!ax179_data)
+ return -ENOMEM;
+
+ dev->driver_priv = ax179_data;
+
+ ret = ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_CHIP_STATUS,
+ 1, 1, &ax179_data->chip_version);
+ if (ret < 0)
+ goto err_nodev;
+
+ ax179_data->chip_version = (ax179_data->chip_version & 0xf0) >> 4;
+ ax179_data->is_ax88772d = 0;
+ if (ax179_data->chip_version == AX_VERSION_AX88179A) {
+ if (udev->descriptor.bcdDevice == 0x300)
+ ax179_data->is_ax88772d = 1;
+ }
+
+ for (int i = 0; i < 3; i++) {
+ ret = ax88179_read_cmd(dev, AX88179A_ACCESS_BL, (0xFD + i),
+ 1, 1, &ax179_data->fw_version[i]);
+ if (ret < 0)
+ ax179_data->fw_version[i] = 0xff;
+ }
+ netdev_info(dev->net, "AX88179A/279/772D Chip Version: %x, FW: %d.%d.%d.%d\n",
+ ax179_data->chip_version,
+ ax179_data->fw_version[0], ax179_data->fw_version[1],
+ ax179_data->fw_version[2], ax179_data->fw_version[3]);
+
+ /* The AX88279 requires both the AX_RX_CTL_IPE and AX_RX_CTL_DROPCRCERR
+ * bits set in AX_RX_CTL for creating correct RX-URBs. AX_RX_CTL_DROPCRCERR
+ * is anyway set for all chips, make sure AX_RX_CTL_IPE is set via ip_align.
+ * Also configure eeprom access parameters.
+ */
+ if (ax179_data->chip_version == AX_VERSION_AX88279) {
+ ax179_data->ip_align = 1;
+ ax179_data->eeprom_read_cmd = AX88179A_FLASH_READ;
+ ax179_data->eeprom_block = 256;
+ } else {
+ ax179_data->ip_align = 0;
+ ax179_data->eeprom_read_cmd = AX_ACCESS_EFUS;
+ ax179_data->eeprom_block = 20;
+ }
+
+ dev->net->netdev_ops = &ax88179_netdev_ops;
+ dev->net->ethtool_ops = &ax88179_ethtool_ops;
+ dev->net->needed_headroom = 8;
+ dev->net->needed_tailroom = 8;
+ dev->net->min_mtu = ETH_MIN_MTU;
+ dev->hard_mtu = 9 * 1024;
+ dev->net->max_mtu = dev->hard_mtu - dev->net->hard_header_len;
+
+ /* Initialize MII structure */
+ dev->mii.dev = dev->net;
+ dev->mii.mdio_read = ax88179_mdio_read;
+ dev->mii.mdio_write = ax88179_mdio_write;
+ dev->mii.phy_id_mask = 0xff;
+ dev->mii.reg_num_mask = 0xff;
+ dev->mii.phy_id = 0x03;
+ if (!ax179_data->is_ax88772d)
+ dev->mii.supports_gmii = 1;
+
+ dev->net->features |= NETIF_F_SG | NETIF_F_IP_CSUM |
+ NETIF_F_IPV6_CSUM | NETIF_F_RXCSUM | NETIF_F_TSO |
+ NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX |
+ NETIF_F_HW_VLAN_CTAG_FILTER;
+
+ dev->net->hw_features |= dev->net->features;
+
+ dev->net->vlan_features = NETIF_F_SG | NETIF_F_IP_CSUM |
+ NETIF_F_IPV6_CSUM | NETIF_F_RXCSUM | NETIF_F_TSO;
+
+ netif_set_tso_max_size(dev->net, 16384);
+
+ /* Enable Transmission of Link Speed byte in interrupt URB */
+ ax88179_write_cmd(dev, AX_FW_MODE, AX_FW_MODE_179A, 0, 0, NULL);
+ ax88179_write_cmd(dev, AX_RELOAD_EEPROM_EFUSE, 0, 0, 0, NULL);
+
+ /* Read MAC address from DTB or ASIX chip */
+ ax88179_get_mac_addr(dev);
+ memcpy(dev->net->perm_addr, dev->net->dev_addr, ETH_ALEN);
+
+ return 0;
+
+err_nodev:
+ kfree(ax179_data);
+ ax179_data = NULL;
+
+ return ret;
}
static void ax88179_unbind(struct usbnet *dev, struct usb_interface *intf)
@@ -1338,6 +1665,22 @@ static void ax88179_unbind(struct usbnet *dev, struct usb_interface *intf)
kfree(ax179_data);
}
+static void ax88179a_unbind(struct usbnet *dev, struct usb_interface *intf)
+{
+ struct ax88179_data *ax179_data = dev->driver_priv;
+ u16 tmp16;
+ u8 tmp8;
+
+ /* Configure RX control register => stop operation */
+ tmp16 = AX_RX_CTL_STOP;
+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RX_CTL, 2, 2, &tmp16);
+
+ tmp8 = 0;
+ ax88179_write_cmd(dev, AX88179A_PHY_POWER, 0, 0, 1, &tmp8);
+
+ kfree(ax179_data);
+}
+
static void
ax88179_rx_checksum(struct sk_buff *skb, u32 *pkt_hdr)
{
@@ -1354,6 +1697,21 @@ ax88179_rx_checksum(struct sk_buff *skb, u32 *pkt_hdr)
skb->ip_summed = CHECKSUM_UNNECESSARY;
}
+static void ax88179a_rx_checksum(struct sk_buff *skb, u64 pkt_desc)
+{
+ u32 pkt_type;
+
+ skb->ip_summed = CHECKSUM_NONE;
+ /* checksum error bit is set */
+ if (pkt_desc & AX179A_RX_PD_L4_ERR || pkt_desc & AX179A_RX_PD_L3_ERR)
+ return;
+
+ pkt_type = pkt_desc & AX179A_RX_PD_L4_TYPE_MASK;
+ /* It must be a TCP or UDP packet with a valid checksum */
+ if (pkt_type == AX179A_RX_PD_L4_TCP || pkt_type == AX179A_RX_PD_L4_UDP)
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+}
+
static int ax88179_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
{
struct sk_buff *ax_skb;
@@ -1472,6 +1830,121 @@ static int ax88179_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
return 0;
}
+static int ax88179a_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
+{
+ struct ax88179_data *ax179_data = dev->driver_priv;
+ struct sk_buff *ax_skb;
+ u32 hdr_off, pkt_end;
+ u64 *pkt_desc_ptr;
+ u16 vlan_tag;
+ u16 pkt_cnt;
+ u64 rx_hdr;
+
+ /* SKB contents for AX179A-based chips:
+ * <packet 1>
+ * ...
+ * <packet N>
+ * <per-packet metadata entry 1>
+ * ...
+ * <per-packet metadata entry N>
+ * <rx_hdr>
+ *
+ * where:
+ * <packet N> contains pkt_len data bytes and padding:
+ * 2 bytes of IP alignment (optional, depends on AX_RX_CTL_IPE flag)
+ * packet data received
+ * optional padding to 8-bytes boundary
+ * <per-packet metadata entry N> contains 8 bytes:
+ * pkt_len and fields AX_RXHDR_*
+ * <rx-hdr> contains 8 bytes:
+ * pkt_cnt and hdr_off (offset of <per-packet metadata entry 1>)
+ *
+ * pkt_cnt is number of entries in the per-packet metadata array.
+ */
+
+ if (!skb || skb->len < sizeof(rx_hdr))
+ goto err;
+
+ /* RX Descriptor Header */
+ skb_trim(skb, skb->len - sizeof(rx_hdr));
+ rx_hdr = le64_to_cpup((u64 *)skb_tail_pointer(skb));
+
+ /* Check these packets */
+ hdr_off = (rx_hdr & AX179A_RX_DH_DESC_OFFSET_MASK) >> AX179A_RX_DH_DESC_OFFSET_SHIFT;
+ pkt_cnt = rx_hdr & AX179A_RX_DH_PKT_CNT_MASK;
+
+ /* Consistency check header position */
+ if (hdr_off != skb->len - (pkt_cnt * sizeof(rx_hdr)))
+ goto err;
+
+ /* Make sure that the bounds of the metadata array are inside the SKB
+ * (and in front of the counter at the end).
+ */
+ if (pkt_cnt * 8 + hdr_off > skb->len)
+ goto err;
+
+ /* Packets must not overlap the metadata array */
+ skb_trim(skb, hdr_off);
+
+ if (!pkt_cnt)
+ goto err;
+
+ /* Get the first RX packet descriptor */
+ pkt_desc_ptr = (u64 *)(skb->data + hdr_off);
+
+ pkt_end = 0;
+ while (pkt_cnt--) {
+ u64 pkt_desc = le64_to_cpup(pkt_desc_ptr);
+ u32 pkt_len_plus_padd;
+ u32 pkt_len;
+
+ pkt_len = (u32)((pkt_desc & AX179A_RX_PD_LEN_MASK) >> AX179A_RX_PD_LEN_SHIFT)
+ - (ax179_data->ip_align ? 2 : 0);
+ pkt_len_plus_padd = ((pkt_len + 7 + (ax179_data->ip_align ? 2 : 0)) & 0x7FFF8);
+
+ pkt_end += pkt_len_plus_padd;
+ if (pkt_end > hdr_off || (pkt_cnt == 0 && pkt_end != hdr_off))
+ goto err;
+
+ if (pkt_desc & AX179A_RX_PD_DROP || !(pkt_desc & AX179A_RX_PD_RX_OK) ||
+ pkt_len > (dev->hard_mtu + AX179A_RX_HW_PAD)) {
+ skb_pull(skb, pkt_len_plus_padd);
+
+ /* Next RX Packet Descriptor */
+ pkt_desc_ptr++;
+ continue;
+ }
+
+ ax_skb = netdev_alloc_skb_ip_align(dev->net, pkt_len);
+ if (!ax_skb)
+ goto err;
+
+ skb_put(ax_skb, pkt_len);
+ memcpy(ax_skb->data, skb->data + (ax179_data->ip_align ? AX179A_RX_HW_PAD : 0),
+ pkt_len);
+
+ if (ax179_data->rx_checksum)
+ ax88179a_rx_checksum(ax_skb, pkt_desc);
+
+ if (pkt_desc & AX179A_RX_PD_VLAN) {
+ vlan_tag = pkt_desc >> AX179A_RX_PD_VLAN_SHIFT;
+ __vlan_hwaccel_put_tag(ax_skb, htons(ETH_P_8021Q),
+ vlan_tag & VLAN_VID_MASK);
+ }
+
+ usbnet_skb_return(dev, ax_skb);
+ skb_pull(skb, pkt_len_plus_padd);
+
+ /* Next RX Packet Header */
+ pkt_desc_ptr++;
+ }
+
+ return 1;
+
+err:
+ return 0;
+}
+
static struct sk_buff *
ax88179_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags)
{
@@ -1505,6 +1978,59 @@ ax88179_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags)
return skb;
}
+static struct sk_buff *
+ax88179a_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags)
+{
+ u64 tx_desc = skb->len & AX179A_TX_DESC_LEN_MASK;
+ int frame_size = dev->maxpacket;
+ struct sk_buff *ax_skb;
+ u64 *tx_desc_ptr;
+ int padding_size;
+ int headroom;
+ int tailroom;
+ u16 tci = 0;
+
+ /* TSO MSS */
+ tx_desc |= ((u64)(skb_shinfo(skb)->gso_size & AX179A_TX_DESC_MSS_MASK)) <<
+ AX179A_TX_DESC_MSS_SHIFT;
+
+ headroom = (skb->len + sizeof(tx_desc)) % 8;
+ padding_size = headroom ? 8 - headroom : 0;
+
+ if (((skb->len + sizeof(tx_desc) + padding_size) % frame_size) == 0) {
+ padding_size += 8;
+ tx_desc |= AX179A_TX_DESC_DROP_PADD;
+ }
+
+ if ((dev->net->features & NETIF_F_HW_VLAN_CTAG_TX) && (vlan_get_tag(skb, &tci) >= 0)) {
+ tx_desc |= AX179A_TX_DESC_VLAN;
+ tx_desc |= ((u64)tci & AX179A_TX_DESC_VLAN_MASK) << AX179A_TX_DESC_VLAN_SHIFT;
+ }
+
+ if (!dev->can_dma_sg && (dev->net->features & NETIF_F_SG) && skb_linearize(skb))
+ return NULL;
+
+ headroom = skb_headroom(skb);
+ tailroom = skb_tailroom(skb);
+
+ if (!(headroom >= sizeof(tx_desc) && tailroom >= padding_size)) {
+ ax_skb = skb_copy_expand(skb, sizeof(tx_desc), padding_size, flags);
+ dev_kfree_skb_any(skb);
+ skb = ax_skb;
+ if (!skb)
+ return NULL;
+ }
+ if (padding_size != 0)
+ skb_put_zero(skb, padding_size);
+ /* Copy TX header */
+ tx_desc_ptr = skb_push(skb, sizeof(tx_desc));
+ *tx_desc_ptr = cpu_to_le64(tx_desc);
+
+ usbnet_set_skb_tx_stats(skb, 1, 0);
+
+ return skb;
+}
+
static int ax88179_link_reset(struct usbnet *dev)
{
struct ax88179_data *ax179_data = dev->driver_priv;
@@ -1580,72 +2106,343 @@ static int ax88179_link_reset(struct usbnet *dev)
return 0;
}
+static void ax88179a_bulkin_config(struct usbnet *dev, u8 link_sts)
+{
+ struct ax88179_data *ax179_data = dev->driver_priv;
+ const struct ax_bulkin_settings *bulkin_data;
+ int index = 0;
+
+ switch (ax179_data->speed) {
+ case ETHER_LINK_2500: /* AX88279 only */
+ index = 0;
+ break;
+
+ case ETHER_LINK_1000: /* AX88279 & AX88178A */
+ if (ax179_data->chip_version == AX_VERSION_AX88279) {
+ if (link_sts & AX_USB_SS)
+ index = 1;
+ else if (link_sts & AX_USB_HS)
+ index = 2;
+ } else {
+ if (link_sts & AX_USB_SS)
+ index = 0;
+ else if (link_sts & AX_USB_HS)
+ index = 1;
+ }
+ break;
+
+ case ETHER_LINK_100:
+ if (ax179_data->chip_version == AX_VERSION_AX88279) {
+ if (link_sts & AX_USB_SS)
+ index = 3;
+ else if (link_sts & AX_USB_HS)
+ index = 5;
+ if (!ax179_data->full_duplex)
+ index++;
+ } else {
+ /* AX88279A & AX88277D */
+ if (link_sts & AX_USB_SS)
+ index = 2;
+ else if (link_sts & AX_USB_HS)
+ index = 4;
+ if (!ax179_data->full_duplex)
+ index++;
+ }
+ break;
+
+ case ETHER_LINK_10:
+ if (ax179_data->chip_version == AX_VERSION_AX88279)
+ index = 7;
+ else
+ index = 6;
+ break;
+
+ default: /* No link */
+ index = 0;
+ }
+
+ if (ax179_data->chip_version == AX_VERSION_AX88279 && (link_sts & AX_USB_FS))
+ index = 7;
+
+ if (ax179_data->chip_version == AX_VERSION_AX88279) {
+ bulkin_data = AX88279_BULKIN_SIZE;
+ } else {
+ if (ax179_data->is_ax88772d)
+ bulkin_data = AX88772D_BULKIN_SIZE;
+ else
+ bulkin_data = AX88179A_BULKIN_SIZE;
+ }
+
+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RX_BULKIN_QCTRL, 5, 5, &bulkin_data[index]);
+}
+
+static int ax88179a_link_reset(struct usbnet *dev)
+{
+ struct ax88179_data *ax179_data = dev->driver_priv;
+ u8 tmp8, link_sts, reg8[3];
+ u16 tmp16, mode, speed;
+
+ if (!ax179_data->link) {
+ netdev_info(dev->net, "ax88179a - Link status is: 0\n");
+ return 0;
+ }
+
+ /* Stop RX/TX for link configuration */
+ tmp16 = AX_RX_CTL_STOP;
+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RX_CTL, 2, 2, &tmp16);
+ tmp8 = 0;
+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_MAC_PATH, 1, 1, &tmp8);
+
+ tmp8 = 0xa5;
+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_MAC_CDC_DELAY_TX, 1, 1, &tmp8);
+
+ tmp16 = 0x0410;
+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PAUSE_WATERLVL_LOW, 2, 2, &tmp16);
+
+ tmp8 = 0;
+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_ETH_TX_GAP, 1, 1, &tmp8);
+
+ tmp8 = 0x07;
+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_EP5_EHR, 1, 1, &tmp8);
+
+ tmp8 = 0x28 | AX_NEW_PAUSE_EN;
+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_NEW_PAUSE_CTRL, 1, 1, &tmp8);
+
+ mode = AX_MEDIUM_RECEIVE_EN | AX_MEDIUM_TXFLOW_CTRLEN | AX_MEDIUM_RXFLOW_CTRLEN;
+
+ /* Link is up, but some older AX88179A FW versions do not send link speed
+ * and duplex status in interrupt URB, so read it via MII
+ */
+ if (!ax179_data->speed) {
+ struct ethtool_link_ksettings cmd;
+
+ mii_ethtool_get_link_ksettings(&dev->mii, &cmd);
+ ax179_data->full_duplex = cmd.base.duplex;
+ switch (cmd.base.speed) {
+ case SPEED_1000:
+ ax179_data->speed = ETHER_LINK_1000;
+ break;
+ case SPEED_100:
+ ax179_data->speed = ETHER_LINK_100;
+ break;
+ case SPEED_10:
+ default:
+ ax179_data->speed = ETHER_LINK_10;
+ break;
+ };
+ }
+
+ speed = 0;
+ switch (ax179_data->speed) {
+ case ETHER_LINK_2500:
+ reg8[0] = 0x00;
+ reg8[1] = 0xF8;
+ reg8[2] = 0x07;
+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_MAC_TX_PAUSE, 3, 3, reg8);
+
+ reg8[0] = 0x78;
+ reg8[1] = (AX_LSOFC_WCNT_7_ACCESS << 5);
+ reg8[2] = 0;
+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_MAC_RX_STATUS_CDC, 3, 3, reg8);
+
+ reg8[0] = 0x40;
+ reg8[1] = AX_MAC_MIQFFCTRL_FORMAT | AX_MAC_MIQFFCTRL_DROP_CRC | AX_MAC_LSO_ERR_EN;
+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_MAC_RX_DATA_CDC_CNT, 2, 2, reg8);
+
+ tmp8 = AX_XGMII_EN;
+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_BFM_DATA, 1, 1, &tmp8);
+
+ tmp8 = 0x1C | AX_LSO_ENHANCE_EN;
+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_MAC_LSO_ENHANCE_CTRL, 1, 1, &tmp8);
+
+ mode |= AX_MEDIUM_GIGAMODE | AX_MEDIUM_FULL_DUPLEX;
+
+ speed = 2500;
+ break;
+
+ case ETHER_LINK_1000:
+ mode |= AX_MEDIUM_GIGAMODE;
+ speed = 1000;
+ fallthrough;
+
+ case ETHER_LINK_100:
+ reg8[0] = 0x78;
+ reg8[1] = (AX_LSOFC_WCNT_7_ACCESS << 5) | AX_GMII_CRC_APPEND;
+ reg8[2] = 0;
+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_MAC_RX_STATUS_CDC, 3, 3, reg8);
+
+ tmp8 = 0x40;
+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_MAC_RX_DATA_CDC_CNT, 1, 1, &tmp8);
+
+ speed = speed ? speed : 100;
+ break;
+
+ case ETHER_LINK_10:
+ reg8[0] = 0xFA;
+ reg8[1] = (AX_LSOFC_WCNT_7_ACCESS << 5) | AX_GMII_CRC_APPEND;
+ reg8[2] = 0xFF;
+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_MAC_RX_STATUS_CDC, 3, 3, reg8);
+
+ tmp8 = 0xFA;
+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_MAC_RX_DATA_CDC_CNT, 1, 1, &tmp8);
+
+ speed = 10;
+ break;
+ }
+
+ ax88179_read_cmd(dev, AX_ACCESS_MAC, PHYSICAL_LINK_STATUS, 1, 1, &link_sts);
+ ax88179a_bulkin_config(dev, link_sts);
+
+ if (ax179_data->chip_version < AX_VERSION_AX88279) {
+ tmp8 = 0;
+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_BFM_DATA, 1, 1, &tmp8);
+ }
+
+ if (ax179_data->full_duplex)
+ mode |= AX_MEDIUM_FULL_DUPLEX;
+
+ if (dev->net->mtu > 1500)
+ mode |= AX_MEDIUM_JUMBO_EN;
+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE, 2, 2, &mode);
+
+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RX_CTL, 2, 2, &ax179_data->rxctl);
+
+ tmp8 = AX_MAC_RX_PATH_READY | AX_MAC_TX_PATH_READY;
+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_MAC_PATH, 1, 1, &tmp8);
+
+ ax179_data->eee_enabled = ax88179_chk_eee(dev);
+
+ netif_carrier_on(dev->net);
+
+ netdev_info(dev->net, "ax88179a - Link status is: 1, Link speed: %d, Duplex: %d\n",
+ speed, ax179_data->full_duplex);
+
+ return 0;
+}
+
static int ax88179_reset(struct usbnet *dev)
{
- u8 buf[5];
- u16 *tmp16;
- u8 *tmp;
struct ax88179_data *ax179_data = dev->driver_priv;
struct ethtool_keee eee_data;
+ u16 *tmp16;
+ u8 buf[5];
+ u8 *tmp;
tmp16 = (u16 *)buf;
tmp = (u8 *)buf;
/* Power up ethernet PHY */
- *tmp16 = 0;
- ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL, 2, 2, tmp16);
+ if (ax179_data->chip_version < AX_VERSION_AX88179A) {
+ *tmp16 = 0;
+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL, 2, 2, tmp16);
- *tmp16 = AX_PHYPWR_RSTCTL_IPRL;
- ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL, 2, 2, tmp16);
- msleep(500);
+ *tmp16 = AX_PHYPWR_RSTCTL_IPRL;
+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL, 2, 2, tmp16);
+ msleep(500);
- *tmp = AX_CLK_SELECT_ACS | AX_CLK_SELECT_BCS;
- ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_CLK_SELECT, 1, 1, tmp);
- msleep(200);
+ *tmp = AX_CLK_SELECT_ACS | AX_CLK_SELECT_BCS;
+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_CLK_SELECT, 1, 1, tmp);
+ msleep(200);
+ } else {
+ *tmp = AX_PHY_POWER;
+ ax88179_write_cmd(dev, AX88179A_PHY_POWER, 0, 0, 1, tmp);
+ msleep(250);
+ }
+
+ if (ax179_data->chip_version == AX_VERSION_AX88279) {
+ *tmp16 = ax88179_mdio_read(dev->net, dev->mii.phy_id, MII_ADVERTISE);
+ *tmp16 &= ~(ADVERTISE_10FULL | ADVERTISE_10HALF);
+ *tmp16 |= ADVERTISE_RESV; /* Advertise 2.5GBit link */
+ ax88179_mdio_write(dev->net, dev->mii.phy_id, MII_ADVERTISE, *tmp16);
+ }
/* Ethernet PHY Auto Detach*/
ax88179_auto_detach(dev);
+ if (ax179_data->chip_version >= AX_VERSION_AX88179A) {
+ *tmp = AX_MAC_EFF_EN;
+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_MAC_BULK_OUT_CTRL, 1, 1, tmp);
+
+ *tmp16 = 0;
+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RX_CTL, 2, 2, tmp16);
+
+ *tmp = 0x04;
+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PAUSE_WATERLVL_LOW, 1, 1, tmp);
+ *tmp = 0x10;
+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PAUSE_WATERLVL_HIGH, 1, 1, tmp);
+
+ *tmp = 0;
+ if (dev->net->features & NETIF_F_HW_VLAN_CTAG_FILTER)
+ *tmp |= AX_VLAN_CONTROL_VFE;
+ if (dev->net->features & NETIF_F_HW_VLAN_CTAG_RX)
+ *tmp |= AX_VLAN_CONTROL_VSO;
+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_CONTROL, 1, 1, tmp);
+
+ *tmp = 0xff;
+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_MAC_BM_INT_MASK, 1, 1, tmp);
+
+ *tmp = 0;
+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_MAC_BM_RX_DMA_CTL, 1, 1, tmp);
+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_MAC_BM_TX_DMA_CTL, 1, 1, tmp);
+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_MAC_ARC_CTRL, 1, 1, tmp);
+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_MAC_SWP_CTRL, 1, 1, tmp);
+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_MAC_TX_HDR_CKSUM, 1, 1, tmp);
+ }
+
/* Read MAC address from DTB or asix chip */
ax88179_get_mac_addr(dev);
memcpy(dev->net->perm_addr, dev->net->dev_addr, ETH_ALEN);
/* RX bulk configuration */
- memcpy(tmp, &AX88179_BULKIN_SIZE[0], 5);
- ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RX_BULKIN_QCTRL, 5, 5, tmp);
-
- dev->rx_urb_size = 1024 * 20;
-
- *tmp = 0x34;
- ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PAUSE_WATERLVL_HIGH, 1, 1, tmp);
-
- *tmp = 0x52;
- ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PAUSE_WATERLVL_LOW, 1, 1, tmp);
+ if (ax179_data->chip_version < AX_VERSION_AX88179A) {
+ memcpy(tmp, &AX88179_BULKIN_SIZE[0], 5);
+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RX_BULKIN_QCTRL, 5, 5, tmp);
+ *tmp = 0x34;
+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PAUSE_WATERLVL_LOW, 1, 1, tmp);
+
+ *tmp = 0x52;
+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PAUSE_WATERLVL_HIGH,
+ 1, 1, tmp);
+ dev->rx_urb_size = 1024 * 20;
+ } else {
+ /* The Bulk-Register configuration for the AX88179A is done in
+ * ax88179a_link_reset(), once the link is up for a given link and USB-speed.
+ */
+ if (ax179_data->is_ax88772d)
+ dev->rx_urb_size = 1024 * 24;
+ else
+ dev->rx_urb_size = 1024 * 48;
+ }
/* Enable checksum offload */
*tmp = AX_RXCOE_IP | AX_RXCOE_TCP | AX_RXCOE_UDP |
AX_RXCOE_TCPV6 | AX_RXCOE_UDPV6;
ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RXCOE_CTL, 1, 1, tmp);
+ ax179_data->rx_checksum = 1;
*tmp = AX_TXCOE_IP | AX_TXCOE_TCP | AX_TXCOE_UDP |
AX_TXCOE_TCPV6 | AX_TXCOE_UDPV6;
ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_TXCOE_CTL, 1, 1, tmp);
/* Configure RX control register => start operation */
- *tmp16 = AX_RX_CTL_DROPCRCERR | AX_RX_CTL_IPE | AX_RX_CTL_START |
- AX_RX_CTL_AP | AX_RX_CTL_AMALL | AX_RX_CTL_AB;
- ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RX_CTL, 2, 2, tmp16);
-
- *tmp = AX_MONITOR_MODE_PMETYPE | AX_MONITOR_MODE_PMEPOL |
- AX_MONITOR_MODE_RWMP;
+ ax179_data->rxctl = AX_RX_CTL_DROPCRCERR | AX_RX_CTL_START |
+ AX_RX_CTL_AP | AX_RX_CTL_AMALL | AX_RX_CTL_AB;
+ if (ax179_data->ip_align)
+ ax179_data->rxctl |= AX_RX_CTL_IPE;
+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RX_CTL, 2, 2, &ax179_data->rxctl);
+
+ if (ax179_data->chip_version < AX_VERSION_AX88179A)
+ *tmp = AX_MONITOR_MODE_PMETYPE | AX_MONITOR_MODE_PMEPOL | AX_MONITOR_MODE_RWMP;
+ else
+ *tmp = AX_MONITOR_MODE_RWMP;
ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MONITOR_MOD, 1, 1, tmp);
/* Configure default medium type => giga */
*tmp16 = AX_MEDIUM_RECEIVE_EN | AX_MEDIUM_TXFLOW_CTRLEN |
- AX_MEDIUM_RXFLOW_CTRLEN | AX_MEDIUM_FULL_DUPLEX |
- AX_MEDIUM_GIGAMODE;
- ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE,
- 2, 2, tmp16);
+ AX_MEDIUM_RXFLOW_CTRLEN | AX_MEDIUM_FULL_DUPLEX;
+ if (!ax179_data->is_ax88772d)
+ *tmp16 |= AX_MEDIUM_GIGAMODE;
+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE, 2, 2, tmp16);
/* Check if WoL is supported */
ax179_data->wol_supported = 0;
@@ -1653,7 +2450,11 @@ static int ax88179_reset(struct usbnet *dev)
1, 1, &tmp) > 0)
ax179_data->wol_supported = WAKE_MAGIC | WAKE_PHY;
- ax88179_led_setting(dev);
+ /* For chips starting with AX88179A, LEDS are configured by the adapter
+ * firmware directly from EEPROM/EFUSE values
+ */
+ if (ax179_data->chip_version < AX_VERSION_AX88179A)
+ ax88179_led_setting(dev);
ax179_data->eee_enabled = 0;
ax179_data->eee_active = 0;
@@ -1706,6 +2507,24 @@ static int ax88179_stop(struct usbnet *dev)
return 0;
}
+static int ax88179a_stop(struct usbnet *dev)
+{
+ u16 reg16;
+ u8 reg8;
+
+ ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE, 2, 2, ®16);
+ reg16 &= ~AX_MEDIUM_RECEIVE_EN;
+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE, 2, 2, ®16);
+
+ reg16 = 0;
+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RX_CTL, 2, 2, ®16);
+
+ reg8 = 0;
+ ax88179_read_cmd(dev, AX88179A_PHY_POWER, 0, 0, 1, ®8);
+
+ return 0;
+}
+
static const struct driver_info ax88179_info = {
.description = "ASIX AX88179 USB 3.0 Gigabit Ethernet",
.bind = ax88179_bind,
@@ -1732,6 +2551,45 @@ static const struct driver_info ax88178a_info = {
.tx_fixup = ax88179_tx_fixup,
};
+static const struct driver_info ax88179a_info = {
+ .description = "ASIX AX88179A USB 3.2 Gigabit Ethernet",
+ .bind = ax88179a_bind,
+ .unbind = ax88179a_unbind,
+ .status = ax88179_status,
+ .link_reset = ax88179a_link_reset,
+ .reset = ax88179_reset,
+ .stop = ax88179a_stop,
+ .flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_MULTI_PACKET | FLAG_AVOID_UNLINK_URBS,
+ .rx_fixup = ax88179a_rx_fixup,
+ .tx_fixup = ax88179a_tx_fixup,
+};
+
+static const struct driver_info ax88772d_info = {
+ .description = "ASIX AX88772D/E USB 2.0 Fast Ethernet",
+ .bind = ax88179a_bind,
+ .unbind = ax88179a_unbind,
+ .status = ax88179_status,
+ .link_reset = ax88179a_link_reset,
+ .reset = ax88179_reset,
+ .stop = ax88179a_stop,
+ .flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_MULTI_PACKET | FLAG_AVOID_UNLINK_URBS,
+ .rx_fixup = ax88179a_rx_fixup,
+ .tx_fixup = ax88179a_tx_fixup,
+};
+
+static const struct driver_info ax88279_info = {
+ .description = "ASIX AX88279 USB 3.2 2.5Gigabit Ethernet",
+ .bind = ax88179a_bind,
+ .unbind = ax88179a_unbind,
+ .status = ax88179_status,
+ .link_reset = ax88179a_link_reset,
+ .reset = ax88179_reset,
+ .stop = ax88179a_stop,
+ .flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_MULTI_PACKET | FLAG_AVOID_UNLINK_URBS,
+ .rx_fixup = ax88179a_rx_fixup,
+ .tx_fixup = ax88179a_tx_fixup,
+};
+
static const struct driver_info cypress_GX3_info = {
.description = "Cypress GX3 SuperSpeed to Gigabit Ethernet Controller",
.bind = ax88179_bind,
@@ -1877,6 +2735,18 @@ static const struct driver_info at_umc2000sp_info = {
static const struct usb_device_id products[] = {
{
+ /* ASIX AX88179A/B USB 3.2 Gigabit Ethernet */
+ USB_DEVICE_VER(0x0b95, 0x1790, 0x0200, 0x0200),
+ .driver_info = (unsigned long)&ax88179a_info,
+}, {
+ /* ASIX AX88772D USB 2.0 100Mbit Ethernet */
+ USB_DEVICE_VER(0x0b95, 0x1790, 0x0300, 0x0300),
+ .driver_info = (unsigned long)&ax88772d_info,
+}, {
+ /* ASIX AX88279 USB 3.2 2.5GBit Ethernet */
+ USB_DEVICE_VER(0x0b95, 0x1790, 0x0400, 0x0400),
+ .driver_info = (unsigned long)&ax88279_info,
+}, {
/* ASIX AX88179 10/100/1000 */
USB_DEVICE_AND_INTERFACE_INFO(0x0b95, 0x1790, 0xff, 0xff, 0),
.driver_info = (unsigned long)&ax88179_info,
--
2.47.3
^ permalink raw reply related
* [PATCH 0/9] ax88179_178a: Add support for AX88179A-based chips
From: Birger Koblitz @ 2026-07-01 5:42 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni
Cc: linux-usb, netdev, linux-kernel, Birger Koblitz
This adds support for the current generation of ASIX network adapter chips,
which are based on the AX88179A. This includes the AX88179A/B (1GBit-PHY),
AX88772D/E (100MBit) and AX88279 (2.5GBit).
The AX179A-based chips all provide both a CDC-NCM compatible USB interface,
and a proprietary vendor interface with more features. By default, the
proprietary vendor interface is not active and Linux will load the CDC-NCM
driver to support the devices. If the ax88179_178a module is configured by
the OS to have precedence over CDC-NCM, then this driver will switch the
device to use the vendor interface, and the device will be controlled by
the ax88179_178a driver when the device is probed again after an automatic
reset of the device bringing up the vendor interface.
The following hardware was tested:
Delock 66046 2.5GBit adapter (AX88279, FW: 1.2.0.0)
TP-Link UE306 1GBit adapter (AX88179B, FW: 1.3.0.0)
Renkforce RF-4708614 1GBit adapter (AX88179A, FW: 1.0.4.0)
UGREEN CR110 100MBit adapter (AX88722E, FW: 1.3.0.0)
The driver supports the following features
- EEE
- TCP segmentation offload
- VLAN filtering/tagging offload
(NETIF_F_HW_VLAN_CTAG_FILTER, NETIF_F_HW_VLAN_CTAG_RX/TX)
- RX/TX checksum offload
- FC/Pause configuration
- EEPROM read access
The code is based on the ASIX 4.1.0 out-of-tree driver published under
the GPL,, the aqc111 driver which provides support for the AX88279A,
and some tracing of USB-transfers of the Windows-driver.
Signed-off-by: Birger Koblitz <mail@birger-koblitz.de>
---
Birger Koblitz (9):
ax88179_178a: Fix endianness of pause watermark register
ax88179_178a: Add HW support for AX179A-based chips
ax88179_178a: Add support for AX88179A MMD access
ax88179_178a: Obtain speed and duplex from Interrupt URB
ax88179_178a: Add support for ethtool pause parameter configuration
ax88179_178a: Add VLAN offload support for AX88179A
ax88179_178a: Add ethtool get_drvinfo
ax88179_178a: Add support for AX88179A/772D/279 EEPROM access
ax88179_178a: Add AX179A/AX279 multicast configuration
drivers/net/usb/ax88179_178a.c | 1459 ++++++++++++++++++++++++++++++++++++----
1 file changed, 1316 insertions(+), 143 deletions(-)
---
base-commit: 805185b7c7a1069e407b6f7b3bc98e44d415f484
change-id: 20260630-ax88179a-a1d89fe21730
Best regards,
--
Birger Koblitz <mail@birger-koblitz.de>
^ permalink raw reply
* [PATCH 3/9] ax88179_178a: Add support for AX88179A MMD access
From: Birger Koblitz @ 2026-07-01 5:42 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni
Cc: linux-usb, netdev, linux-kernel, Birger Koblitz
In-Reply-To: <20260701-ax88179a-v1-0-13685df67515@birger-koblitz.de>
The AX88179A uses a much simpler Clause-45 MMD access interface,
make use of this interface and abstract MMD read/write operations
for the AX88179 and AX88179A architecture by introducing
ax_read_mmd() and ax_write_mmd(), which in turn call the chips'
respective implementation.
Make use of the MMD read/write functions in the link-speed and EEE
configuration settings via ethtool in order to support the
AX179A-based chips. The AX88279 supports EEE only at 1000MBit speed,
the other chips require full duplex.
Signed-off-by: Birger Koblitz <mail@birger-koblitz.de>
---
drivers/net/usb/ax88179_178a.c | 238 +++++++++++++++++++++++++++--------------
1 file changed, 155 insertions(+), 83 deletions(-)
diff --git a/drivers/net/usb/ax88179_178a.c b/drivers/net/usb/ax88179_178a.c
index c6945e131e63fd053a23bf14ab5d5948456ce4a5..fadf449fa5df70d4aed706b7488ac61d4cdf4cc9 100644
--- a/drivers/net/usb/ax88179_178a.c
+++ b/drivers/net/usb/ax88179_178a.c
@@ -35,8 +35,10 @@
#define AX_RELOAD_EEPROM_EFUSE 0x06
#define AX88179A_WAKEUP_SETTING 0x07
#define AX_FW_MODE 0x08
+#define AX_GPHY_CTL 0x0F
#define AX88179A_FLASH_READ 0x21
#define AX88179A_FLASH_WRITE 0x24
+#define AX88179A_PHY_CLAUSE45 0x27
#define AX88179A_ACCESS_BL 0x2A
#define AX88179A_PHY_POWER 0x31
#define AX88179A_AUTODETACH 0xC0
@@ -113,6 +115,9 @@
#define AX_PHYPWR_RSTCTL_AT 0x1000
#define AX_RX_BULKIN_QCTRL 0x2e
+
+#define AX_GPHY_EEE_CTRL 0x01
+
#define AX_CLK_SELECT 0x33
#define AX_CLK_SELECT_BCS 0x01
#define AX_CLK_SELECT_ACS 0x02
@@ -559,6 +564,32 @@ ax88179_phy_write_mmd_indirect(struct usbnet *dev, u16 prtad, u16 devad,
return 0;
}
+static int ax_read_mmd(struct usbnet *dev, u16 dev_addr, u16 reg)
+{
+ struct ax88179_data *priv = dev->driver_priv;
+ u16 res;
+ int ret;
+
+ if (priv->chip_version >= AX_VERSION_AX88179A) {
+ ret = ax88179_read_cmd(dev, AX88179A_PHY_CLAUSE45, dev_addr, reg, 2, &res);
+ if (ret < 0)
+ return ret;
+ return res;
+ }
+
+ return ax88179_phy_read_mmd_indirect(dev, reg, dev_addr);
+}
+
+static int ax_write_mmd(struct usbnet *dev, u16 dev_addr, u16 reg, u16 data)
+{
+ struct ax88179_data *priv = dev->driver_priv;
+
+ if (priv->chip_version >= AX_VERSION_AX88179A)
+ return ax88179_write_cmd(dev, AX88179A_PHY_CLAUSE45, dev_addr, reg, 2, &data);
+
+ return ax88179_phy_write_mmd_indirect(dev, reg, dev_addr, data);
+}
+
static int ax88179_suspend(struct usb_interface *intf, pm_message_t message)
{
struct usbnet *dev = usb_get_intfdata(intf);
@@ -853,12 +884,39 @@ ax88179_set_eeprom(struct net_device *net, struct ethtool_eeprom *eeprom,
}
static int ax88179_get_link_ksettings(struct net_device *net,
- struct ethtool_link_ksettings *cmd)
+ struct ethtool_link_ksettings *cmd)
{
struct usbnet *dev = netdev_priv(net);
+ struct ax88179_data *data;
+ int v;
+
+ data = dev->driver_priv;
mii_ethtool_get_link_ksettings(&dev->mii, cmd);
+ if (data->chip_version >= AX_VERSION_AX88279) {
+ linkmode_set_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT,
+ cmd->link_modes.supported);
+
+ v = ax88179_mdio_read(dev->net, dev->mii.phy_id, MII_ADVERTISE);
+ if (v >= 0 && v & ADVERTISE_RESV)
+ linkmode_set_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT,
+ cmd->link_modes.advertising);
+
+ v = ax_read_mmd(dev, MDIO_MMD_AN, MDIO_AN_10GBT_STAT);
+ if (data->speed == ETHER_LINK_2500) {
+ cmd->base.speed = SPEED_2500;
+ /* MDIO_AN_10GBT_STAT_LP2_5G is broken, but we can deduce that
+ * the link-partner advertised 2500M if remotely AN succceded
+ * for link speed > 1000M and we locally have a link speed of
+ * 2500M
+ */
+ if (v >= 0 && v & MDIO_AN_10GBT_STAT_REMOK)
+ linkmode_set_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT,
+ cmd->link_modes.lp_advertising);
+ }
+ }
+
return 0;
}
@@ -866,34 +924,63 @@ static int ax88179_set_link_ksettings(struct net_device *net,
const struct ethtool_link_ksettings *cmd)
{
struct usbnet *dev = netdev_priv(net);
+ struct ax88179_data *data;
+ int v;
+
+ data = dev->driver_priv;
+
+ /* mii_ethtool_set_link_ksettings handles unknown bits in MII_ADVERTISE
+ * transparently, so for the 2.5GBit link speed of the AX_VERSION_AX88279
+ * we just set up ADVERTISE_RESV before calling mii_ethtool_set_link_ksettings
+ * at least for speeds < 2500
+ */
+ if (data->chip_version == AX_VERSION_AX88279) {
+ v = ax88179_mdio_read(net, dev->mii.phy_id, MII_ADVERTISE);
+ if (v < 0)
+ return v;
+
+ if (linkmode_test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT,
+ cmd->link_modes.advertising))
+ v |= ADVERTISE_RESV;
+ else
+ v &= ~ADVERTISE_RESV;
+ ax88179_mdio_write(net, dev->mii.phy_id, MII_ADVERTISE, v);
+ if (cmd->base.speed == SPEED_2500)
+ return mii_nway_restart(&dev->mii);
+ }
+
return mii_ethtool_set_link_ksettings(&dev->mii, cmd);
}
static int
ax88179_ethtool_get_eee(struct usbnet *dev, struct ethtool_keee *data)
{
+ struct ax88179_data *priv = dev->driver_priv;
int val;
/* Get Supported EEE */
- val = ax88179_phy_read_mmd_indirect(dev, MDIO_PCS_EEE_ABLE,
- MDIO_MMD_PCS);
+ val = ax_read_mmd(dev, MDIO_MMD_PCS, MDIO_PCS_EEE_ABLE);
if (val < 0)
return val;
mii_eee_cap1_mod_linkmode_t(data->supported, val);
/* Get advertisement EEE */
- val = ax88179_phy_read_mmd_indirect(dev, MDIO_AN_EEE_ADV,
- MDIO_MMD_AN);
+ val = ax_read_mmd(dev, MDIO_MMD_AN, MDIO_AN_EEE_ADV);
if (val < 0)
return val;
mii_eee_cap1_mod_linkmode_t(data->advertised, val);
/* Get LP advertisement EEE */
- val = ax88179_phy_read_mmd_indirect(dev, MDIO_AN_EEE_LPABLE,
- MDIO_MMD_AN);
+ val = ax_read_mmd(dev, MDIO_MMD_AN, MDIO_AN_EEE_LPABLE);
if (val < 0)
return val;
mii_eee_cap1_mod_linkmode_t(data->lp_advertised, val);
+ if (priv->chip_version >= AX_VERSION_AX88279) {
+ val = ax_read_mmd(dev, MDIO_MMD_AN, MDIO_AN_EEE_LPABLE2);
+ if (val < 0)
+ return val;
+ mii_eee_cap2_mod_linkmode_adv_t(data->lp_advertised, val);
+ }
return 0;
}
@@ -903,8 +990,7 @@ ax88179_ethtool_set_eee(struct usbnet *dev, struct ethtool_keee *data)
{
u16 tmp16 = linkmode_to_mii_eee_cap1_t(data->advertised);
- return ax88179_phy_write_mmd_indirect(dev, MDIO_AN_EEE_ADV,
- MDIO_MMD_AN, tmp16);
+ return ax_write_mmd(dev, MDIO_MMD_AN, MDIO_AN_EEE_ADV, tmp16);
}
static int ax88179_chk_eee(struct usbnet *dev)
@@ -914,40 +1000,27 @@ static int ax88179_chk_eee(struct usbnet *dev)
mii_ethtool_gset(&dev->mii, &ecmd);
- if (ecmd.duplex & DUPLEX_FULL) {
+ priv->eee_active = 0;
+ if ((priv->chip_version < AX_VERSION_AX88279 && (ecmd.duplex & DUPLEX_FULL)) ||
+ (ecmd.speed == SPEED_1000 && (ecmd.duplex & DUPLEX_FULL))) {
int eee_lp, eee_cap, eee_adv;
u32 lp, cap, adv, supported = 0;
- eee_cap = ax88179_phy_read_mmd_indirect(dev,
- MDIO_PCS_EEE_ABLE,
- MDIO_MMD_PCS);
- if (eee_cap < 0) {
- priv->eee_active = 0;
+ eee_cap = ax_read_mmd(dev, MDIO_MMD_PCS, MDIO_PCS_EEE_ABLE);
+ if (eee_cap < 0)
return false;
- }
cap = mmd_eee_cap_to_ethtool_sup_t(eee_cap);
- if (!cap) {
- priv->eee_active = 0;
+ if (!cap)
return false;
- }
- eee_lp = ax88179_phy_read_mmd_indirect(dev,
- MDIO_AN_EEE_LPABLE,
- MDIO_MMD_AN);
- if (eee_lp < 0) {
- priv->eee_active = 0;
- return false;
- }
+ eee_lp = ax_read_mmd(dev, MDIO_MMD_AN, MDIO_AN_EEE_LPABLE);
+ if (eee_lp < 0)
+ return true;
- eee_adv = ax88179_phy_read_mmd_indirect(dev,
- MDIO_AN_EEE_ADV,
- MDIO_MMD_AN);
-
- if (eee_adv < 0) {
- priv->eee_active = 0;
- return false;
- }
+ eee_adv = ax_read_mmd(dev, MDIO_MMD_AN, MDIO_AN_EEE_ADV);
+ if (eee_adv < 0)
+ return true;
adv = mmd_eee_adv_to_ethtool_adv_t(eee_adv);
lp = mmd_eee_adv_to_ethtool_adv_t(eee_lp);
@@ -955,65 +1028,53 @@ static int ax88179_chk_eee(struct usbnet *dev)
SUPPORTED_1000baseT_Full :
SUPPORTED_100baseT_Full;
- if (!(lp & adv & supported)) {
- priv->eee_active = 0;
- return false;
- }
+ if (!(lp & adv & supported))
+ return true;
priv->eee_active = 1;
return true;
}
- priv->eee_active = 0;
return false;
}
-static void ax88179_disable_eee(struct usbnet *dev)
-{
- u16 tmp16;
-
- tmp16 = GMII_PHY_PGSEL_PAGE3;
- ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID,
- GMII_PHY_PAGE_SELECT, 2, &tmp16);
-
- tmp16 = 0x3246;
- ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID,
- MII_PHYADDR, 2, &tmp16);
-
- tmp16 = GMII_PHY_PGSEL_PAGE0;
- ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID,
- GMII_PHY_PAGE_SELECT, 2, &tmp16);
-}
-
-static void ax88179_enable_eee(struct usbnet *dev)
+static void ax88179_eee_config(struct usbnet *dev, bool enable)
{
+ struct ax88179_data *priv = dev->driver_priv;
u16 tmp16;
- tmp16 = GMII_PHY_PGSEL_PAGE3;
- ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID,
- GMII_PHY_PAGE_SELECT, 2, &tmp16);
-
- tmp16 = 0x3247;
- ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID,
- MII_PHYADDR, 2, &tmp16);
-
- tmp16 = GMII_PHY_PGSEL_PAGE5;
- ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID,
- GMII_PHY_PAGE_SELECT, 2, &tmp16);
-
- tmp16 = 0x0680;
- ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID,
- MII_BMSR, 2, &tmp16);
+ if (priv->chip_version < AX_VERSION_AX88179A) {
+ tmp16 = GMII_PHY_PGSEL_PAGE3;
+ ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID,
+ GMII_PHY_PAGE_SELECT, 2, &tmp16);
+
+ tmp16 = enable ? 0x3247 : 0x3246;
+ ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID,
+ MII_PHYADDR, 2, &tmp16);
+ if (enable) {
+ tmp16 = GMII_PHY_PGSEL_PAGE5;
+ ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID,
+ GMII_PHY_PAGE_SELECT, 2, &tmp16);
+
+ tmp16 = 0x0680;
+ ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID,
+ MII_BMSR, 2, &tmp16);
+ }
- tmp16 = GMII_PHY_PGSEL_PAGE0;
- ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID,
- GMII_PHY_PAGE_SELECT, 2, &tmp16);
+ tmp16 = GMII_PHY_PGSEL_PAGE0;
+ ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID,
+ GMII_PHY_PAGE_SELECT, 2, &tmp16);
+ } else {
+ ax88179_write_cmd(dev, AX_GPHY_CTL, AX_GPHY_EEE_CTRL, enable, 0, NULL);
+ }
}
static int ax88179_get_eee(struct net_device *net, struct ethtool_keee *edata)
{
struct usbnet *dev = netdev_priv(net);
- struct ax88179_data *priv = dev->driver_priv;
+ struct ax88179_data *priv;
+
+ priv = dev->driver_priv;
edata->eee_enabled = priv->eee_enabled;
edata->eee_active = priv->eee_active;
@@ -1024,18 +1085,20 @@ static int ax88179_get_eee(struct net_device *net, struct ethtool_keee *edata)
static int ax88179_set_eee(struct net_device *net, struct ethtool_keee *edata)
{
struct usbnet *dev = netdev_priv(net);
- struct ax88179_data *priv = dev->driver_priv;
+ struct ax88179_data *priv;
int ret;
+ priv = dev->driver_priv;
+
priv->eee_enabled = edata->eee_enabled;
if (!priv->eee_enabled) {
- ax88179_disable_eee(dev);
+ ax88179_eee_config(dev, false);
} else {
priv->eee_enabled = ax88179_chk_eee(dev);
if (!priv->eee_enabled)
return -EOPNOTSUPP;
- ax88179_enable_eee(dev);
+ ax88179_eee_config(dev, true);
}
ret = ax88179_ethtool_set_eee(dev, edata);
@@ -2459,11 +2522,20 @@ static int ax88179_reset(struct usbnet *dev)
ax179_data->eee_enabled = 0;
ax179_data->eee_active = 0;
- ax88179_disable_eee(dev);
+ if (ax179_data->chip_version < AX_VERSION_AX88179A) {
+ ax88179_eee_config(dev, false);
- ax88179_ethtool_get_eee(dev, &eee_data);
- linkmode_zero(eee_data.advertised);
- ax88179_ethtool_set_eee(dev, &eee_data);
+ ax88179_ethtool_get_eee(dev, &eee_data);
+ linkmode_zero(eee_data.advertised);
+ ax88179_ethtool_set_eee(dev, &eee_data);
+ } else {
+ ax88179_eee_config(dev, true);
+ ax88179_ethtool_get_eee(dev, &eee_data);
+ linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, eee_data.advertised);
+ if (ax179_data->chip_version >= AX_VERSION_AX88279)
+ linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, eee_data.advertised);
+ ax88179_ethtool_set_eee(dev, &eee_data);
+ }
/* Restart autoneg */
mii_nway_restart(&dev->mii);
--
2.47.3
^ permalink raw reply related
* [PATCH 4/9] ax88179_178a: Obtain speed and duplex from Interrupt URB
From: Birger Koblitz @ 2026-07-01 5:42 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni
Cc: linux-usb, netdev, linux-kernel, Birger Koblitz
In-Reply-To: <20260701-ax88179a-v1-0-13685df67515@birger-koblitz.de>
For newer AX179A/772D firmwares and the AX88279, the
interrupt URB response also contains information on the Ethernet
speed and duplex status. Read this in order to use it to configure
the link.
Signed-off-by: Birger Koblitz <mail@birger-koblitz.de>
---
drivers/net/usb/ax88179_178a.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/drivers/net/usb/ax88179_178a.c b/drivers/net/usb/ax88179_178a.c
index fadf449fa5df70d4aed706b7488ac61d4cdf4cc9..16528d873e804fde5dcc27659048882eee1c3eaa 100644
--- a/drivers/net/usb/ax88179_178a.c
+++ b/drivers/net/usb/ax88179_178a.c
@@ -474,6 +474,7 @@ static int ax88179_write_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index,
static void ax88179_status(struct usbnet *dev, struct urb *urb)
{
+ struct ax88179_data *data = dev->driver_priv;
struct ax88179_int_data *event;
u32 link;
@@ -484,6 +485,9 @@ static void ax88179_status(struct usbnet *dev, struct urb *urb)
le32_to_cpus((void *)&event->intdata1);
link = (((__force u32)event->intdata1) & AX_INT_PPLS_LINK) >> 16;
+ data->speed = (((__force u32)event->intdata1) >> 8) & 0x7;
+ data->full_duplex = (((__force u32)event->intdata1) >> 12) & 0x1;
+ data->link = link;
if (netif_carrier_ok(dev->net) != link) {
usbnet_link_change(dev, link, 1);
--
2.47.3
^ permalink raw reply related
* [PATCH 1/9] ax88179_178a: Fix endianness of pause watermark register
From: Birger Koblitz @ 2026-07-01 5:42 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni
Cc: linux-usb, netdev, linux-kernel, Birger Koblitz
In-Reply-To: <20260701-ax88179a-v1-0-13685df67515@birger-koblitz.de>
The 16-bit pause watermark register is little endian as
described in the ASIX 4.1.0 out-of-tree driver. Correct the
register byte sequence but also swap the configuration values
used in the code in order to keep the current behaviour.
The endianness is relevant for 16-bit writes to the register.
Signed-off-by: Birger Koblitz <mail@birger-koblitz.de>
---
drivers/net/usb/ax88179_178a.c | 9 ++++-----
1 file changed, 4 insertions(+), 5 deletions(-)
diff --git a/drivers/net/usb/ax88179_178a.c b/drivers/net/usb/ax88179_178a.c
index 98f899ea2e9462f1ba99281a875385241745458b..945c071dfd1d2f0816c779e1a401ac158adc8d99 100644
--- a/drivers/net/usb/ax88179_178a.c
+++ b/drivers/net/usb/ax88179_178a.c
@@ -32,8 +32,8 @@
#define AX_ACCESS_EEPROM 0x04
#define AX_ACCESS_EFUS 0x05
#define AX_RELOAD_EEPROM_EFUSE 0x06
-#define AX_PAUSE_WATERLVL_HIGH 0x54
-#define AX_PAUSE_WATERLVL_LOW 0x55
+#define AX_PAUSE_WATERLVL_LOW 0x54
+#define AX_PAUSE_WATERLVL_HIGH 0x55
#define PHYSICAL_LINK_STATUS 0x02
#define AX_USB_SS 0x04
@@ -1617,11 +1617,10 @@ static int ax88179_reset(struct usbnet *dev)
dev->rx_urb_size = 1024 * 20;
*tmp = 0x34;
- ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PAUSE_WATERLVL_LOW, 1, 1, tmp);
+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PAUSE_WATERLVL_HIGH, 1, 1, tmp);
*tmp = 0x52;
- ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PAUSE_WATERLVL_HIGH,
- 1, 1, tmp);
+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PAUSE_WATERLVL_LOW, 1, 1, tmp);
/* Enable checksum offload */
*tmp = AX_RXCOE_IP | AX_RXCOE_TCP | AX_RXCOE_UDP |
--
2.47.3
^ permalink raw reply related
* RE: [PATCH v3 net-next 1/1] tcp: Replace min_tso_segs() with tso_segs() CC callback
From: Chia-Yu Chang (Nokia) @ 2026-07-01 5:46 UTC (permalink / raw)
To: Alexei Starovoitov, jolsa@kernel.org, yonghong.song@linux.dev,
song@kernel.org, linux-kselftest@vger.kernel.org,
memxor@gmail.com, shuah@kernel.org, martin.lau@linux.dev,
ast@kernel.org, daniel@iogearbox.net, andrii@kernel.org,
eddyz87@gmail.com, horms@kernel.org, dsahern@kernel.org,
bpf@vger.kernel.org, netdev@vger.kernel.org, pabeni@redhat.com,
jhs@mojatatu.com, kuba@kernel.org, stephen@networkplumber.org,
davem@davemloft.net, edumazet@google.com, andrew+netdev@lunn.ch,
donald.hunter@gmail.com, kuniyu@google.com, ij@kernel.org,
ncardwell@google.com, Koen De Schepper (Nokia),
g.white@cablelabs.com, ingemar.s.johansson@ericsson.com,
mirja.kuehlewind@ericsson.com, cheshire@apple.com, rs.ietf@gmx.at,
Jason_Livingood@comcast.com, vidhi_goel@apple.com
In-Reply-To: <DJMRPJEUTY5G.30HYOMPLBGZOJ@gmail.com>
> -----Original Message-----
> From: Alexei Starovoitov <alexei.starovoitov@gmail.com>
> Sent: Wednesday, July 1, 2026 1:21 AM
> To: Chia-Yu Chang (Nokia) <chia-yu.chang@nokia-bell-labs.com>; jolsa@kernel.org; yonghong.song@linux.dev; song@kernel.org; linux-kselftest@vger.kernel.org; memxor@gmail.com; shuah@kernel.org; martin.lau@linux.dev; ast@kernel.org; daniel@iogearbox.net; andrii@kernel.org; eddyz87@gmail.com; horms@kernel.org; dsahern@kernel.org; bpf@vger.kernel.org; netdev@vger.kernel.org; pabeni@redhat.com; jhs@mojatatu.com; kuba@kernel.org; stephen@networkplumber.org; davem@davemloft.net; edumazet@google.com; andrew+netdev@lunn.ch; donald.hunter@gmail.com; kuniyu@google.com; ij@kernel.org; ncardwell@google.com; Koen De Schepper (Nokia) <koen.de_schepper@nokia-bell-labs.com>; g.white@cablelabs.com; ingemar.s.johansson@ericsson.com; mirja.kuehlewind@ericsson.com; cheshire@apple.com; rs.ietf@gmx.at; Jason_Livingood@comcast.com; vidhi_goel@apple.com
> Subject: Re: [PATCH v3 net-next 1/1] tcp: Replace min_tso_segs() with tso_segs() CC callback
>
> [You don't often get email from alexei.starovoitov@gmail.com. Learn why this is important at https://aka.ms/LearnAboutSenderIdentification ]
>
> CAUTION: This is an external email. Please be very careful when clicking links or opening attachments. See the URL nok.it/ext for additional information.
>
>
>
> On Tue Jun 30, 2026 at 5:01 AM PDT, chia-yu.chang wrote:
> > From: Chia-Yu Chang <chia-yu.chang@nokia-bell-labs.com>
> >
> > This patch replaces existing min_tso_segs() with tso_segs() CC callbak
> > for CC algorithm to provides explicit tso segment number of each data
> > burst and overrides tcp_tso_autosize().
> >
> > This change provides below impacts on BPF struct_ops users:
> > - The callback is renamed from min_tso_segs to tso_segs
> > - The signature gains an extra u32 mss_now argument
> > - The return value semantics is changed from "floor value passed into
> > tcp_tso_autosize()" to "final tso_segs value", bypassing autosizing
> >
> > As a result, BPF programs shall be updated, beccause retuning a small
> > constans will now directly limit tso_segs instead of the minimum.
> >
> > Signed-off-by: Ilpo Järvinen <ij@kernel.org>
> > Signed-off-by: Chia-Yu Chang <chia-yu.chang@nokia-bell-labs.com>
> > ---
> > include/net/tcp.h | 13 +++++++++++--
> > net/ipv4/bpf_tcp_ca.c | 8 +++++---
> > net/ipv4/tcp_bbr.c | 13 ++++++++++---
> > net/ipv4/tcp_output.c | 13 +++++++------
> > tools/testing/selftests/bpf/progs/tcp_ca_kfunc.c | 8 ++++----
> > 5 files changed, 37 insertions(+), 18 deletions(-)
> >
> > diff --git a/include/net/tcp.h b/include/net/tcp.h index
> > 6d376ea4d1c0..7fb42a0ce7da 100644
> > --- a/include/net/tcp.h
> > +++ b/include/net/tcp.h
> > @@ -824,6 +824,9 @@ unsigned int tcp_sync_mss(struct sock *sk, u32
> > pmtu); unsigned int tcp_current_mss(struct sock *sk);
> > u32 tcp_clamp_probe0_to_user_timeout(const struct sock *sk, u32
> > when);
> >
> > +u32 tcp_tso_autosize(const struct sock *sk, unsigned int mss_now,
> > + int min_tso_segs);
> > +
> > /* Bound MSS / TSO packet size with the half of the window */ static
> > inline int tcp_bound_to_half_wnd(struct tcp_sock *tp, int pktsize) {
> > @@ -1361,8 +1364,14 @@ struct tcp_congestion_ops {
> > /* hook for packet ack accounting (optional) */
> > void (*pkts_acked)(struct sock *sk, const struct ack_sample
> > *sample);
> >
> > - /* override sysctl_tcp_min_tso_segs (optional) */
> > - u32 (*min_tso_segs)(struct sock *sk);
> > + /*
> > + * Override tcp_tso_autosize (optional)
> > + *
> > + * If provided, this callback returns the final TSO segment number
> > + * and will bypass tcp_tso_autosize() entirely. The implementation
> > + * must derive an appropriate value and ensure the result is valid.
> > + */
> > + u32 (*tso_segs)(struct sock *sk, u32 mss_now);
>
> I don't like this interface change.
> It introduces churn for no good reason.
> At least I don't see why you cannot live with the existing api.
Hi Alexei,
This patch was part of TCP Prague preparation series: https://lore.kernel.org/all/20260611161504.228319-4-chia-yu.chang@nokia-bell-labs.com/
Our original patch is to add an extra tso_segs, and after discussion it's recommended to replace exisiting min_tso_segs.
This is needed because TCP Prague would set the exact TSO size rather than using autosizing from TCP.
The TCP Prague itself is planned to be submitted after all preparation commits are accepted.
You can find its current stauts: https://github.com/L4STeam/linux-net-next/blob/upstream_l4steam/net/ipv4/tcp_prague.c
Thanks.
Chia-Yu
^ permalink raw reply
* [PATCH 9/9] ax88179_178a: Add AX179A/AX279 multicast configuration
From: Birger Koblitz @ 2026-07-01 5:42 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni
Cc: linux-usb, netdev, linux-kernel, Birger Koblitz
In-Reply-To: <20260701-ax88179a-v1-0-13685df67515@birger-koblitz.de>
Add support for conditionally setting the ip_alignement flag
AX_RX_CTL_IPE in AX_RX_CTL and make sure that AX_RX_CTL_DROPCRCERR
is also set to be consistent with the initial configuration in
ax88179_reset()
Signed-off-by: Birger Koblitz <mail@birger-koblitz.de>
---
drivers/net/usb/ax88179_178a.c | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/drivers/net/usb/ax88179_178a.c b/drivers/net/usb/ax88179_178a.c
index 001faa66efb29df1e09fa3fdc7aa582ab254baca..12fc20e5f76e6906c5d61f5b0d15b9fcb5b60210 100644
--- a/drivers/net/usb/ax88179_178a.c
+++ b/drivers/net/usb/ax88179_178a.c
@@ -1329,10 +1329,14 @@ static int ax179a_vlan_rx_add_vid(struct net_device *net, __be16 proto, u16 vid)
static void ax88179_set_multicast(struct net_device *net)
{
struct usbnet *dev = netdev_priv(net);
- struct ax88179_data *data = dev->driver_priv;
u8 *m_filter = ((u8 *)dev->data);
+ struct ax88179_data *data;
+
+ data = dev->driver_priv;
- data->rxctl = (AX_RX_CTL_START | AX_RX_CTL_AB | AX_RX_CTL_IPE);
+ data->rxctl = (AX_RX_CTL_START | AX_RX_CTL_AB | AX_RX_CTL_DROPCRCERR);
+ if (data->ip_align)
+ data->rxctl |= AX_RX_CTL_IPE;
if (net->flags & IFF_PROMISC) {
data->rxctl |= AX_RX_CTL_PRO;
--
2.47.3
^ permalink raw reply related
* [PATCH 8/9] ax88179_178a: Add support for AX88179A/772D/279 EEPROM access
From: Birger Koblitz @ 2026-07-01 5:42 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni
Cc: linux-usb, netdev, linux-kernel, Birger Koblitz
In-Reply-To: <20260701-ax88179a-v1-0-13685df67515@birger-koblitz.de>
The AX88179A/772D devices have 32 efuses with 20 bytes each,
which can be randomly programmed. The AX88279 has 16K FLASH.
Provide ethtool read capability for these devices. However,
no write access is provided.
Signed-off-by: Birger Koblitz <mail@birger-koblitz.de>
---
drivers/net/usb/ax88179_178a.c | 75 +++++++++++++++++++++++++++++++++---------
1 file changed, 59 insertions(+), 16 deletions(-)
diff --git a/drivers/net/usb/ax88179_178a.c b/drivers/net/usb/ax88179_178a.c
index d990e28540bdf9336cebfd820b90468d01554450..001faa66efb29df1e09fa3fdc7aa582ab254baca 100644
--- a/drivers/net/usb/ax88179_178a.c
+++ b/drivers/net/usb/ax88179_178a.c
@@ -17,6 +17,8 @@
#define AX88179_PHY_ID 0x03
#define AX_EEPROM_LEN 0x100
+#define AX88279_EEPROM_LEN 0x4000
+#define AX88179A_EEPROM_LEN (32 * 20)
#define AX88179_EEPROM_MAGIC 0x17900b95
#define AX_MCAST_FLTSIZE 8
#define AX_MAX_MCAST 64
@@ -37,8 +39,11 @@
#define AX_FW_MODE 0x08
#define AX_GPHY_CTL 0x0F
#define AX88179A_FLASH_READ 0x21
+#define AX88179A_FLASH_WEN 0x22
+#define AX88179A_FLASH_WDIS 0x23
#define AX88179A_FLASH_WRITE 0x24
#define AX88179A_PHY_CLAUSE45 0x27
+#define AX88179A_FLASH_ERASE_SECTION 0x28
#define AX88179A_ACCESS_BL 0x2A
#define AX88179A_PHY_POWER 0x31
#define AX88179A_AUTODETACH 0xC0
@@ -799,41 +804,74 @@ ax88179_set_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo)
static int ax88179_get_eeprom_len(struct net_device *net)
{
- return AX_EEPROM_LEN;
+ struct usbnet *dev = netdev_priv(net);
+ struct ax88179_data *ax179_data;
+
+ ax179_data = dev->driver_priv;
+
+ if (ax179_data->chip_version < AX_VERSION_AX88179A)
+ return AX_EEPROM_LEN;
+ else if (ax179_data->chip_version >= AX_VERSION_AX88279)
+ return AX88279_EEPROM_LEN;
+ else
+ return AX88179A_EEPROM_LEN;
}
-static int
-ax88179_get_eeprom(struct net_device *net, struct ethtool_eeprom *eeprom,
- u8 *data)
+static void
+ax88179_eeprom_access_params(struct ax88179_data *ax179_data, int i, u16 *value, u16 *idx)
+{
+ /* AX88179 has a word-addressable EEPROM
+ * AX88179A uses EFUSES with 20 bytes length
+ * AX88279 has an EEPROM addressable in 256 byte blocks
+ */
+ if (ax179_data->chip_version < AX_VERSION_AX88179A) {
+ *value = i;
+ *idx = 1;
+ } else if (ax179_data->chip_version >= AX_VERSION_AX88279) {
+ *value = (i * ax179_data->eeprom_block) >> 16;
+ *idx = (i * ax179_data->eeprom_block) & 0xffff;
+ } else {
+ *value = i << 4;
+ *idx = 0;
+ }
+}
+
+static int ax88179_get_eeprom(struct net_device *net, struct ethtool_eeprom *eeprom, u8 *data)
{
struct usbnet *dev = netdev_priv(net);
- u16 *eeprom_buff;
- int first_word, last_word;
- int i, ret;
+ struct ax88179_data *ax179_data;
+ int first, last, i, ret;
+ u8 *eeprom_buff;
+
+ ax179_data = dev->driver_priv;
if (eeprom->len == 0)
return -EINVAL;
eeprom->magic = AX88179_EEPROM_MAGIC;
- first_word = eeprom->offset >> 1;
- last_word = (eeprom->offset + eeprom->len - 1) >> 1;
- eeprom_buff = kmalloc_array(last_word - first_word + 1, sizeof(u16),
- GFP_KERNEL);
+ first = eeprom->offset / ax179_data->eeprom_block;
+ last = (eeprom->offset + eeprom->len - 1) / ax179_data->eeprom_block;
+
+ eeprom_buff = kzalloc((last - first + 1) * ax179_data->eeprom_block, GFP_KERNEL);
if (!eeprom_buff)
return -ENOMEM;
- /* ax88179/178A returns 2 bytes from eeprom on read */
- for (i = first_word; i <= last_word; i++) {
- ret = __ax88179_read_cmd(dev, AX_ACCESS_EEPROM, i, 1, 2,
- &eeprom_buff[i - first_word]);
+ for (i = first; i <= last; i++) {
+ u16 value, idx;
+
+ ax88179_eeprom_access_params(ax179_data, i, &value, &idx);
+ ret = __ax88179_read_cmd(dev, ax179_data->eeprom_read_cmd,
+ value, idx, ax179_data->eeprom_block,
+ eeprom_buff + (i - first) * ax179_data->eeprom_block);
+
if (ret < 0) {
kfree(eeprom_buff);
return -EIO;
}
}
- memcpy(data, (u8 *)eeprom_buff + (eeprom->offset & 1), eeprom->len);
+ memcpy(data, eeprom_buff + eeprom->offset % ax179_data->eeprom_block, eeprom->len);
kfree(eeprom_buff);
return 0;
}
@@ -843,12 +881,17 @@ ax88179_set_eeprom(struct net_device *net, struct ethtool_eeprom *eeprom,
u8 *data)
{
struct usbnet *dev = netdev_priv(net);
+ struct ax88179_data *ax179_data;
u16 *eeprom_buff;
int first_word;
int last_word;
int ret;
int i;
+ ax179_data = dev->driver_priv;
+ if (ax179_data->chip_version >= AX_VERSION_AX88179A)
+ return -EOPNOTSUPP;
+
netdev_dbg(net, "write EEPROM len %d, offset %d, magic 0x%x\n",
eeprom->len, eeprom->offset, eeprom->magic);
--
2.47.3
^ permalink raw reply related
* [PATCH 7/9] ax88179_178a: Add ethtool get_drvinfo
From: Birger Koblitz @ 2026-07-01 5:42 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni
Cc: linux-usb, netdev, linux-kernel, Birger Koblitz
In-Reply-To: <20260701-ax88179a-v1-0-13685df67515@birger-koblitz.de>
Add ax88179_get_drvinfo() as implementation of get_drvinfo, in order
to provide information about the device firmware.
Signed-off-by: Birger Koblitz <mail@birger-koblitz.de>
---
drivers/net/usb/ax88179_178a.c | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
diff --git a/drivers/net/usb/ax88179_178a.c b/drivers/net/usb/ax88179_178a.c
index f70f79ebc7f7c4d2321e033f7b8d09e6291c842f..d990e28540bdf9336cebfd820b90468d01554450 100644
--- a/drivers/net/usb/ax88179_178a.c
+++ b/drivers/net/usb/ax88179_178a.c
@@ -758,6 +758,21 @@ static void ax88179_disconnect(struct usb_interface *intf)
usbnet_disconnect(intf);
}
+static void ax88179_get_drvinfo(struct net_device *net, struct ethtool_drvinfo *info)
+{
+ struct usbnet *dev = netdev_priv(net);
+ struct ax88179_data *priv = dev->driver_priv;
+
+ /* Inherit standard device info */
+ usbnet_get_drvinfo(net, info);
+ if (priv->chip_version < AX_VERSION_AX88179A)
+ return;
+
+ snprintf(info->fw_version, sizeof(info->fw_version), "%d.%d.%d.%d",
+ priv->fw_version[0], priv->fw_version[1],
+ priv->fw_version[2], priv->fw_version[3]);
+}
+
static void
ax88179_get_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo)
{
@@ -1193,6 +1208,7 @@ static int ax88179_set_eee(struct net_device *net, struct ethtool_keee *edata)
}
static const struct ethtool_ops ax88179_ethtool_ops = {
+ .get_drvinfo = ax88179_get_drvinfo,
.get_link = ethtool_op_get_link,
.get_msglevel = usbnet_get_msglevel,
.set_msglevel = usbnet_set_msglevel,
--
2.47.3
^ permalink raw reply related
* [PATCH 6/9] ax88179_178a: Add VLAN offload support for AX88179A
From: Birger Koblitz @ 2026-07-01 5:42 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni
Cc: linux-usb, netdev, linux-kernel, Birger Koblitz
In-Reply-To: <20260701-ax88179a-v1-0-13685df67515@birger-koblitz.de>
The AX88179A-based chips support VLAN offload. Add configuration
support in netdev_ops. Features supported are:
NETIF_F_HW_VLAN_CTAG_TX, NETIF_F_HW_VLAN_CTAG_RX
and NETIF_F_HW_VLAN_CTAG_FILTER.
Signed-off-by: Birger Koblitz <mail@birger-koblitz.de>
---
drivers/net/usb/ax88179_178a.c | 98 ++++++++++++++++++++++++++++++++++++++++++
1 file changed, 98 insertions(+)
diff --git a/drivers/net/usb/ax88179_178a.c b/drivers/net/usb/ax88179_178a.c
index 586c049c6f7422a853aeae5e9372ead3a6d106c5..f70f79ebc7f7c4d2321e033f7b8d09e6291c842f 100644
--- a/drivers/net/usb/ax88179_178a.c
+++ b/drivers/net/usb/ax88179_178a.c
@@ -114,6 +114,17 @@
#define AX_PHYPWR_RSTCTL_IPRL 0x0020
#define AX_PHYPWR_RSTCTL_AT 0x1000
+#define AX88179A_VLAN_ID_ADDRESS 0x2A
+
+#define AX88179A_VLAN_ID_CONTROL 0x2B
+ #define AX_VLAN_CONTROL_WE 0x0001
+ #define AX_VLAN_CONTROL_RD 0x0002
+ #define AX_VLAN_CONTROL_VSO 0x0010
+ #define AX_VLAN_CONTROL_VFE 0x0020
+
+#define AX88179A_VLAN_ID_DATA0 0x2C
+#define AX88179A_VLAN_ID_DATA1 0x2D
+
#define AX_RX_BULKIN_QCTRL 0x2e
#define AX_GPHY_EEE_CTRL 0x01
@@ -1200,6 +1211,62 @@ static const struct ethtool_ops ax88179_ethtool_ops = {
.get_ts_info = ethtool_op_get_ts_info,
};
+static int ax179a_vlan_rx_kill_vid(struct net_device *net, __be16 proto, u16 vid)
+{
+ struct usbnet *dev = netdev_priv(net);
+ u8 vlan_ctrl;
+ u16 reg16;
+ u8 reg8;
+
+ ax88179_read_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_CONTROL, 1, 1, ®8);
+ vlan_ctrl = reg8;
+
+ /* Address */
+ reg8 = (vid / 16);
+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_ADDRESS, 1, 1, ®8);
+
+ /* Data */
+ reg8 = vlan_ctrl | AX_VLAN_CONTROL_RD;
+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_CONTROL, 1, 1, ®8);
+
+ ax88179_read_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_DATA0, 2, 2, ®16);
+ reg16 &= ~(1 << (vid % 16));
+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_DATA0, 2, 2, ®16);
+
+ reg8 = vlan_ctrl | AX_VLAN_CONTROL_WE;
+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_CONTROL, 1, 1, ®8);
+
+ return 0;
+}
+
+static int ax179a_vlan_rx_add_vid(struct net_device *net, __be16 proto, u16 vid)
+{
+ struct usbnet *dev = netdev_priv(net);
+ u8 vlan_ctrl;
+ u16 reg16;
+ u8 reg8;
+
+ ax88179_read_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_CONTROL, 1, 1, ®8);
+ vlan_ctrl = reg8;
+
+ /* Address */
+ reg8 = (vid / 16);
+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_ADDRESS, 1, 1, ®8);
+
+ /* Data */
+ reg8 = vlan_ctrl | AX_VLAN_CONTROL_RD;
+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_CONTROL, 1, 1, ®8);
+
+ ax88179_read_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_DATA0, 2, 2, ®16);
+ reg16 |= (1 << (vid % 16));
+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_DATA0, 2, 2, ®16);
+
+ reg8 = vlan_ctrl | AX_VLAN_CONTROL_WE;
+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_CONTROL, 1, 1, ®8);
+
+ return 0;
+}
+
static void ax88179_set_multicast(struct net_device *net)
{
struct usbnet *dev = netdev_priv(net);
@@ -1245,6 +1312,7 @@ ax88179_set_features(struct net_device *net, netdev_features_t features)
{
u8 tmp;
struct usbnet *dev = netdev_priv(net);
+ struct ax88179_data *data = dev->driver_priv;
netdev_features_t changed = net->features ^ features;
if (changed & NETIF_F_IP_CSUM) {
@@ -1264,6 +1332,34 @@ ax88179_set_features(struct net_device *net, netdev_features_t features)
tmp ^= AX_RXCOE_IP | AX_RXCOE_TCP | AX_RXCOE_UDP |
AX_RXCOE_TCPV6 | AX_RXCOE_UDPV6;
ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RXCOE_CTL, 1, 1, &tmp);
+ data->rx_checksum = !!(features & NETIF_F_RXCSUM);
+ }
+
+ if (changed & NETIF_F_HW_VLAN_CTAG_FILTER) {
+ ax88179_read_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_CONTROL, 1, 1, &tmp);
+ tmp ^= AX_VLAN_CONTROL_VFE;
+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_CONTROL, 1, 1, &tmp);
+ if (features & NETIF_F_HW_VLAN_CTAG_FILTER) {
+ for (int i = 0; i < 256; i++) {
+ u16 tmp16;
+ /* Address */
+ tmp = i;
+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_ADDRESS,
+ 1, 1, &tmp);
+ /* Data */
+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_DATA0,
+ 2, 2, &tmp16);
+ tmp = AX_VLAN_CONTROL_WE;
+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_CONTROL,
+ 1, 1, &tmp);
+ }
+ }
+ }
+
+ if (changed & NETIF_F_HW_VLAN_CTAG_RX) {
+ ax88179_read_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_CONTROL, 1, 1, &tmp);
+ tmp ^= AX_VLAN_CONTROL_VSO;
+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_CONTROL, 1, 1, &tmp);
}
return 0;
@@ -1331,6 +1427,8 @@ static const struct net_device_ops ax88179_netdev_ops = {
.ndo_eth_ioctl = usbnet_mii_ioctl,
.ndo_set_rx_mode = ax88179_set_multicast,
.ndo_set_features = ax88179_set_features,
+ .ndo_vlan_rx_add_vid = ax179a_vlan_rx_add_vid,
+ .ndo_vlan_rx_kill_vid = ax179a_vlan_rx_kill_vid,
};
static int ax88179_check_eeprom(struct usbnet *dev)
--
2.47.3
^ permalink raw reply related
* [PATCH 5/9] ax88179_178a: Add support for ethtool pause parameter configuration
From: Birger Koblitz @ 2026-07-01 5:42 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni
Cc: linux-usb, netdev, linux-kernel, Birger Koblitz
In-Reply-To: <20260701-ax88179a-v1-0-13685df67515@birger-koblitz.de>
The AX179A-based chips support pause parameter configuration.
Make it available through ethtool ops.
Signed-off-by: Birger Koblitz <mail@birger-koblitz.de>
---
drivers/net/usb/ax88179_178a.c | 67 ++++++++++++++++++++++++++++++++++++++++++
1 file changed, 67 insertions(+)
diff --git a/drivers/net/usb/ax88179_178a.c b/drivers/net/usb/ax88179_178a.c
index 16528d873e804fde5dcc27659048882eee1c3eaa..586c049c6f7422a853aeae5e9372ead3a6d106c5 100644
--- a/drivers/net/usb/ax88179_178a.c
+++ b/drivers/net/usb/ax88179_178a.c
@@ -956,6 +956,71 @@ static int ax88179_set_link_ksettings(struct net_device *net,
return mii_ethtool_set_link_ksettings(&dev->mii, cmd);
}
+static void ax88179a_get_pauseparam(struct net_device *net, struct ethtool_pauseparam *pause)
+{
+ struct usbnet *dev = netdev_priv(net);
+ struct ax88179_data *data;
+ u16 bmcr, lcladv, rmtadv;
+ u8 cap;
+
+ data = dev->driver_priv;
+
+ if (data->chip_version < AX_VERSION_AX88179A)
+ return;
+
+ bmcr = ax88179_mdio_read(net, dev->mii.phy_id, MII_BMCR);
+ lcladv = ax88179_mdio_read(net, dev->mii.phy_id, MII_ADVERTISE);
+ rmtadv = ax88179_mdio_read(net, dev->mii.phy_id, MII_LPA);
+
+ if (!(bmcr & BMCR_ANENABLE)) {
+ pause->autoneg = 0;
+ pause->rx_pause = 0;
+ pause->tx_pause = 0;
+ return;
+ }
+
+ pause->autoneg = 1;
+
+ cap = mii_resolve_flowctrl_fdx(lcladv, rmtadv);
+
+ if (cap & FLOW_CTRL_RX)
+ pause->rx_pause = 1;
+
+ if (cap & FLOW_CTRL_TX)
+ pause->tx_pause = 1;
+}
+
+static int ax88179a_set_pauseparam(struct net_device *net, struct ethtool_pauseparam *pause)
+{
+ struct usbnet *dev = netdev_priv(net);
+ struct ax88179_data *data;
+ u16 old, new1, bmcr;
+ u8 cap = 0;
+
+ data = dev->driver_priv;
+
+ if (data->chip_version < AX_VERSION_AX88179A)
+ return -EOPNOTSUPP;
+
+ bmcr = ax88179_mdio_read(net, dev->mii.phy_id, MII_BMCR);
+ if (pause->autoneg && !(bmcr & BMCR_ANENABLE))
+ return -EINVAL;
+
+ if (pause->rx_pause)
+ cap |= FLOW_CTRL_RX;
+
+ if (pause->tx_pause)
+ cap |= FLOW_CTRL_TX;
+
+ old = ax88179_mdio_read(net, dev->mii.phy_id, MII_ADVERTISE);
+ new1 = (old & ~(ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM)) |
+ mii_advertise_flowctrl(cap);
+ if (old != new1)
+ ax88179_mdio_write(net, dev->mii.phy_id, MII_ADVERTISE, new1);
+
+ return mii_nway_restart(&dev->mii);
+}
+
static int
ax88179_ethtool_get_eee(struct usbnet *dev, struct ethtool_keee *data)
{
@@ -1130,6 +1195,8 @@ static const struct ethtool_ops ax88179_ethtool_ops = {
.nway_reset = usbnet_nway_reset,
.get_link_ksettings = ax88179_get_link_ksettings,
.set_link_ksettings = ax88179_set_link_ksettings,
+ .get_pauseparam = ax88179a_get_pauseparam,
+ .set_pauseparam = ax88179a_set_pauseparam,
.get_ts_info = ethtool_op_get_ts_info,
};
--
2.47.3
^ permalink raw reply related
* Re: [PATCH v8 07/14] remoteproc: qcom: Select QCOM_PAS generic service
From: Sumit Garg @ 2026-07-01 5:36 UTC (permalink / raw)
To: Konrad Dybcio
Cc: andersson, linux-arm-msm, dri-devel, freedreno, linux-media,
netdev, linux-wireless, ath12k, linux-remoteproc, konradybcio,
robh, krzk+dt, conor+dt, robin.clark, sean, akhilpo, lumag,
abhinav.kumar, jesszhan0024, marijn.suijten, airlied, simona,
vikash.garodia, bod, mchehab, elder, andrew+netdev, davem,
edumazet, kuba, pabeni, jjohnson, mathieu.poirier,
trilokkumar.soni, mukesh.ojha, pavan.kondeti, jorge.ramirez,
tonyh, vignesh.viswanathan, srinivas.kandagatla, amirreza.zarrabi,
jens.wiklander, op-tee, apurupa, skare, linux-kernel, Sumit Garg
In-Reply-To: <64e425ab-dddc-4221-81d3-3283e2961bea@oss.qualcomm.com>
Hi Konrad,
On Tue, Jun 30, 2026 at 02:37:59PM +0200, Konrad Dybcio wrote:
> On 6/26/26 3:34 PM, Sumit Garg wrote:
> > From: Sumit Garg <sumit.garg@oss.qualcomm.com>
> >
> > Select PAS generic service driver to enable support for multiple PAS
> > backends like OP-TEE in addition to SCM.
> >
> > Tested-by: Mukesh Ojha <mukesh.ojha@oss.qualcomm.com> # Lemans
> > Tested-by: Vignesh Viswanathan <vignesh.viswanathan@oss.qualcomm.com> # IPQ9650
> > Signed-off-by: Sumit Garg <sumit.garg@oss.qualcomm.com>
> > ---
> > drivers/remoteproc/Kconfig | 4 +++-
> > 1 file changed, 3 insertions(+), 1 deletion(-)
> >
> > diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig
> > index c521c744e7db..65befdbfa5f7 100644
> > --- a/drivers/remoteproc/Kconfig
> > +++ b/drivers/remoteproc/Kconfig
> > @@ -210,6 +210,7 @@ config QCOM_Q6V5_MSS
> > select QCOM_Q6V5_COMMON
> > select QCOM_RPROC_COMMON
> > select QCOM_SCM
> > + select QCOM_PAS
>
> This is a NOP, SCM already requires QCOM_PAS
That's true but I think we should rather have explicit modules
dependencies whose APIs are being used by their clients. QCOM_SCM is a
special module in this case exporting direct SCM calls apart from being
the backend for the PAS service.
I would prefer explicit dependencies but let me know if you feel
strongly otherwise.
>
> > help
> > Say y here to support the Qualcomm self-authenticating modem
> > subsystem based on Hexagon V5. The TrustZone based system is
> > @@ -230,6 +231,7 @@ config QCOM_Q6V5_PAS
> > select QCOM_Q6V5_COMMON
> > select QCOM_RPROC_COMMON
> > select QCOM_SCM
> > + select QCOM_PAS
>
> Likewise
>
> > help
> > Say y here to support the TrustZone based Peripheral Image Loader for
> > the Qualcomm remote processors. This is commonly used to control
> > @@ -282,7 +284,7 @@ config QCOM_WCNSS_PIL
> > select QCOM_MDT_LOADER
> > select QCOM_PIL_INFO
> > select QCOM_RPROC_COMMON
> > - select QCOM_SCM
> > + select QCOM_PAS
>
> This is OK
>
> _however_
>
> It leads to a situation where no back-ends can be enabled
The TEE backend is enabled by default if ARCH_QCOM is enabled, if you
agree then we can similarly enable SCM backend too.
-Sumit
^ permalink raw reply
* [PATCH] net: stmmac: recalibrate CBS idle slope when EST is enabled
From: muhammad.nazim.amirul.nazle.asmade @ 2026-07-01 5:32 UTC (permalink / raw)
To: netdev, kuba; +Cc: davem, edumazet, pabeni, rmk+kernel, maxime.chevallier
From: Nazim Amirul <muhammad.nazim.amirul.nazle.asmade@altera.com>
When both CBS (Credit-Based Shaper) and EST (Enhanced Scheduled
Transmit, IEEE 802.1Qbv) are active simultaneously, the CBS idle slope
configured for a queue must be adjusted to account for the EST gate
duty cycle.
Under normal CBS operation, credit accumulates continuously at the
configured idle slope rate. However, when EST is enabled, credit can
only accumulate while the queue's transmission gate is open. This means
the effective bandwidth delivered by CBS is reduced proportionally to
the fraction of time the gate is open.
To preserve the intended bandwidth allocation, the idle slope must be
scaled up by the ratio of the cycle time to the gate open time:
idleSlope = operIdleSlope * (cycleTime / gateOpenTime)
Store the time interval (ti_ns) and gate mask for each GCL entry when
configuring EST via tc-taprio, then use these values in tc_setup_cbs to
recalculate the idle slope when EST is active.
If EST is not enabled, the cycle time is zero, or the queue's gate open
time is zero, the idle slope is left unchanged. The recalculated value
is capped at MAX_IDLE_SLOPE_CREDIT to prevent hardware register overflow.
Signed-off-by: Nazim Amirul <muhammad.nazim.amirul.nazle.asmade@altera.com>
---
drivers/net/ethernet/stmicro/stmmac/stmmac.h | 2 +
.../net/ethernet/stmicro/stmmac/stmmac_tc.c | 55 ++++++++++++++++++-
2 files changed, 54 insertions(+), 3 deletions(-)
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac.h b/drivers/net/ethernet/stmicro/stmmac/stmmac.h
index 8ba8f03e1ce0..8eb73ca6e7e4 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac.h
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac.h
@@ -237,6 +237,8 @@ struct stmmac_est {
u32 ter;
u32 gcl_unaligned[EST_GCL];
u32 gcl[EST_GCL];
+ u32 ti_ns[EST_GCL];
+ u32 gates[EST_GCL];
u32 gcl_size;
u32 max_sdu[MTL_MAX_TX_QUEUES];
};
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c
index d78652718599..1215946e49cb 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c
@@ -11,6 +11,8 @@
#include "dwmac5.h"
#include "stmmac.h"
+#define MAX_IDLE_SLOPE_CREDIT 0x1FFFFF
+
static void tc_fill_all_pass_entry(struct stmmac_tc_entry *entry)
{
memset(entry, 0, sizeof(*entry));
@@ -332,13 +334,14 @@ static int tc_init(struct stmmac_priv *priv)
static int tc_setup_cbs(struct stmmac_priv *priv,
struct tc_cbs_qopt_offload *qopt)
{
+ u64 value, scaling = 0, cycle_time_ns = 0, open_time = 0, tti_ns = 0;
u32 tx_queues_count = priv->plat->tx_queues_to_use;
+ u32 gate = 0x1 << qopt->queue;
s64 port_transmit_rate_kbps;
u32 queue = qopt->queue;
+ u32 ptr, idle_slope;
u32 mode_to_use;
- u64 value;
- u32 ptr;
- int ret;
+ int ret, row;
/* Queue 0 is not AVB capable */
if (queue <= 0 || queue >= tx_queues_count)
@@ -402,6 +405,50 @@ static int tc_setup_cbs(struct stmmac_priv *priv,
value = qopt->locredit * 1024ll * 8;
priv->plat->tx_queues_cfg[queue].low_credit = value & GENMASK(31, 0);
+ /* If EST is not enabled, no need to recalibrate idle slope */
+ if (!priv->est)
+ goto config_cbs;
+ if (!priv->est->enable)
+ goto config_cbs;
+
+ cycle_time_ns = (priv->est->ctr[1] * NSEC_PER_SEC) +
+ priv->est->ctr[0];
+ if (!cycle_time_ns)
+ goto config_cbs;
+
+ /* Sum the gate open time for this queue across all GCL entries. Entries
+ * beyond the cycle time are truncated. Any remaining cycle time after
+ * the last GCL entry is treated as gate-open (implicit open).
+ */
+ for (row = 0; row < priv->est->gcl_size; row++) {
+ tti_ns += priv->est->ti_ns[row];
+ if (priv->est->gates[row] & gate)
+ open_time += priv->est->ti_ns[row];
+ if (tti_ns > cycle_time_ns) {
+ if (priv->est->gates[row] & gate)
+ open_time -= tti_ns - cycle_time_ns;
+ break;
+ }
+ }
+ if (tti_ns < cycle_time_ns)
+ open_time += cycle_time_ns - tti_ns;
+ if (!open_time)
+ goto config_cbs;
+
+ /* When EST is active, credit accumulates only when the gate is open,
+ * so scale up the idle slope by the duty cycle:
+ * idleSlope = operIdleSlope * (cycleTime / gateOpenTime)
+ */
+ scaling = cycle_time_ns;
+ do_div(scaling, open_time);
+ idle_slope = priv->plat->tx_queues_cfg[queue].idle_slope;
+ idle_slope *= scaling;
+ if (idle_slope > MAX_IDLE_SLOPE_CREDIT)
+ idle_slope = MAX_IDLE_SLOPE_CREDIT;
+
+ priv->plat->tx_queues_cfg[queue].idle_slope = idle_slope;
+
+config_cbs:
ret = stmmac_config_cbs(priv, priv->hw,
priv->plat->tx_queues_cfg[queue].send_slope,
priv->plat->tx_queues_cfg[queue].idle_slope,
@@ -1031,6 +1078,8 @@ static int tc_taprio_configure(struct stmmac_priv *priv,
}
priv->est->gcl[i] = delta_ns | (gates << wid);
+ priv->est->ti_ns[i] = delta_ns;
+ priv->est->gates[i] = gates;
}
mutex_lock(&priv->est_lock);
--
2.43.7
^ permalink raw reply related
* RE: [Intel-wired-lan] [PATCH net-next] i40e: Avoid repeating RX filter warning
From: Rinitha, SX @ 2026-07-01 5:23 UTC (permalink / raw)
To: Chris Packham, Nguyen, Anthony L, Kitszel, Przemyslaw,
andrew+netdev@lunn.ch, davem@davemloft.net, edumazet@google.com,
kuba@kernel.org, pabeni@redhat.com, Lobakin, Aleksander
Cc: intel-wired-lan@lists.osuosl.org, netdev@vger.kernel.org,
linux-kernel@vger.kernel.org, Blair Steven, Carl Smith
In-Reply-To: <20260514003733.1718771-1-chris.packham@alliedtelesis.co.nz>
> -----Original Message-----
> From: Intel-wired-lan <intel-wired-lan-bounces@osuosl.org> On Behalf Of Chris Packham
> Sent: 14 May 2026 06:08
> To: Nguyen, Anthony L <anthony.l.nguyen@intel.com>; Kitszel, Przemyslaw <przemyslaw.kitszel@intel.com>; andrew+netdev@lunn.ch; davem@davemloft.net; edumazet@google.com; kuba@kernel.org; pabeni@redhat.com; Lobakin, Aleksander <aleksander.lobakin@intel.com>
> Cc: intel-wired-lan@lists.osuosl.org; netdev@vger.kernel.org; linux-kernel@vger.kernel.org; Blair Steven <blair.steven@alliedtelesis.co.nz>; Carl Smith <carl.smith@alliedtelesis.co.nz>; Chris Packham <chris.packham@alliedtelesis.co.nz>
> Subject: [Intel-wired-lan] [PATCH net-next] i40e: Avoid repeating RX filter warning
>
> When the i40e runs out of space for RX filters the driver switches to promiscuous mode and warns that it has done so. In scenarios with a large number of these filters this can generate a lot of warnings. For
example:
>
> $ dmesg -c > /dev/null
> $ ip link add dev br0 type bridge vlan_filtering 1 vlan_default_pvid 1
> $ ip link set dev eth7 master br0
> $ bridge vlan add vid 1 dev eth7 pvid untagged self
> $ bridge vlan add vid 2-4094 dev eth7 tagged
> $ dmesg
> [ 25.601705] i40e 0000:01:00.1: Error LIBIE_AQ_RC_ENOSPC, forcing overflow promiscuous on PF
> [ 25.601833] i40e 0000:01:00.1: Error LIBIE_AQ_RC_ENOSPC, forcing overflow promiscuous on PF
> [ 25.601961] i40e 0000:01:00.1: Error LIBIE_AQ_RC_ENOSPC, forcing overflow promiscuous on PF
> [ 25.602088] i40e 0000:01:00.1: Error LIBIE_AQ_RC_ENOSPC, forcing overflow promiscuous on PF
> [ 25.602216] i40e 0000:01:00.1: Error LIBIE_AQ_RC_ENOSPC, forcing overflow promiscuous on PF
> [ 25.602344] i40e 0000:01:00.1: Error LIBIE_AQ_RC_ENOSPC, forcing overflow promiscuous on PF
> ...
>
> Use test_and_set_bit() so that the warning is only issued when the driver enables promiscuous mode and not on the addition of subsequent RX filters.
>
> Signed-off-by: Chris Packham <chris.packham@alliedtelesis.co.nz>
> ---
>
> Resend with net-next tag
>
> drivers/net/ethernet/intel/i40e/i40e_main.c | 18 ++++++++++--------
> 1 file changed, 10 insertions(+), 8 deletions(-)
>
Tested-by: Rinitha S <sx.rinitha@intel.com> (A Contingent worker at Intel)
^ permalink raw reply
* Re: [PATCH net 3/9] netfilter: ipset: fix race between dump and ip_set_list resize
From: Florian Westphal @ 2026-07-01 5:09 UTC (permalink / raw)
To: netdev; +Cc: netfilter-devel, kadlec, xmei5
In-Reply-To: <20260630045243.2657-4-fw@strlen.de>
Florian Westphal <fw@strlen.de> wrote:
> From: Xiang Mei <xmei5@asu.edu>
Xiang, Jozsef, could you please have a look at
https://sashiko.dev/#/patchset/20260630045243.2657-1-fw%40strlen.de
AFAICS it's correct but should be handled in a followup patch rather
than a v2.
Thanks!
^ permalink raw reply
* Re: [PATCH net 5/9] netfilter: nfnetlink_cthelper: cap to maximum number of expectation per master
From: Florian Westphal @ 2026-07-01 5:04 UTC (permalink / raw)
To: netdev
Cc: Paolo Abeni, David S. Miller, Eric Dumazet, Jakub Kicinski,
netfilter-devel, pablo
In-Reply-To: <20260630045243.2657-6-fw@strlen.de>
Florian Westphal <fw@strlen.de> wrote:
> From: Pablo Neira Ayuso <pablo@netfilter.org>
>
> If userspace helper policy updates sets maximum number of expectation to
> zero, cap it to NF_CT_EXPECT_MAX_CNT (255) on updates too.
>
> Fixes: 397c8300972f ("netfilter: nf_conntrack_helper: cap maximum number of expectation at helper registration")
> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
> Signed-off-by: Florian Westphal <fw@strlen.de>
Pablo, can you please check
https://sashiko.dev/#/message/20260630045243.2657-6-fw%40strlen.de
?
AFAICS the comment is correct, but it should be handled in a
followup patch rather than a v2.
^ permalink raw reply
* Re: [PATCH bpf-next v5 0/3] bpf, sockmap: reject a packet-modifying SK_SKB stream parser
From: Jiayuan Chen @ 2026-07-01 5:03 UTC (permalink / raw)
To: Ihor Solodrai, Sechang Lim, Alexei Starovoitov, Daniel Borkmann,
Andrii Nakryiko, John Fastabend, Jakub Sitnicki, Eduard Zingerman
Cc: Eric Dumazet, Kuniyuki Iwashima, Paolo Abeni, Willem de Bruijn,
David S . Miller, Jakub Kicinski, Martin KaFai Lau, Song Liu,
Yonghong Song, Jiri Olsa, Kumar Kartikeya Dwivedi, Simon Horman,
Shuah Khan, Bobby Eshleman, netdev, bpf, linux-kselftest,
linux-kernel
In-Reply-To: <e3a91acd-2b4d-4e93-a3bb-a0e9ee5ede0f@linux.dev>
On 7/1/26 12:48 PM, Ihor Solodrai wrote:
> On 2026-06-19 7:44 p.m., Sechang Lim wrote:
>> A BPF_PROG_TYPE_SK_SKB stream parser runs on strparser's message head,
>> which can chain skbs through frag_list. A parser that resizes the skb
>> frees the frag_list segments that strparser still tracks through
>> skb_nextp, leading to a use-after-free.
>>
>> A stream parser is only meant to measure the next message, not to modify
>> the packet, so reject a packet-modifying parser at attach time.
>>
>> v5:
>> - target bpf-next instead of bpf
>> - add Reviewed-by tag (Jiayuan Chen)
>>
>> v4:
>> -
>> https://lore.kernel.org/all/20260619062959.3277612-1-rhkrqnwk98@gmail.com/
>>
>> v3:
>> -
>> https://lore.kernel.org/all/20260618102718.2331468-1-rhkrqnwk98@gmail.com/
>>
>> v2:
>> -
>> https://lore.kernel.org/all/20260612123553.2724240-1-rhkrqnwk98@gmail.com/
>>
>> v1:
>> -
>> https://lore.kernel.org/all/20260609112316.3685738-1-rhkrqnwk98@gmail.com/
>>
>> Sechang Lim (3):
>> selftests/bpf: don't modify the skb in the strparser parser prog
>> bpf, sockmap: reject a packet-modifying SK_SKB stream parser
>> selftests/bpf: test rejection of a packet-modifying SK_SKB stream
>> parser
>
>
> Hi Sechang, all,
>
> This series broke test_maps (test_sockmap subtest) on the bpf
> tree. Currently on BPF CI the test fails on bpf, but passes on
> bpf-next (it doesn't have the series yet).
>
> test_maps fails with:
>
> + taskset 0xF ./test_maps
> [ 8.352378] clocksource: Watchdog remote CPU 2 read timed out
> Failed sockmap unexpected timeout
>
> See test_maps.c:995 in test_sockmap(): the 30s select() times out and
> test_maps exits 1. Note there is no "Failed stream parser bpf prog
> attach" message, the parser attaches fine.
>
> The series was merged into bpf on 2026-06-26 00:42 UTC
>
> CI runs:
> last good (pre-merge, 06-25):
> https://github.com/kernel-patches/bpf/actions/runs/28158326456
> first bad (post-merge, 06-26):
> https://github.com/kernel-patches/bpf/actions/runs/28210181858
> recent bad (06-30):
> https://github.com/kernel-patches/bpf/actions/runs/28475936023
>
> Confirmed locally reverting the 3 commits and rebuilding makes
> test_sockmap pass again.
>
> Could you please help investigate?
I'll work on this.
^ permalink raw reply
* Re: [PATCH bpf-next v5 0/3] bpf, sockmap: reject a packet-modifying SK_SKB stream parser
From: Ihor Solodrai @ 2026-07-01 4:48 UTC (permalink / raw)
To: Sechang Lim, Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko,
John Fastabend, Jakub Sitnicki, Eduard Zingerman
Cc: Eric Dumazet, Kuniyuki Iwashima, Paolo Abeni, Willem de Bruijn,
David S . Miller, Jakub Kicinski, Martin KaFai Lau, Song Liu,
Yonghong Song, Jiri Olsa, Kumar Kartikeya Dwivedi, Simon Horman,
Shuah Khan, Jiayuan Chen, Bobby Eshleman, netdev, bpf,
linux-kselftest, linux-kernel
In-Reply-To: <20260620024423.4141004-1-rhkrqnwk98@gmail.com>
On 2026-06-19 7:44 p.m., Sechang Lim wrote:
> A BPF_PROG_TYPE_SK_SKB stream parser runs on strparser's message head,
> which can chain skbs through frag_list. A parser that resizes the skb
> frees the frag_list segments that strparser still tracks through
> skb_nextp, leading to a use-after-free.
>
> A stream parser is only meant to measure the next message, not to modify
> the packet, so reject a packet-modifying parser at attach time.
>
> v5:
> - target bpf-next instead of bpf
> - add Reviewed-by tag (Jiayuan Chen)
>
> v4:
> - https://lore.kernel.org/all/20260619062959.3277612-1-rhkrqnwk98@gmail.com/
>
> v3:
> - https://lore.kernel.org/all/20260618102718.2331468-1-rhkrqnwk98@gmail.com/
>
> v2:
> - https://lore.kernel.org/all/20260612123553.2724240-1-rhkrqnwk98@gmail.com/
>
> v1:
> - https://lore.kernel.org/all/20260609112316.3685738-1-rhkrqnwk98@gmail.com/
>
> Sechang Lim (3):
> selftests/bpf: don't modify the skb in the strparser parser prog
> bpf, sockmap: reject a packet-modifying SK_SKB stream parser
> selftests/bpf: test rejection of a packet-modifying SK_SKB stream
> parser
Hi Sechang, all,
This series broke test_maps (test_sockmap subtest) on the bpf
tree. Currently on BPF CI the test fails on bpf, but passes on
bpf-next (it doesn't have the series yet).
test_maps fails with:
+ taskset 0xF ./test_maps
[ 8.352378] clocksource: Watchdog remote CPU 2 read timed out
Failed sockmap unexpected timeout
See test_maps.c:995 in test_sockmap(): the 30s select() times out and
test_maps exits 1. Note there is no "Failed stream parser bpf prog
attach" message, the parser attaches fine.
The series was merged into bpf on 2026-06-26 00:42 UTC
CI runs:
last good (pre-merge, 06-25):
https://github.com/kernel-patches/bpf/actions/runs/28158326456
first bad (post-merge, 06-26):
https://github.com/kernel-patches/bpf/actions/runs/28210181858
recent bad (06-30):
https://github.com/kernel-patches/bpf/actions/runs/28475936023
Confirmed locally reverting the 3 commits and rebuilding makes
test_sockmap pass again.
Could you please help investigate?
Thanks!
>
> net/core/sock_map.c | 20 ++++++++++++
> .../selftests/bpf/prog_tests/sockmap_strp.c | 31 +++++++++++++++++++
> .../selftests/bpf/progs/sockmap_parse_prog.c | 22 -------------
> .../selftests/bpf/progs/test_sockmap_strp.c | 7 +++++
> 4 files changed, 58 insertions(+), 22 deletions(-)
>
^ permalink raw reply
* RE: [PATCH net v2 1/2] net: ethernet: oa_tc6: Protect skb pointer used by two different kernel instances
From: Selvamani Rajagopal @ 2026-07-01 4:15 UTC (permalink / raw)
To: Jakub Kicinski
Cc: Selvamani Rajagopal via B4 Relay, Parthiban Veerasooran,
Andrew Lunn, Piergiorgio Beruto, David S. Miller, Eric Dumazet,
Paolo Abeni, netdev@vger.kernel.org, linux-kernel@vger.kernel.org,
Andrew Lunn
In-Reply-To: <20260630154647.468076db@kernel.org>
> -----Original Message-----
> From: Jakub Kicinski <kuba@kernel.org>
> Sent: Tuesday, June 30, 2026 3:47 PM
> Subject: Re: [PATCH net v2 1/2] net: ethernet: oa_tc6: Protect skb pointer used by two
> different kernel instances
>
> > I believe xmit path and IRQ thread would run in different kernel
> > instances. Imagine oa_tc6_try_spi_transfer call fails in threaded
> > IRQ. It would set disable_irq. If xmit function didn't see that when
> > it checked, but it is set before placing skb buffer in the
> > waiting_tx_skb pointer (due to skb_linearize for example), the skb
> > would be stuck in waiting_tx_skb.
>
> Perhaps, but wouldn't that cause a stall not a leak?
I should have mentioned disable_traffic flag instead of disable_irq, though
Irq would be disabled when this flag is set.
Once it is set, there won't be anymore traffic, unless driver is unloaded and
reloaded. At this state, tx queue would be blocked The skb pointer
would be held in waiting_tx_skb for ever. (skb lost as far as kernel is concerned)
> Please do your digging and submit high quality patches which don't
While I appreciate you spending time to review this, I do believe the changes in the patch
is the right approach. Not sure what I am missing here.
^ permalink raw reply
* [PATCH v3] net/liquidio: drop cached VF pci_dev LUT
From: Yuho Choi @ 2026-07-01 4:08 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, netdev
Cc: Kory Maincent, Zilin Guan, Arend van Spriel, Marco Crivellari,
Uwe Kleine-König (The Capable Hub), Vadim Fedorenko,
linux-kernel, Yuho Choi
The PF SR-IOV enable path caches VF pci_dev pointers in
dpiring_to_vfpcidev_lut[] by iterating with pci_get_device(). Those
entries do not own a reference, because the iterator drops the previous
device reference on each step. The cached pointer is then dereferenced
later when handling OCTEON_VF_FLR_REQUEST.
Replace the cached VF mapping with runtime lookup on the mailbox DPI
ring: derive the VF index from q_no, resolve the VF via exported PCI
IOV helpers, validate it with the PF pointer and VF ID, then issue
pcie_flr() and drop the reference with pci_dev_put(). Remove the
unused VF lookup table initialization and cleanup.
Fixes: ca6139ffc67ee ("liquidio CN23XX: sysfs VF config support")
Fixes: 8c978d059224 ("liquidio CN23XX: Mailbox support")
Signed-off-by: Yuho Choi <dbgh9129@gmail.com>
---
Changes in v3:
- Drop unrelated Xgene PCI probe note from the commit message.
Changes in v2:
- Replace use of pci_iov_virtfn_bus() with runtime VF lookup using exported helpers.
.../net/ethernet/cavium/liquidio/lio_main.c | 27 ---------------
.../ethernet/cavium/liquidio/octeon_device.h | 3 --
.../ethernet/cavium/liquidio/octeon_mailbox.c | 33 ++++++++++++++++++-
3 files changed, 32 insertions(+), 31 deletions(-)
diff --git a/drivers/net/ethernet/cavium/liquidio/lio_main.c b/drivers/net/ethernet/cavium/liquidio/lio_main.c
index 0db08ac3d098..e303956b4bf1 100644
--- a/drivers/net/ethernet/cavium/liquidio/lio_main.c
+++ b/drivers/net/ethernet/cavium/liquidio/lio_main.c
@@ -3779,9 +3779,7 @@ static int setup_nic_devices(struct octeon_device *octeon_dev)
static int octeon_enable_sriov(struct octeon_device *oct)
{
unsigned int num_vfs_alloced = oct->sriov_info.num_vfs_alloced;
- struct pci_dev *vfdev;
int err;
- u32 u;
if (OCTEON_CN23XX_PF(oct) && num_vfs_alloced) {
err = pci_enable_sriov(oct->pci_dev,
@@ -3794,23 +3792,6 @@ static int octeon_enable_sriov(struct octeon_device *oct)
return err;
}
oct->sriov_info.sriov_enabled = 1;
-
- /* init lookup table that maps DPI ring number to VF pci_dev
- * struct pointer
- */
- u = 0;
- vfdev = pci_get_device(PCI_VENDOR_ID_CAVIUM,
- OCTEON_CN23XX_VF_VID, NULL);
- while (vfdev) {
- if (vfdev->is_virtfn &&
- (vfdev->physfn == oct->pci_dev)) {
- oct->sriov_info.dpiring_to_vfpcidev_lut[u] =
- vfdev;
- u += oct->sriov_info.rings_per_vf;
- }
- vfdev = pci_get_device(PCI_VENDOR_ID_CAVIUM,
- OCTEON_CN23XX_VF_VID, vfdev);
- }
}
return num_vfs_alloced;
@@ -3818,8 +3799,6 @@ static int octeon_enable_sriov(struct octeon_device *oct)
static int lio_pci_sriov_disable(struct octeon_device *oct)
{
- int u;
-
if (pci_vfs_assigned(oct->pci_dev)) {
dev_err(&oct->pci_dev->dev, "VFs are still assigned to VMs.\n");
return -EPERM;
@@ -3827,12 +3806,6 @@ static int lio_pci_sriov_disable(struct octeon_device *oct)
pci_disable_sriov(oct->pci_dev);
- u = 0;
- while (u < MAX_POSSIBLE_VFS) {
- oct->sriov_info.dpiring_to_vfpcidev_lut[u] = NULL;
- u += oct->sriov_info.rings_per_vf;
- }
-
oct->sriov_info.num_vfs_alloced = 0;
dev_info(&oct->pci_dev->dev, "oct->pf_num:%d disabled VFs\n",
oct->pf_num);
diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_device.h b/drivers/net/ethernet/cavium/liquidio/octeon_device.h
index 19344b21f8fb..858a0fff2cc0 100644
--- a/drivers/net/ethernet/cavium/liquidio/octeon_device.h
+++ b/drivers/net/ethernet/cavium/liquidio/octeon_device.h
@@ -390,9 +390,6 @@ struct octeon_sriov_info {
struct lio_trusted_vf trusted_vf;
- /*lookup table that maps DPI ring number to VF pci_dev struct pointer*/
- struct pci_dev *dpiring_to_vfpcidev_lut[MAX_POSSIBLE_VFS];
-
u64 vf_macaddr[MAX_POSSIBLE_VFS];
u16 vf_vlantci[MAX_POSSIBLE_VFS];
diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_mailbox.c b/drivers/net/ethernet/cavium/liquidio/octeon_mailbox.c
index ad685f5d0a13..697fcdc41e3c 100644
--- a/drivers/net/ethernet/cavium/liquidio/octeon_mailbox.c
+++ b/drivers/net/ethernet/cavium/liquidio/octeon_mailbox.c
@@ -26,6 +26,31 @@
#include "octeon_mailbox.h"
#include "cn23xx_pf_device.h"
+static struct pci_dev *lio_vf_pci_dev_by_qno(struct octeon_device *oct, u32 q_no)
+{
+ struct pci_dev *vfdev = NULL;
+ int vfidx;
+
+ if (!oct->sriov_info.rings_per_vf)
+ return NULL;
+
+ if (q_no % oct->sriov_info.rings_per_vf)
+ return NULL;
+
+ vfidx = q_no / oct->sriov_info.rings_per_vf;
+ if (vfidx >= oct->sriov_info.num_vfs_alloced)
+ return NULL;
+
+ while ((vfdev = pci_get_device(PCI_VENDOR_ID_CAVIUM,
+ OCTEON_CN23XX_VF_VID, vfdev))) {
+ if (pci_physfn(vfdev) && pci_physfn(vfdev) == oct->pci_dev &&
+ pci_iov_vf_id(vfdev) == vfidx)
+ return vfdev;
+ }
+
+ return NULL;
+}
+
/**
* octeon_mbox_read:
* @mbox: Pointer mailbox
@@ -237,6 +262,7 @@ static int octeon_mbox_process_cmd(struct octeon_mbox *mbox,
struct octeon_mbox_cmd *mbox_cmd)
{
struct octeon_device *oct = mbox->oct_dev;
+ struct pci_dev *vfdev;
switch (mbox_cmd->msg.s.cmd) {
case OCTEON_VF_ACTIVE:
@@ -260,7 +286,12 @@ static int octeon_mbox_process_cmd(struct octeon_mbox *mbox,
dev_info(&oct->pci_dev->dev,
"got a request for FLR from VF that owns DPI ring %u\n",
mbox->q_no);
- pcie_flr(oct->sriov_info.dpiring_to_vfpcidev_lut[mbox->q_no]);
+ vfdev = lio_vf_pci_dev_by_qno(oct, mbox->q_no);
+ if (!vfdev)
+ break;
+
+ pcie_flr(vfdev);
+ pci_dev_put(vfdev);
break;
case OCTEON_PF_CHANGED_VF_MACADDR:
--
2.43.0
^ permalink raw reply related
* Re: [PATCH v2 0/7] vmsplice: fix some problems in my previous vmsplice patchset
From: Askar Safin @ 2026-07-01 4:00 UTC (permalink / raw)
To: Christian Brauner
Cc: David Hildenbrand (Arm), akpm, avagin, axboe, collin.funk1,
david.laight.linux, dhowells, fuse-devel, hch, jack, joannelkoong,
kernel, linux-api, linux-fsdevel, linux-kernel, linux-mm, luto,
metze, miklos, netdev, patches, pfalcato, torvalds, val, viro, w,
willy
In-Reply-To: <20260629-bauland-knabbern-abgeladen-c0acbfa62cc2@brauner>
On Mon, Jun 29, 2026 at 11:56 AM Christian Brauner <brauner@kernel.org> wrote:
> The amount of regression reports that we got in short succession doesn't
> make it likely that we can merge a plain degradation.
Let me repeat: this v2 patchset fixes all regressions found so far,
except for major CRIU performance regression
--
Askar Safin
^ permalink raw reply
* [PATCH v2] xfrm: iptfs: propagate SKBFL_SHARED_FRAG in iptfs_skb_add_frags()
From: Chen YanJun @ 2026-07-01 3:31 UTC (permalink / raw)
To: steffen.klassert, herbert, davem; +Cc: netdev, moomichen
From: Chen YanJun <moomichen@tencent.com>
When iptfs_skb_add_frags() copies frag references from the source
frag walk into a new SKB, it increments the page reference count via
__skb_frag_ref() but does not propagate SKBFL_SHARED_FRAG to the
destination SKB's skb_shinfo->flags.
If the source SKB carries shared frags (e.g. from a page-pool backed
receive path), the new inner SKB will appear to ESP as having privately
owned frags. A subsequent esp_input() call for a nested transport-mode
SA then takes the no-COW fast path and decrypts in place, writing over
pages that are still referenced by the outer IPTFS SKB. This causes
kernel-visible memory corruption and can trigger a panic.
All other frag-transfer helpers in the kernel (skb_try_coalesce,
skb_gro_receive, __pskb_copy_fclone, skb_shift, skb_segment) correctly
propagate SKBFL_SHARED_FRAG; align iptfs_skb_add_frags() with this
convention by setting the flag inside the loop immediately after
__skb_frag_ref() and nr_frags++, so every exit path that attaches a frag
unconditionally propagates SKBFL_SHARED_FRAG.
Fixes: 5f2b6a909574 ("xfrm: iptfs: add skb-fragment sharing code")
Signed-off-by: Chen YanJun <moomichen@tencent.com>
---
v2: move SKBFL_SHARED_FRAG assignment inside the loop, immediately after
__skb_frag_ref()/nr_frags++, to also cover the early-return path
that fires when the requested length is satisfied mid-frag (pointed
out by maintainer review of v1).
---
net/xfrm/xfrm_iptfs.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/net/xfrm/xfrm_iptfs.c b/net/xfrm/xfrm_iptfs.c
index ad810d1f97c0..597aedeac26e 100644
--- a/net/xfrm/xfrm_iptfs.c
+++ b/net/xfrm/xfrm_iptfs.c
@@ -480,6 +480,7 @@ static int iptfs_skb_add_frags(struct sk_buff *skb,
}
__skb_frag_ref(tofrag);
shinfo->nr_frags++;
+ shinfo->flags |= SKBFL_SHARED_FRAG;
/* see if we are done */
fraglen = tofrag->len;
--
2.47.0
^ permalink raw reply related
* [PATCH net-next v2] ipv4: hold a consistent view of rt->dst.dev under RCU
From: xuanqiang.luo @ 2026-07-01 3:24 UTC (permalink / raw)
To: David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
David Ahern, Ido Schimmel
Cc: Simon Horman, Kuniyuki Iwashima, netdev, linux-kernel,
Xuanqiang Luo
From: Xuanqiang Luo <luoxuanqiang@kylinos.cn>
rt_flush_dev() walks the per-CPU uncached route list and rewrites
rt->dst.dev in-place to blackhole_netdev under spin_lock_bh().
This lock does not exclude RCU readers, which may load rt->dst.dev
multiple times within a single rcu_read_lock() region.
ip_rt_send_redirect() is a typical example: it reads rt->dst.dev
three times to obtain in_dev, the L3 master ifindex, and net.
A concurrent device unregistration can repoint rt->dst.dev to
blackhole_netdev between those reads, making the reader combine
state from two different net_devices — for instance, an in_dev
from the real device but a netns and peer lookup from the blackhole
device. ip_rt_get_source() has the same problem: it reads
rt->dst.dev four times to obtain the output ifindex, the netns,
and the source address, so a concurrent flush can cause the source
selection to mix state from different devices.
Take a single dst_dev_rcu() snapshot of rt->dst.dev at the start
of each affected RCU reader and use that snapshot throughout, so
concurrent flushes cannot cause mid-function inconsistency.
Publish the in-place write in rt_flush_dev() with rcu_assign_pointer()
to match the readers.
Fixes: caacf05e5ad1a ("ipv4: Properly purge netdev references on uncached routes.")
Signed-off-by: Xuanqiang Luo <luoxuanqiang@kylinos.cn>
---
v2:
- Use dst_dev_rcu() and dev_net_rcu() for the RCU readers.
- Use rcu_assign_pointer() when publishing the uncached route device
replacement.
- Slightly adjust the commit message wording because this issue was found
by inspection, not from an observed user-visible failure.
v1: https://lore.kernel.org/all/20260630094250.29386-1-xuanqiang.luo@linux.dev/
net/ipv4/route.c | 29 +++++++++++++++++------------
1 file changed, 17 insertions(+), 12 deletions(-)
diff --git a/net/ipv4/route.c b/net/ipv4/route.c
index 3f3de5164d6e5..57f38467e6d0c 100644
--- a/net/ipv4/route.c
+++ b/net/ipv4/route.c
@@ -873,6 +873,7 @@ static void ipv4_negative_advice(struct sock *sk,
void ip_rt_send_redirect(struct sk_buff *skb)
{
struct rtable *rt = skb_rtable(skb);
+ struct net_device *dev;
struct in_device *in_dev;
struct inet_peer *peer;
struct net *net;
@@ -880,15 +881,16 @@ void ip_rt_send_redirect(struct sk_buff *skb)
int vif;
rcu_read_lock();
- in_dev = __in_dev_get_rcu(rt->dst.dev);
+ dev = dst_dev_rcu(&rt->dst);
+ in_dev = __in_dev_get_rcu(dev);
if (!in_dev || !IN_DEV_TX_REDIRECTS(in_dev)) {
rcu_read_unlock();
return;
}
log_martians = IN_DEV_LOG_MARTIANS(in_dev);
- vif = l3mdev_master_ifindex_rcu(rt->dst.dev);
+ vif = l3mdev_master_ifindex_rcu(dev);
- net = dev_net(rt->dst.dev);
+ net = dev_net_rcu(dev);
peer = inet_getpeer_v4(net->ipv4.peers, ip_hdr(skb)->saddr, vif);
if (!peer) {
rcu_read_unlock();
@@ -1287,29 +1289,32 @@ void ip_rt_get_source(u8 *addr, struct sk_buff *skb, struct rtable *rt)
{
__be32 src;
- if (rt_is_output_route(rt))
+ rcu_read_lock();
+ if (rt_is_output_route(rt)) {
src = ip_hdr(skb)->saddr;
- else {
+ } else {
struct fib_result res;
struct iphdr *iph = ip_hdr(skb);
+ struct net_device *dev = dst_dev_rcu(&rt->dst);
+ struct net *net = dev_net_rcu(dev);
struct flowi4 fl4 = {
.daddr = iph->daddr,
.saddr = iph->saddr,
.flowi4_dscp = ip4h_dscp(iph),
- .flowi4_oif = rt->dst.dev->ifindex,
+ .flowi4_oif = dev->ifindex,
.flowi4_iif = skb->dev->ifindex,
.flowi4_mark = skb->mark,
};
- rcu_read_lock();
- if (fib_lookup(dev_net(rt->dst.dev), &fl4, &res, 0) == 0)
- src = fib_result_prefsrc(dev_net(rt->dst.dev), &res);
+ if (fib_lookup(net, &fl4, &res, 0) == 0)
+ src = fib_result_prefsrc(net, &res);
else
- src = inet_select_addr(rt->dst.dev,
+ src = inet_select_addr(dev,
rt_nexthop(rt, iph->daddr),
RT_SCOPE_UNIVERSE);
- rcu_read_unlock();
}
+ rcu_read_unlock();
+
memcpy(addr, &src, 4);
}
@@ -1565,7 +1570,7 @@ void rt_flush_dev(struct net_device *dev)
list_for_each_entry_safe(rt, safe, &ul->head, dst.rt_uncached) {
if (rt->dst.dev != dev)
continue;
- rt->dst.dev = blackhole_netdev;
+ rcu_assign_pointer(rt->dst.dev_rcu, blackhole_netdev);
netdev_ref_replace(dev, blackhole_netdev,
&rt->dst.dev_tracker, GFP_ATOMIC);
list_del_init(&rt->dst.rt_uncached);
--
2.43.0
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox