AW: AW: AW: AW: [PATCH net] net: usb: lan78xx: restore VLAN filter table after device reset
From: Sven Schuchmann
Date: Mon Jun 22 2026 - 06:08:44 EST
Hello Nicolai,
On 19.6.2026 16:01, Nicolai Buchwitz wrote:
> Hi Sven
>
> On 19.6.2026 15:31, Sven Schuchmann wrote:
> > Hello Nicolai,
> >
> > looks good from my point of view
> > (Calling the lan78xx_write_vlan_table() from
> > lan78xx_mac_link_up() and from lan78xx_reset()).
>
> Thanks.
Just to be clear I used this patch which is looking good:
---
drivers/net/usb/lan78xx.c | 26 +++++++++++++++++++++++---
1 file changed, 23 insertions(+), 3 deletions(-)
diff --git a/drivers/net/usb/lan78xx.c b/drivers/net/usb/lan78xx.c
index a5132f2f9..a2db38650 100644
--- a/drivers/net/usb/lan78xx.c
+++ b/drivers/net/usb/lan78xx.c
@@ -1571,6 +1571,7 @@ static void lan78xx_set_multicast(struct net_device *netdev)
}
static void lan78xx_rx_urb_submit_all(struct lan78xx_net *dev);
+static int lan78xx_write_vlan_table(struct lan78xx_net *dev);
static int lan78xx_mac_reset(struct lan78xx_net *dev)
{
@@ -2528,6 +2529,10 @@ static void lan78xx_mac_link_up(struct phylink_config *config,
if (ret < 0)
goto link_up_fail;
+ ret = lan78xx_write_vlan_table(dev);
+ if (ret < 0)
+ goto link_up_fail;
+
netif_start_queue(net);
return;
@@ -3081,14 +3086,20 @@ static int lan78xx_set_features(struct net_device *netdev,
return lan78xx_write_reg(dev, RFE_CTL, pdata->rfe_ctl);
}
+static int lan78xx_write_vlan_table(struct lan78xx_net *dev)
+{
+ struct lan78xx_priv *pdata = (struct lan78xx_priv *)(dev->data[0]);
+
+ return lan78xx_dataport_write(dev, DP_SEL_RSEL_VLAN_DA_, 0,
+ DP_SEL_VHF_VLAN_LEN, pdata->vlan_table);
+}
+
static void lan78xx_deferred_vlan_write(struct work_struct *param)
{
struct lan78xx_priv *pdata =
container_of(param, struct lan78xx_priv, set_vlan);
- struct lan78xx_net *dev = pdata->dev;
- lan78xx_dataport_write(dev, DP_SEL_RSEL_VLAN_DA_, 0,
- DP_SEL_VHF_VLAN_LEN, pdata->vlan_table);
+ lan78xx_write_vlan_table(pdata->dev);
}
static int lan78xx_vlan_rx_add_vid(struct net_device *netdev,
@@ -3378,6 +3389,15 @@ static int lan78xx_reset(struct lan78xx_net *dev)
lan78xx_set_multicast(dev->net);
+ /* The chip reset above also clears the VLAN filter table held in the
+ * shared VLAN/DA hash RAM. The network stack does not re-add VLANs
+ * after a silent device reset (e.g. on reset_resume after USB
+ * autosuspend), so restore the table from our shadow copy here.
+ */
+ ret = lan78xx_write_vlan_table(dev);
+ if (ret < 0)
+ return ret;
+
/* reset PHY */
ret = lan78xx_read_reg(dev, PMT_CTL, &buf);
if (ret < 0)
--
>
> > But I investigated a little more and it seems the hash table
> > (which is right behind the vlan table in the controllers memory)
> > also gets cleared. I wrote some random data into this table and have
> > seen that it gets also cleared. I think this needs to be fixed too.
>
> Something like
>
> static int lan78xx_write_mchash_table(struct lan78xx_net *dev)
> {
> struct lan78xx_priv *pdata = (struct lan78xx_priv
> *)(dev->data[0]);
>
> return lan78xx_dataport_write(dev, DP_SEL_RSEL_VLAN_DA_,
> DP_SEL_VHF_VLAN_LEN,
> DP_SEL_VHF_HASH_LEN,
> pdata->mchash_table); // from lan78xx_deferred_multicast_write)
> }
>
> with callers in lan78xx_deferred_multicast_write() and
> lan78xx_mac_link_up(), should
> do the trick?
I used this one which is also looking good:
---
drivers/net/usb/lan78xx.c | 24 +++++++++++++++++++++---
1 file changed, 21 insertions(+), 3 deletions(-)
diff --git a/drivers/net/usb/lan78xx.c b/drivers/net/usb/lan78xx.c
index d449c1950fd3..6d7d349816a6 100644
--- a/drivers/net/usb/lan78xx.c
+++ b/drivers/net/usb/lan78xx.c
@@ -1466,6 +1466,8 @@ static inline u32 lan78xx_hash(char addr[ETH_ALEN])
return (ether_crc(ETH_ALEN, addr) >> 23) & 0x1ff;
}
+static int lan78xx_write_mchash_table(struct lan78xx_net *dev);
+
static void lan78xx_deferred_multicast_write(struct work_struct *param)
{
struct lan78xx_priv *pdata =
@@ -1476,9 +1478,7 @@ static void lan78xx_deferred_multicast_write(struct work_struct *param)
netif_dbg(dev, drv, dev->net, "deferred multicast write 0x%08x\n",
pdata->rfe_ctl);
- ret = lan78xx_dataport_write(dev, DP_SEL_RSEL_VLAN_DA_,
- DP_SEL_VHF_VLAN_LEN,
- DP_SEL_VHF_HASH_LEN, pdata->mchash_table);
+ ret = lan78xx_write_mchash_table(dev);
if (ret < 0)
goto multicast_write_done;
@@ -2533,6 +2533,10 @@ static void lan78xx_mac_link_up(struct phylink_config *config,
if (ret < 0)
goto link_up_fail;
+ ret = lan78xx_write_mchash_table(dev);
+ if (ret < 0)
+ goto link_up_fail;
+
netif_start_queue(net);
return;
@@ -3094,6 +3098,16 @@ static int lan78xx_write_vlan_table(struct lan78xx_net *dev)
DP_SEL_VHF_VLAN_LEN, pdata->vlan_table);
}
+static int lan78xx_write_mchash_table(struct lan78xx_net *dev)
+{
+ struct lan78xx_priv *pdata = (struct lan78xx_priv *)(dev->data[0]);
+
+ return lan78xx_dataport_write(dev, DP_SEL_RSEL_VLAN_DA_,
+ DP_SEL_VHF_VLAN_LEN,
+ DP_SEL_VHF_HASH_LEN,
+ pdata->mchash_table);
+}
+
static void lan78xx_deferred_vlan_write(struct work_struct *param)
{
struct lan78xx_priv *pdata =
@@ -3398,6 +3412,10 @@ static int lan78xx_reset(struct lan78xx_net *dev)
if (ret < 0)
return ret;
+ ret = lan78xx_write_mchash_table(dev);
+ if (ret < 0)
+ return ret;
+
/* reset PHY */
ret = lan78xx_read_reg(dev, PMT_CTL, &buf);
if (ret < 0)
--
>
> >
> > In the Datasheet from the LAN7801 I can read:
> > "After a reset event, the RFE will automatically initialize the
> > contents of the VHF to 0h."
> > Where VHF also refers to the hash table.
> > But I still do not understand what reset is happening when I just
> > unplug the network cable....
>
> I suspect it is triggered from the PHY:
>
> 8.10 (MAC Reset Watchdog Timer):
> "A portion of the MAC operates on clocks generated by the Ethernet PHY
> [...] PHY Reset
> (PHY_RST) results in resetting the portion of the MAC operating on the
> PHY receive and
> transmit clocks."
>
> So which PHY are you using?
I am using a DP83TC812R from TI. There is currently no driver available
so I ported this one
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/net/phy/dp83tg720.c
which is working fine (maybe I will also publish a patch for this).
The strange thing is that the MAC Reset Watchdog Timer seems
to occur "silently" so that nor the mac or the phy driver know
about this reset.
But never the less. The two patches fixed my problem and
I think they should be mainline.
Regards,
Sven