* 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
* [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: [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 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
* [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 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 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 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
* 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 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
* [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 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 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 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
* 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
* 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] netfilter: x_tables: replace strlcat() with snprintf()
From: Ian Bridges @ 2026-07-01 6:25 UTC (permalink / raw)
To: David Laight
Cc: Pablo Neira Ayuso, Florian Westphal, Phil Sutter, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
netfilter-devel, coreteam, netdev, linux-kernel, linux-hardening
In-Reply-To: <20260627221643.1e837496@pumpkin>
On Sat, Jun 27, 2026 at 10:16:43PM +0100, David Laight wrote:
> On Fri, 26 Jun 2026 17:25:35 -0500
> Ian Bridges <icb@fastmail.org> wrote:
>
> > In preparation for removing the deprecated strlcat() API[1], replace the
> > strscpy()/strlcat() pairs in xt_proto_init() and xt_proto_fini() with
> > snprintf(), which builds each /proc file name in a single call.
> >
> > Each name is "<prefix><suffix>", where <prefix> is the address-family
> > string xt_prefix[af] and <suffix> is one of the FORMAT_TABLES,
> > FORMAT_MATCHES or FORMAT_TARGETS literals. snprintf() with a "%s%s"
> > format produces the same NUL-terminated, length-bounded string as the
> > strscpy()/strlcat() chain it replaces, so the proc entry names are
> > unchanged.
> >
> > Link: https://github.com/KSPP/linux/issues/370 [1]
> > Signed-off-by: Ian Bridges <icb@fastmail.org>
> > ---
> > net/netfilter/x_tables.c | 24 ++++++++----------------
> > 1 file changed, 8 insertions(+), 16 deletions(-)
> >
> > diff --git a/net/netfilter/x_tables.c b/net/netfilter/x_tables.c
> > index 4e6708c23922..56f4546be336 100644
> > --- a/net/netfilter/x_tables.c
> > +++ b/net/netfilter/x_tables.c
> > @@ -2033,8 +2033,7 @@ int xt_proto_init(struct net *net, u_int8_t af)
> > root_uid = make_kuid(net->user_ns, 0);
> > root_gid = make_kgid(net->user_ns, 0);
> >
> > - strscpy(buf, xt_prefix[af], sizeof(buf));
> > - strlcat(buf, FORMAT_TABLES, sizeof(buf));
> > + snprintf(buf, sizeof(buf), "%s%s", xt_prefix[af], FORMAT_TABLES);
>
> If you are going to use snprintf either paste the strings together:
> snprintf(buf, sizeof(buf), "%s" FORMAT_TABLES, xt_prefix[af]);
> or prepend the "%s" onto the #define of FORMAT_TABLES itself:
> snprintf(buf, sizeof(buf), FORMAT_TABLES, xt_prefix[af]);
>
I learned something new today, thanks. I'll use the first form in v2.
> FORMAT_TABLES should also be FORMAT_NAMES.
The macro is already named FORMAT_TABLES today, so that rename would
be a cleanup of pre-existing code rather than part of the strlcat
conversion. I'm happy to fold it into v2 if a maintainer is fine
including the tidy-up in this patch.
Thanks for the review,
Ian
>
> -- David
>
> > proc = proc_create_net_data(buf, 0440, net->proc_net, &xt_table_seq_ops,
> > sizeof(struct seq_net_private),
> > (void *)(unsigned long)af);
> > @@ -2043,8 +2042,7 @@ int xt_proto_init(struct net *net, u_int8_t af)
> > if (uid_valid(root_uid) && gid_valid(root_gid))
> > proc_set_user(proc, root_uid, root_gid);
> >
> > - strscpy(buf, xt_prefix[af], sizeof(buf));
> > - strlcat(buf, FORMAT_MATCHES, sizeof(buf));
> > + snprintf(buf, sizeof(buf), "%s%s", xt_prefix[af], FORMAT_MATCHES);
> > proc = proc_create_seq_private(buf, 0440, net->proc_net,
> > &xt_match_seq_ops, sizeof(struct nf_mttg_trav),
> > (void *)(unsigned long)af);
> > @@ -2053,8 +2051,7 @@ int xt_proto_init(struct net *net, u_int8_t af)
> > if (uid_valid(root_uid) && gid_valid(root_gid))
> > proc_set_user(proc, root_uid, root_gid);
> >
> > - strscpy(buf, xt_prefix[af], sizeof(buf));
> > - strlcat(buf, FORMAT_TARGETS, sizeof(buf));
> > + snprintf(buf, sizeof(buf), "%s%s", xt_prefix[af], FORMAT_TARGETS);
> > proc = proc_create_seq_private(buf, 0440, net->proc_net,
> > &xt_target_seq_ops, sizeof(struct nf_mttg_trav),
> > (void *)(unsigned long)af);
> > @@ -2068,13 +2065,11 @@ int xt_proto_init(struct net *net, u_int8_t af)
> >
> > #ifdef CONFIG_PROC_FS
> > out_remove_matches:
> > - strscpy(buf, xt_prefix[af], sizeof(buf));
> > - strlcat(buf, FORMAT_MATCHES, sizeof(buf));
> > + snprintf(buf, sizeof(buf), "%s%s", xt_prefix[af], FORMAT_MATCHES);
> > remove_proc_entry(buf, net->proc_net);
> >
> > out_remove_tables:
> > - strscpy(buf, xt_prefix[af], sizeof(buf));
> > - strlcat(buf, FORMAT_TABLES, sizeof(buf));
> > + snprintf(buf, sizeof(buf), "%s%s", xt_prefix[af], FORMAT_TABLES);
> > remove_proc_entry(buf, net->proc_net);
> > out:
> > return -1;
> > @@ -2087,16 +2082,13 @@ void xt_proto_fini(struct net *net, u_int8_t af)
> > #ifdef CONFIG_PROC_FS
> > char buf[XT_FUNCTION_MAXNAMELEN];
> >
> > - strscpy(buf, xt_prefix[af], sizeof(buf));
> > - strlcat(buf, FORMAT_TABLES, sizeof(buf));
> > + snprintf(buf, sizeof(buf), "%s%s", xt_prefix[af], FORMAT_TABLES);
> > remove_proc_entry(buf, net->proc_net);
> >
> > - strscpy(buf, xt_prefix[af], sizeof(buf));
> > - strlcat(buf, FORMAT_TARGETS, sizeof(buf));
> > + snprintf(buf, sizeof(buf), "%s%s", xt_prefix[af], FORMAT_TARGETS);
> > remove_proc_entry(buf, net->proc_net);
> >
> > - strscpy(buf, xt_prefix[af], sizeof(buf));
> > - strlcat(buf, FORMAT_MATCHES, sizeof(buf));
> > + snprintf(buf, sizeof(buf), "%s%s", xt_prefix[af], FORMAT_MATCHES);
> > remove_proc_entry(buf, net->proc_net);
> > #endif /*CONFIG_PROC_FS*/
> > }
>
^ permalink raw reply
* Re: [PATCH] netfilter: x_tables: replace strlcat() with snprintf()
From: Florian Westphal @ 2026-07-01 6:31 UTC (permalink / raw)
To: Ian Bridges
Cc: David Laight, Pablo Neira Ayuso, Phil Sutter, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
netfilter-devel, coreteam, netdev, linux-kernel, linux-hardening
In-Reply-To: <akSy4AzKgPffteU7@dev>
Ian Bridges <icb@fastmail.org> wrote:
> I learned something new today, thanks. I'll use the first form in v2.
>
> > FORMAT_TABLES should also be FORMAT_NAMES.
No. The name is fine.
> The macro is already named FORMAT_TABLES today, so that rename would
> be a cleanup of pre-existing code rather than part of the strlcat
> conversion. I'm happy to fold it into v2 if a maintainer is fine
> including the tidy-up in this patch.
No need for a v2, I mangled this patch locally already to use
"%s_FOO" in FORMAT_FOO.
^ permalink raw reply
* Re: [PATCH net 4/9] netfilter: nf_conntrack_sip: validate skb_dst() before accessing it
From: Florian Westphal @ 2026-07-01 6:36 UTC (permalink / raw)
To: netdev
Cc: Paolo Abeni, David S. Miller, Eric Dumazet, Jakub Kicinski,
netfilter-devel, pablo
In-Reply-To: <20260630045243.2657-5-fw@strlen.de>
Florian Westphal <fw@strlen.de> wrote:
> From: Pablo Neira Ayuso <pablo@netfilter.org>
>
> tc ingress and openvswitch do not guarantee routing information to be
> available. These subsystems use the conntrack helper infrastructure, and
> the SIP helper relies on the skb_dst() to be present if
> sip_external_media is set to 1 (which is disabled by default as a module
> parameter).
The sashiko drive-by appears real, I submitted a patch for it.
Its not a regression added by this patch but a unrelated issue.
https://patchwork.ozlabs.org/project/netfilter-devel/patch/20260701062922.9660-1-fw@strlen.de/
^ permalink raw reply
* Re: [PATCH] net: airoha: fix MIB stats collection to be lossless
From: Aniket Negi @ 2026-07-01 6:38 UTC (permalink / raw)
To: Lorenzo Bianconi
Cc: Andrew Lunn, David S . Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, linux-arm-kernel, linux-mediatek, netdev,
linux-kernel, aniket.negi
In-Reply-To: <akPQ7P1Dqb00oWa3@lore-desk>
Hi Lorenzo,
Thank you for the detailed review and suggestions!
> > + /* TX - 64-bit H+L registers: hw accumulates the total, read directly. =
> */
> > val = airoha_fe_rr(eth, REG_FE_GDM_TX_OK_PKT_CNT_H(port->id));
> > - dev->stats.tx_ok_pkts += ((u64)val << 32);
> > - val = airoha_fe_rr(eth, REG_FE_GDM_TX_OK_PKT_CNT_L(port->id));
> > - dev->stats.tx_ok_pkts += val;
> > + dev->stats.tx_ok_pkts = (u64)val << 32;
>
> I guess it is more readable to store REG_FE_GDM_TX_OK_PKT_CNT_L() read in v=
> al
> here. Something like:
>
> val = airoha_fe_rr(eth, REG_FE_GDM_TX_OK_PKT_CNT_L(port->id));
> dev->stats.tx_ok_pkts += val;
>
> This apply even to occurrence below
Agreed. I'll store CNT_L read in val first to improve readability.
> > + dev->stats.tx_ok_pkts += airoha_fe_rr(eth, REG_FE_GDM_TX_OK_PKT_CNT_L=
> (port->id));
> > =20
> > val = airoha_fe_rr(eth, REG_FE_GDM_TX_OK_BYTE_CNT_H(port->id));
> > - dev->stats.tx_ok_bytes += ((u64)val << 32);
> > - val = airoha_fe_rr(eth, REG_FE_GDM_TX_OK_BYTE_CNT_L(port->id));
> > - dev->stats.tx_ok_bytes += val;
> > + dev->stats.tx_ok_bytes = (u64)val << 32;
> > + dev->stats.tx_ok_bytes += airoha_fe_rr(eth, REG_FE_GDM_TX_OK_BYTE_CNT=
> _L(port->id));
> > =20
> > + /* TX - 32-bit registers: accumulate delta to handle wrap-around. */
> > val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_DROP_CNT(port->id));
> > - dev->stats.tx_drops += val;
> > + dev->stats.tx_drops += (u32)(val - dev->stats.hw_prev_stats.tx_drops);
> > + dev->stats.hw_prev_stats.tx_drops = val;
> > =20
> > val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_BC_CNT(port->id));
> > - dev->stats.tx_broadcast += val;
> > + dev->stats.tx_broadcast += (u32)(val - dev->stats.hw_prev_stats.tx_br=
> oadcast);
> > + dev->stats.hw_prev_stats.tx_broadcast = val;
> > =20
> > val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_MC_CNT(port->id));
> > - dev->stats.tx_multicast += val;
> > + dev->stats.tx_multicast += (u32)(val - dev->stats.hw_prev_stats.tx_mu=
> lticast);
> > + dev->stats.hw_prev_stats.tx_multicast = val;
> > =20
> > val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_RUNT_CNT(port->id));
> > - dev->stats.tx_len[i] += val;
> > + dev->stats.tx_len[i] += (u32)(val - dev->stats.hw_prev_stats.tx_len[i=
> ]);
> > + dev->stats.hw_prev_stats.tx_len[i] = val;
> > =20
> > val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_E64_CNT_H(port->id));
> > - dev->stats.tx_len[i] += ((u64)val << 32);
> > - val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_E64_CNT_L(port->id));
> > - dev->stats.tx_len[i++] += val;
> > + dev->stats.tx_len[i] += (u64)val << 32;
>
> Since now we do not reset MIB counters, this is wrong, you can't use "+="
You are absolutely right, since MIB counters are no longer cleared, using "+=" for E64 counter would cause double counting each iteration. This was missed in the patch, specifically for the case where runt count(32 bit) and E64 counter (64 bit) need to be combined in the same counter.
I'll fix this by using separate accumulator fields to "tx_runt_accum/rx_runt_accum" to track the runt deltas, then compute tx_len[i] as tx_len[i]= tx_runt_accum + E64_CNT (H+L).
> > val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_RUNT_CNT(port->id));
> > - dev->stats.rx_len[i] += val;
> > + dev->stats.rx_len[i] += (u32)(val - dev->stats.hw_prev_stats.rx_len[i=
> ]);
> > + dev->stats.hw_prev_stats.rx_len[i] = val;
> > =20
> > val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_E64_CNT_H(port->id));
> > - dev->stats.rx_len[i] += ((u64)val << 32);
> > - val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_E64_CNT_L(port->id));
> > - dev->stats.rx_len[i++] += val;
> > + dev->stats.rx_len[i] += (u64)val << 32;
>
> same here.
Acked. The same approach above will be applied to rx_len[i].
> > +
> > + struct {
> > + /* Previous HW register values for 32-bit counter delta tracking.
> > + * Storing the last seen value and accumulating (u32)(curr - prev)
> > + * in 64-bit software counter & handles wrap-around transparently
> > + * via unsigned arithmetic. These fields are never reported to
> > + * userspace.
> > + */
>
> can you please align the comment here?
Will fix the comment alignment.
>
> > + u32 tx_drops;
> > + u32 tx_broadcast;
> > + u32 tx_multicast;
> > + u32 tx_len[7];
> > + u32 rx_drops;
> > + u32 rx_broadcast;
> > + u32 rx_multicast;
> > + u32 rx_errors;
> > + u32 rx_crc_error;
> > + u32 rx_over_errors;
> > + u32 rx_fragment;
> > + u32 rx_jabber;
> > + u32 rx_len[7];
> > + } hw_prev_stats;
>
> Maybe something like "prev_val32" ?
>
> Will update the name of struct to hold prev counter from hw_pre_stats to prev_val32.
Good suggestion. However, since the struct hw_prev_stats now contains both u32 (previous register value) and u64 (runt accumulators) fields. I'll rename it to "prev_mib_state" to better reflect its dual purpose of storing previous register values for delta calculation and accumulators for combined counters.
Regards,
Aniket Negi
^ permalink raw reply
* [PATCH v2 6.6.y/6.12.y/6.18.y] af_unix: Set gc_in_progress to true in unix_gc().
From: Igor Ushakov @ 2026-07-01 6:39 UTC (permalink / raw)
To: sashal; +Cc: davem, edumazet, kuba, kuniyu, netdev, pabeni, stable, sysroot314
In-Reply-To: <stable-reply-item009-af-unix-gc-20260630181642@kernel.org>
From: Kuniyuki Iwashima <kuniyu@google.com>
[ Upstream commit d82ba05263c69fa2437fe93e4e561cc40f4c03af ]
Igor Ushakov reported that unix_gc() could run with gc_in_progress
being false if the work is scheduled while running:
Thread 1 Thread 2 Thread 3
-------- -------- --------
unix_schedule_gc() unix_schedule_gc()
`- if (!gc_in_progress) `- if (!gc_in_progress)
|- gc_in_progress = true |
`- queue_work() |
unix_gc() <----------------/ |
| |- gc_in_progress = true
... `- queue_work()
| |
`- gc_in_progress = false |
|
unix_gc() <---------------------------------------------'
|
... /* gc_in_progress == false */
|
`- gc_in_progress = false
unix_peek_fpl() relies on gc_in_progress not to confuse GC
by MSG_PEEK.
Let's set gc_in_progress to true in unix_gc().
Fixes: 8b90a9f819dc ("af_unix: Run GC on only one CPU.")
Reported-by: Igor Ushakov <sysroot314@gmail.com>
Signed-off-by: Kuniyuki Iwashima <kuniyu@google.com>
Link: https://patch.msgid.link/20260501073945.1884564-1-kuniyu@google.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
[ Add setting gc_in_progress in __unix_gc(). Keep the existing
set in unix_gc() for wait_for_unix_gc() over-limit throttling. ]
Signed-off-by: Igor Ushakov <sysroot314@gmail.com>
---
net/unix/garbage.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/net/unix/garbage.c b/net/unix/garbage.c
index 1cdb54c616..fa6983dc31 100644
--- a/net/unix/garbage.c
+++ b/net/unix/garbage.c
@@ -583,6 +583,8 @@ static void __unix_gc(struct work_struct *work)
struct sk_buff_head hitlist;
struct sk_buff *skb;
+ WRITE_ONCE(gc_in_progress, true);
+
spin_lock(&unix_gc_lock);
if (!unix_graph_maybe_cyclic) {
--
2.47.3
^ permalink raw reply related
* Re: [PATCH net 1/2] net/sched: act_skbmod: require an Ethernet header for MAC rewrites
From: Yuan Tan @ 2026-07-01 6:46 UTC (permalink / raw)
To: Jakub Kicinski
Cc: Ren Wei, netdev, jhs, jiri, davem, edumazet, pabeni, horms,
peilin.ye, cong.wang, gnault, yifanwucs, tomapufckgml, zcliangcn,
bird, bronzed_45_vested
In-Reply-To: <20260630172027.5eaba39b@kernel.org>
On Tue, Jun 30, 2026 at 5:20 PM Jakub Kicinski <kuba@kernel.org> wrote:
>
> On Tue, 30 Jun 2026 17:10:16 -0700 Jakub Kicinski wrote:
> > On Mon, 29 Jun 2026 10:46:03 +0800 Ren Wei wrote:
> > > Cc: stable@vger.kernel.org
> > > Reported-by: Yuan Tan <yuantan098@gmail.com>
> > > Reported-by: Yifan Wu <yifanwucs@gmail.com>
> > > Reported-by: Juefei Pu <tomapufckgml@gmail.com>
> > > Reported-by: Zhengchuan Liang <zcliangcn@gmail.com>
> > > Reported-by: Xin Liu <bird@lzu.edu.cn>
> > > Assisted-by: Codex:GPT-5.4
> > > Signed-off-by: Wyatt Feng <bronzed_45_vested@icloud.com>
> > > Signed-off-by: Ren Wei <n05ec@lzu.edu.cn>
> >
> > Let's do away with the 5 reported-by tags? You can use a tag for your
> > tool or your team, it doesn't have to be a person. Look at sashiko or
> > syzbot reported-by tags.
>
> On second thought, if y'all work together maybe there should be no
> reported-by tag at all? Can you explain the situation?
Hi Jakub,
Since February, our team's bug-finding tool has found hundreds of bugs
and vulnerabilities. Several people have helped improve the tool to
discover more bugs, and verify that they are real. I did not want
their credit to be lost, so I added Reported-by tags based on whether
they contributed to finding a particular bug.
I agree, though, that having five Reported-by tags may not look ideal.
Maybe using the tool name instead would be better. I will discuss this
with the others.
We also found many external volunteers to submit patches, instead of
simply dumping bug reports on the community and leaving it at that. We
were worried that too many reports might annoy maintainers. That said,
it may be easier in some cases for maintainers to fix the issues
themselves rather than review our patches, since we are not experts in
every subsystem.
^ permalink raw reply
* Re: [PATCH net] gue: validate REMCSUM private option length
From: Eric Dumazet @ 2026-07-01 6:47 UTC (permalink / raw)
To: Qihang; +Cc: netdev, kuba, davem, pabeni
In-Reply-To: <20260701022617.53171-1-q.h.hack.winter@gmail.com>
On Tue, Jun 30, 2026 at 7:27 PM Qihang <q.h.hack.winter@gmail.com> wrote:
>
> GUE private flags can indicate that remote checksum offload metadata is
> present. The private flags field itself is accounted for by
> guehdr_flags_len(), but guehdr_priv_flags_len() currently returns 0 even
> when GUE_PFLAG_REMCSUM is set.
>
> This lets a packet with only the private flags field pass
> validate_gue_flags(), after which gue_remcsum() and gue_gro_remcsum()
> read the missing REMCSUM start/offset fields from the following bytes.
>
> Account for GUE_PLEN_REMCSUM when GUE_PFLAG_REMCSUM is present so that
> malformed packets are rejected during option validation.
>
> Fixes: c1aa8347e73e ("gue: Protocol constants for remote checksum offload")
> Signed-off-by: Qihang <q.h.hack.winter@gmail.com>
> ---
Reviewed-by: Eric Dumazet <edumazet@google.com>
^ permalink raw reply
* Re: [PATCH 3/3] net: stmmac: dwmac-socfpga: Add mac-mode DT property support
From: Maxime Chevallier @ 2026-07-01 6:49 UTC (permalink / raw)
To: muhammad.nazim.amirul.nazle.asmade, dinguyen
Cc: rmk+kernel, krzk+dt, conor+dt, robh, davem, edumazet, kuba,
pabeni, andrew+netdev, devicetree, linux-arm-kernel, netdev,
linux-kernel
In-Reply-To: <20260630133108.27244-4-muhammad.nazim.amirul.nazle.asmade@altera.com>
Hi,
On 6/30/26 15:31, muhammad.nazim.amirul.nazle.asmade@altera.com wrote:
> From: Nazim Amirul <muhammad.nazim.amirul.nazle.asmade@altera.com>
>
> Russell King's commit de696c63c1dc ("net: stmmac: socfpga: convert to
> use phy_interface") replaced mac_interface with phy_interface in
> socfpga_get_plat_phymode(), noting that no upstream DTS files set the
> "mac-mode" property, making the two values identical.
>
> The Agilex5 SoCDK TSN Config2 board is an exception: its gmac1 TSN
> port uses GMII internally in the MAC while the PHY-side interface is
> RGMII, so mac-mode and phy-mode differ. Without restoring mac_interface
> support, the MAC is configured with RGMII instead of GMII, causing
> connectivity failures on this board.
>
> Add socfpga_of_get_mac_mode() to read the optional "mac-mode" DT
> property and store it in a new mac_interface field. When the property
> is absent, mac_interface falls back to phy_interface, preserving
> the existing behaviour for all other boards.
After our discussions, indeed mac-mode seems to be the way to go, however
I have some remarks on how it's handled right now.
>
> Fixes: de696c63c1dc ("net: stmmac: socfpga: convert to use phy_interface")
> Signed-off-by: Nazim Amirul <muhammad.nazim.amirul.nazle.asmade@altera.com>
> ---
> .../ethernet/stmicro/stmmac/dwmac-socfpga.c | 23 ++++++++++++++++++-
> 1 file changed, 22 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c
> index 1d7f0a57d288..6a6837c4a414 100644
> --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c
> +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c
> @@ -69,12 +69,30 @@ struct socfpga_dwmac {
> void __iomem *tse_pcs_base;
> void __iomem *sgmii_adapter_base;
> bool f2h_ptp_ref_clk;
> + phy_interface_t mac_interface;
> const struct socfpga_dwmac_ops *ops;
> };
>
> +static int socfpga_of_get_mac_mode(struct device_node *np)
> +{
> + const char *pm;
> + int err, i;
> +
> + err = of_property_read_string(np, "mac-mode", &pm);
> + if (err < 0)
> + return err;
> +
> + for (i = 0; i < PHY_INTERFACE_MODE_MAX; i++) {
> + if (!strcasecmp(pm, phy_modes(i)))
> + return i;
> + }
> +
> + return -ENODEV;
> +}
> +
> static phy_interface_t socfpga_get_plat_phymode(struct socfpga_dwmac *dwmac)
> {
> - return dwmac->plat_dat->phy_interface;
> + return dwmac->mac_interface;
> }
Taking a look at the logic in socfpga_gen{5|10}_set_phy_mode(), we have :
phy_interface_t phymode = socfpga_get_plat_phymode(dwmac);
u32 val;
socfpga_set_phy_mode_common(phymode, &val)
if (dwmac->splitter_base)
val = SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_GMII_MII;
[...]
if (phymode == PHY_INTERFACE_MODE_SGMII)
socfpga_sgmii_config(dwmac, true);
With this new patch, we now have 2 different ways of handling this
converter block (splitter presence, and mac-mode presence in DT)
Can you unify this a bit ?
One thing could be adding a helper to get the macmode such as
socfpga_get_plat_macmode()
I think we should move the splitter handling before calling
socfpga_get_plat_macmode() :
if (dwmac->splitter_base)
dwmav->mac_interface = PHY_INTERFACE_MODE_GMII
We'd get the intf_sel value based on the macmode, and also calls to
helpers such as socfpga_sgmii_config(phymode, xxx) need the actual
phymode as a parameter, not the macmode.
Thanks :)
Maxime
^ permalink raw reply
* [PATCH net] bnx2x: fix null pointer dereference in bnx2x_free_mem_bp()
From: Abdun Nihaal @ 2026-07-01 6:50 UTC (permalink / raw)
To: skalluru
Cc: Abdun Nihaal, manishc, andrew+netdev, davem, edumazet, kuba,
pabeni, netdev, linux-kernel, horms, stable
In one of the error path in bnx2x_alloc_mem_bp(), bnx2x_free_mem_bp()
may be called with bp->fp uninitialized. And so, there could be a null
pointer dereference in bnx2x_free_mem_bp(). Fix that by adding a null
check before the only dereference of bp->fp in the function.
The issue was reported by Sashiko AI review.
Fixes: c3146eb676e7 ("bnx2x: Correct memory preparation and release")
Cc: stable@vger.kernel.org
Signed-off-by: Abdun Nihaal <nihaal@cse.iitm.ac.in>
---
Compile tested only.
Thanks to Simon Horman for pointing out the Sashiko review.
drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c
index 5b2640bd31c3..25ee45cb7f3f 100644
--- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c
@@ -4712,8 +4712,9 @@ void bnx2x_free_mem_bp(struct bnx2x *bp)
{
int i;
- for (i = 0; i < bp->fp_array_size; i++)
- kfree(bp->fp[i].tpa_info);
+ if (bp->fp)
+ for (i = 0; i < bp->fp_array_size; i++)
+ kfree(bp->fp[i].tpa_info);
kfree(bp->fp);
kfree(bp->sp_objs);
kfree(bp->fp_stats);
--
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