mirror of
				git://git.openwrt.org/openwrt/openwrt.git
				synced 2025-11-03 22:44:27 -05:00 
			
		
		
		
	Manually rebased: backport-5.10/611-v5.12-net-ethernet-mediatek-support-setting-MTU.patch Removed upstreamed: bcm47xx/patches-5.10/170-bgmac-fix-initial-chip-reset-to-support-BCM5358.patch[1] All other patches automatically rebased. 1. https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?h=v5.10.175&id=cbf11ff3708ff163387da924f80a47ce7c721e9b Signed-off-by: John Audia <therealgraysky@proton.me>
		
			
				
	
	
		
			425 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
			
		
		
	
	
			425 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
From: Felix Fietkau <nbd@nbd.name>
 | 
						|
Date: Mon, 21 Feb 2022 15:39:18 +0100
 | 
						|
Subject: [PATCH] net: ethernet: mtk_eth_soc: rework hardware flow table
 | 
						|
 management
 | 
						|
 | 
						|
The hardware was designed to handle flow detection and creation of flow entries
 | 
						|
by itself, relying on the software primarily for filling in egress routing
 | 
						|
information.
 | 
						|
When there is a hash collision between multiple flows, this allows the hardware
 | 
						|
to maintain the entry for the most active flow.
 | 
						|
Additionally, the hardware only keeps offloading active for entries with at
 | 
						|
least 30 packets per second.
 | 
						|
 | 
						|
With this rework, the code no longer creates a hardware entries directly.
 | 
						|
Instead, the hardware entry is only created when the PPE reports a matching
 | 
						|
unbound flow with the minimum target rate.
 | 
						|
In order to reduce CPU overhead, looking for flows belonging to a hash entry
 | 
						|
is rate limited to once every 100ms.
 | 
						|
 | 
						|
This rework is also used as preparation for emulating bridge offload by
 | 
						|
managing L4 offload entries on demand.
 | 
						|
 | 
						|
Signed-off-by: Felix Fietkau <nbd@nbd.name>
 | 
						|
---
 | 
						|
 | 
						|
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
 | 
						|
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
 | 
						|
@@ -21,6 +21,7 @@
 | 
						|
 #include <linux/pinctrl/devinfo.h>
 | 
						|
 #include <linux/phylink.h>
 | 
						|
 #include <linux/jhash.h>
 | 
						|
+#include <linux/bitfield.h>
 | 
						|
 #include <net/dsa.h>
 | 
						|
 
 | 
						|
 #include "mtk_eth_soc.h"
 | 
						|
@@ -1286,7 +1287,7 @@ static int mtk_poll_rx(struct napi_struc
 | 
						|
 		struct net_device *netdev;
 | 
						|
 		unsigned int pktlen;
 | 
						|
 		dma_addr_t dma_addr;
 | 
						|
-		u32 hash;
 | 
						|
+		u32 hash, reason;
 | 
						|
 		int mac;
 | 
						|
 
 | 
						|
 		ring = mtk_get_rx_ring(eth);
 | 
						|
@@ -1365,6 +1366,11 @@ static int mtk_poll_rx(struct napi_struc
 | 
						|
 			skb_set_hash(skb, hash, PKT_HASH_TYPE_L4);
 | 
						|
 		}
 | 
						|
 
 | 
						|
+		reason = FIELD_GET(MTK_RXD4_PPE_CPU_REASON, trxd.rxd4);
 | 
						|
+		if (reason == MTK_PPE_CPU_REASON_HIT_UNBIND_RATE_REACHED)
 | 
						|
+			mtk_ppe_check_skb(eth->ppe, skb,
 | 
						|
+					  trxd.rxd4 & MTK_RXD4_FOE_ENTRY);
 | 
						|
+
 | 
						|
 		if (netdev->features & NETIF_F_HW_VLAN_CTAG_RX &&
 | 
						|
 		    (trxd.rxd2 & RX_DMA_VTAG))
 | 
						|
 			__vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q),
 | 
						|
@@ -3290,7 +3296,7 @@ static int mtk_probe(struct platform_dev
 | 
						|
 	}
 | 
						|
 
 | 
						|
 	if (eth->soc->offload_version) {
 | 
						|
-		eth->ppe = mtk_ppe_init(eth->dev, eth->base + MTK_ETH_PPE_BASE, 2);
 | 
						|
+		eth->ppe = mtk_ppe_init(eth, eth->base + MTK_ETH_PPE_BASE, 2);
 | 
						|
 		if (!eth->ppe) {
 | 
						|
 			err = -ENOMEM;
 | 
						|
 			goto err_free_dev;
 | 
						|
--- a/drivers/net/ethernet/mediatek/mtk_ppe.c
 | 
						|
+++ b/drivers/net/ethernet/mediatek/mtk_ppe.c
 | 
						|
@@ -6,9 +6,12 @@
 | 
						|
 #include <linux/iopoll.h>
 | 
						|
 #include <linux/etherdevice.h>
 | 
						|
 #include <linux/platform_device.h>
 | 
						|
+#include "mtk_eth_soc.h"
 | 
						|
 #include "mtk_ppe.h"
 | 
						|
 #include "mtk_ppe_regs.h"
 | 
						|
 
 | 
						|
+static DEFINE_SPINLOCK(ppe_lock);
 | 
						|
+
 | 
						|
 static void ppe_w32(struct mtk_ppe *ppe, u32 reg, u32 val)
 | 
						|
 {
 | 
						|
 	writel(val, ppe->base + reg);
 | 
						|
@@ -41,6 +44,11 @@ static u32 ppe_clear(struct mtk_ppe *ppe
 | 
						|
 	return ppe_m32(ppe, reg, val, 0);
 | 
						|
 }
 | 
						|
 
 | 
						|
+static u32 mtk_eth_timestamp(struct mtk_eth *eth)
 | 
						|
+{
 | 
						|
+	return mtk_r32(eth, 0x0010) & MTK_FOE_IB1_BIND_TIMESTAMP;
 | 
						|
+}
 | 
						|
+
 | 
						|
 static int mtk_ppe_wait_busy(struct mtk_ppe *ppe)
 | 
						|
 {
 | 
						|
 	int ret;
 | 
						|
@@ -353,26 +361,59 @@ static inline bool mtk_foe_entry_usable(
 | 
						|
 	       FIELD_GET(MTK_FOE_IB1_STATE, entry->ib1) != MTK_FOE_STATE_BIND;
 | 
						|
 }
 | 
						|
 
 | 
						|
-int mtk_foe_entry_commit(struct mtk_ppe *ppe, struct mtk_foe_entry *entry,
 | 
						|
-			 u16 timestamp)
 | 
						|
+static bool
 | 
						|
+mtk_flow_entry_match(struct mtk_flow_entry *entry, struct mtk_foe_entry *data)
 | 
						|
+{
 | 
						|
+	int type, len;
 | 
						|
+
 | 
						|
+	if ((data->ib1 ^ entry->data.ib1) & MTK_FOE_IB1_UDP)
 | 
						|
+		return false;
 | 
						|
+
 | 
						|
+	type = FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, entry->data.ib1);
 | 
						|
+	if (type > MTK_PPE_PKT_TYPE_IPV4_DSLITE)
 | 
						|
+		len = offsetof(struct mtk_foe_entry, ipv6._rsv);
 | 
						|
+	else
 | 
						|
+		len = offsetof(struct mtk_foe_entry, ipv4.ib2);
 | 
						|
+
 | 
						|
+	return !memcmp(&entry->data.data, &data->data, len - 4);
 | 
						|
+}
 | 
						|
+
 | 
						|
+static void
 | 
						|
+mtk_flow_entry_update(struct mtk_ppe *ppe, struct mtk_flow_entry *entry)
 | 
						|
 {
 | 
						|
 	struct mtk_foe_entry *hwe;
 | 
						|
-	u32 hash;
 | 
						|
+	struct mtk_foe_entry foe;
 | 
						|
 
 | 
						|
+	spin_lock_bh(&ppe_lock);
 | 
						|
+	if (entry->hash == 0xffff)
 | 
						|
+		goto out;
 | 
						|
+
 | 
						|
+	hwe = &ppe->foe_table[entry->hash];
 | 
						|
+	memcpy(&foe, hwe, sizeof(foe));
 | 
						|
+	if (!mtk_flow_entry_match(entry, &foe)) {
 | 
						|
+		entry->hash = 0xffff;
 | 
						|
+		goto out;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	entry->data.ib1 = foe.ib1;
 | 
						|
+
 | 
						|
+out:
 | 
						|
+	spin_unlock_bh(&ppe_lock);
 | 
						|
+}
 | 
						|
+
 | 
						|
+static void
 | 
						|
+__mtk_foe_entry_commit(struct mtk_ppe *ppe, struct mtk_foe_entry *entry,
 | 
						|
+		       u16 hash)
 | 
						|
+{
 | 
						|
+	struct mtk_foe_entry *hwe;
 | 
						|
+	u16 timestamp;
 | 
						|
+
 | 
						|
+	timestamp = mtk_eth_timestamp(ppe->eth);
 | 
						|
 	timestamp &= MTK_FOE_IB1_BIND_TIMESTAMP;
 | 
						|
 	entry->ib1 &= ~MTK_FOE_IB1_BIND_TIMESTAMP;
 | 
						|
 	entry->ib1 |= FIELD_PREP(MTK_FOE_IB1_BIND_TIMESTAMP, timestamp);
 | 
						|
 
 | 
						|
-	hash = mtk_ppe_hash_entry(entry);
 | 
						|
 	hwe = &ppe->foe_table[hash];
 | 
						|
-	if (!mtk_foe_entry_usable(hwe)) {
 | 
						|
-		hwe++;
 | 
						|
-		hash++;
 | 
						|
-
 | 
						|
-		if (!mtk_foe_entry_usable(hwe))
 | 
						|
-			return -ENOSPC;
 | 
						|
-	}
 | 
						|
-
 | 
						|
 	memcpy(&hwe->data, &entry->data, sizeof(hwe->data));
 | 
						|
 	wmb();
 | 
						|
 	hwe->ib1 = entry->ib1;
 | 
						|
@@ -380,13 +421,77 @@ int mtk_foe_entry_commit(struct mtk_ppe
 | 
						|
 	dma_wmb();
 | 
						|
 
 | 
						|
 	mtk_ppe_cache_clear(ppe);
 | 
						|
+}
 | 
						|
 
 | 
						|
-	return hash;
 | 
						|
+void mtk_foe_entry_clear(struct mtk_ppe *ppe, struct mtk_flow_entry *entry)
 | 
						|
+{
 | 
						|
+	spin_lock_bh(&ppe_lock);
 | 
						|
+	hlist_del_init(&entry->list);
 | 
						|
+	if (entry->hash != 0xffff) {
 | 
						|
+		ppe->foe_table[entry->hash].ib1 &= ~MTK_FOE_IB1_STATE;
 | 
						|
+		ppe->foe_table[entry->hash].ib1 |= FIELD_PREP(MTK_FOE_IB1_STATE,
 | 
						|
+							      MTK_FOE_STATE_BIND);
 | 
						|
+		dma_wmb();
 | 
						|
+	}
 | 
						|
+	entry->hash = 0xffff;
 | 
						|
+	spin_unlock_bh(&ppe_lock);
 | 
						|
+}
 | 
						|
+
 | 
						|
+int mtk_foe_entry_commit(struct mtk_ppe *ppe, struct mtk_flow_entry *entry)
 | 
						|
+{
 | 
						|
+	u32 hash = mtk_ppe_hash_entry(&entry->data);
 | 
						|
+
 | 
						|
+	entry->hash = 0xffff;
 | 
						|
+	spin_lock_bh(&ppe_lock);
 | 
						|
+	hlist_add_head(&entry->list, &ppe->foe_flow[hash / 2]);
 | 
						|
+	spin_unlock_bh(&ppe_lock);
 | 
						|
+
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+void __mtk_ppe_check_skb(struct mtk_ppe *ppe, struct sk_buff *skb, u16 hash)
 | 
						|
+{
 | 
						|
+	struct hlist_head *head = &ppe->foe_flow[hash / 2];
 | 
						|
+	struct mtk_flow_entry *entry;
 | 
						|
+	struct mtk_foe_entry *hwe = &ppe->foe_table[hash];
 | 
						|
+	bool found = false;
 | 
						|
+
 | 
						|
+	if (hlist_empty(head))
 | 
						|
+		return;
 | 
						|
+
 | 
						|
+	spin_lock_bh(&ppe_lock);
 | 
						|
+	hlist_for_each_entry(entry, head, list) {
 | 
						|
+		if (found || !mtk_flow_entry_match(entry, hwe)) {
 | 
						|
+			if (entry->hash != 0xffff)
 | 
						|
+				entry->hash = 0xffff;
 | 
						|
+			continue;
 | 
						|
+		}
 | 
						|
+
 | 
						|
+		entry->hash = hash;
 | 
						|
+		__mtk_foe_entry_commit(ppe, &entry->data, hash);
 | 
						|
+		found = true;
 | 
						|
+	}
 | 
						|
+	spin_unlock_bh(&ppe_lock);
 | 
						|
+}
 | 
						|
+
 | 
						|
+int mtk_foe_entry_idle_time(struct mtk_ppe *ppe, struct mtk_flow_entry *entry)
 | 
						|
+{
 | 
						|
+	u16 now = mtk_eth_timestamp(ppe->eth) & MTK_FOE_IB1_BIND_TIMESTAMP;
 | 
						|
+	u16 timestamp;
 | 
						|
+
 | 
						|
+	mtk_flow_entry_update(ppe, entry);
 | 
						|
+	timestamp = entry->data.ib1 & MTK_FOE_IB1_BIND_TIMESTAMP;
 | 
						|
+
 | 
						|
+	if (timestamp > now)
 | 
						|
+		return MTK_FOE_IB1_BIND_TIMESTAMP + 1 - timestamp + now;
 | 
						|
+	else
 | 
						|
+		return now - timestamp;
 | 
						|
 }
 | 
						|
 
 | 
						|
-struct mtk_ppe *mtk_ppe_init(struct device *dev, void __iomem *base,
 | 
						|
+struct mtk_ppe *mtk_ppe_init(struct mtk_eth *eth, void __iomem *base,
 | 
						|
 		 int version)
 | 
						|
 {
 | 
						|
+	struct device *dev = eth->dev;
 | 
						|
 	struct mtk_foe_entry *foe;
 | 
						|
 	struct mtk_ppe *ppe;
 | 
						|
 
 | 
						|
@@ -398,6 +503,7 @@ struct mtk_ppe *mtk_ppe_init(struct devi
 | 
						|
 	 * not coherent.
 | 
						|
 	 */
 | 
						|
 	ppe->base = base;
 | 
						|
+	ppe->eth = eth;
 | 
						|
 	ppe->dev = dev;
 | 
						|
 	ppe->version = version;
 | 
						|
 
 | 
						|
--- a/drivers/net/ethernet/mediatek/mtk_ppe.h
 | 
						|
+++ b/drivers/net/ethernet/mediatek/mtk_ppe.h
 | 
						|
@@ -235,7 +235,17 @@ enum {
 | 
						|
 	MTK_PPE_CPU_REASON_INVALID			= 0x1f,
 | 
						|
 };
 | 
						|
 
 | 
						|
+struct mtk_flow_entry {
 | 
						|
+	struct rhash_head node;
 | 
						|
+	struct hlist_node list;
 | 
						|
+	unsigned long cookie;
 | 
						|
+	struct mtk_foe_entry data;
 | 
						|
+	u16 hash;
 | 
						|
+	s8 wed_index;
 | 
						|
+};
 | 
						|
+
 | 
						|
 struct mtk_ppe {
 | 
						|
+	struct mtk_eth *eth;
 | 
						|
 	struct device *dev;
 | 
						|
 	void __iomem *base;
 | 
						|
 	int version;
 | 
						|
@@ -243,18 +253,33 @@ struct mtk_ppe {
 | 
						|
 	struct mtk_foe_entry *foe_table;
 | 
						|
 	dma_addr_t foe_phys;
 | 
						|
 
 | 
						|
+	u16 foe_check_time[MTK_PPE_ENTRIES];
 | 
						|
+	struct hlist_head foe_flow[MTK_PPE_ENTRIES / 2];
 | 
						|
+
 | 
						|
 	void *acct_table;
 | 
						|
 };
 | 
						|
 
 | 
						|
-struct mtk_ppe *mtk_ppe_init(struct device *dev, void __iomem *base, int version);
 | 
						|
+struct mtk_ppe *mtk_ppe_init(struct mtk_eth *eth, void __iomem *base, int version);
 | 
						|
 int mtk_ppe_start(struct mtk_ppe *ppe);
 | 
						|
 int mtk_ppe_stop(struct mtk_ppe *ppe);
 | 
						|
 
 | 
						|
+void __mtk_ppe_check_skb(struct mtk_ppe *ppe, struct sk_buff *skb, u16 hash);
 | 
						|
+
 | 
						|
 static inline void
 | 
						|
-mtk_foe_entry_clear(struct mtk_ppe *ppe, u16 hash)
 | 
						|
+mtk_ppe_check_skb(struct mtk_ppe *ppe, struct sk_buff *skb, u16 hash)
 | 
						|
 {
 | 
						|
-	ppe->foe_table[hash].ib1 = 0;
 | 
						|
-	dma_wmb();
 | 
						|
+	u16 now, diff;
 | 
						|
+
 | 
						|
+	if (!ppe)
 | 
						|
+		return;
 | 
						|
+
 | 
						|
+	now = (u16)jiffies;
 | 
						|
+	diff = now - ppe->foe_check_time[hash];
 | 
						|
+	if (diff < HZ / 10)
 | 
						|
+		return;
 | 
						|
+
 | 
						|
+	ppe->foe_check_time[hash] = now;
 | 
						|
+	__mtk_ppe_check_skb(ppe, skb, hash);
 | 
						|
 }
 | 
						|
 
 | 
						|
 static inline int
 | 
						|
@@ -282,8 +307,9 @@ int mtk_foe_entry_set_vlan(struct mtk_fo
 | 
						|
 int mtk_foe_entry_set_pppoe(struct mtk_foe_entry *entry, int sid);
 | 
						|
 int mtk_foe_entry_set_wdma(struct mtk_foe_entry *entry, int wdma_idx, int txq,
 | 
						|
 			   int bss, int wcid);
 | 
						|
-int mtk_foe_entry_commit(struct mtk_ppe *ppe, struct mtk_foe_entry *entry,
 | 
						|
-			 u16 timestamp);
 | 
						|
+int mtk_foe_entry_commit(struct mtk_ppe *ppe, struct mtk_flow_entry *entry);
 | 
						|
+void mtk_foe_entry_clear(struct mtk_ppe *ppe, struct mtk_flow_entry *entry);
 | 
						|
+int mtk_foe_entry_idle_time(struct mtk_ppe *ppe, struct mtk_flow_entry *entry);
 | 
						|
 int mtk_ppe_debugfs_init(struct mtk_ppe *ppe);
 | 
						|
 
 | 
						|
 #endif
 | 
						|
--- a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
 | 
						|
+++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
 | 
						|
@@ -43,13 +43,6 @@ struct mtk_flow_data {
 | 
						|
 	} pppoe;
 | 
						|
 };
 | 
						|
 
 | 
						|
-struct mtk_flow_entry {
 | 
						|
-	struct rhash_head node;
 | 
						|
-	unsigned long cookie;
 | 
						|
-	u16 hash;
 | 
						|
-	s8 wed_index;
 | 
						|
-};
 | 
						|
-
 | 
						|
 static const struct rhashtable_params mtk_flow_ht_params = {
 | 
						|
 	.head_offset = offsetof(struct mtk_flow_entry, node),
 | 
						|
 	.key_offset = offsetof(struct mtk_flow_entry, cookie),
 | 
						|
@@ -57,12 +50,6 @@ static const struct rhashtable_params mt
 | 
						|
 	.automatic_shrinking = true,
 | 
						|
 };
 | 
						|
 
 | 
						|
-static u32
 | 
						|
-mtk_eth_timestamp(struct mtk_eth *eth)
 | 
						|
-{
 | 
						|
-	return mtk_r32(eth, 0x0010) & MTK_FOE_IB1_BIND_TIMESTAMP;
 | 
						|
-}
 | 
						|
-
 | 
						|
 static int
 | 
						|
 mtk_flow_set_ipv4_addr(struct mtk_foe_entry *foe, struct mtk_flow_data *data,
 | 
						|
 		       bool egress)
 | 
						|
@@ -238,10 +225,8 @@ mtk_flow_offload_replace(struct mtk_eth
 | 
						|
 	int offload_type = 0;
 | 
						|
 	int wed_index = -1;
 | 
						|
 	u16 addr_type = 0;
 | 
						|
-	u32 timestamp;
 | 
						|
 	u8 l4proto = 0;
 | 
						|
 	int err = 0;
 | 
						|
-	int hash;
 | 
						|
 	int i;
 | 
						|
 
 | 
						|
 	if (rhashtable_lookup(ð->flow_table, &f->cookie, mtk_flow_ht_params))
 | 
						|
@@ -411,23 +396,21 @@ mtk_flow_offload_replace(struct mtk_eth
 | 
						|
 		return -ENOMEM;
 | 
						|
 
 | 
						|
 	entry->cookie = f->cookie;
 | 
						|
-	timestamp = mtk_eth_timestamp(eth);
 | 
						|
-	hash = mtk_foe_entry_commit(eth->ppe, &foe, timestamp);
 | 
						|
-	if (hash < 0) {
 | 
						|
-		err = hash;
 | 
						|
+	memcpy(&entry->data, &foe, sizeof(entry->data));
 | 
						|
+	entry->wed_index = wed_index;
 | 
						|
+
 | 
						|
+	if (mtk_foe_entry_commit(eth->ppe, entry) < 0)
 | 
						|
 		goto free;
 | 
						|
-	}
 | 
						|
 
 | 
						|
-	entry->hash = hash;
 | 
						|
-	entry->wed_index = wed_index;
 | 
						|
 	err = rhashtable_insert_fast(ð->flow_table, &entry->node,
 | 
						|
 				     mtk_flow_ht_params);
 | 
						|
 	if (err < 0)
 | 
						|
-		goto clear_flow;
 | 
						|
+		goto clear;
 | 
						|
 
 | 
						|
 	return 0;
 | 
						|
-clear_flow:
 | 
						|
-	mtk_foe_entry_clear(eth->ppe, hash);
 | 
						|
+
 | 
						|
+clear:
 | 
						|
+	mtk_foe_entry_clear(eth->ppe, entry);
 | 
						|
 free:
 | 
						|
 	kfree(entry);
 | 
						|
 	if (wed_index >= 0)
 | 
						|
@@ -445,7 +428,7 @@ mtk_flow_offload_destroy(struct mtk_eth
 | 
						|
 	if (!entry)
 | 
						|
 		return -ENOENT;
 | 
						|
 
 | 
						|
-	mtk_foe_entry_clear(eth->ppe, entry->hash);
 | 
						|
+	mtk_foe_entry_clear(eth->ppe, entry);
 | 
						|
 	rhashtable_remove_fast(ð->flow_table, &entry->node,
 | 
						|
 			       mtk_flow_ht_params);
 | 
						|
 	if (entry->wed_index >= 0)
 | 
						|
@@ -459,7 +442,6 @@ static int
 | 
						|
 mtk_flow_offload_stats(struct mtk_eth *eth, struct flow_cls_offload *f)
 | 
						|
 {
 | 
						|
 	struct mtk_flow_entry *entry;
 | 
						|
-	int timestamp;
 | 
						|
 	u32 idle;
 | 
						|
 
 | 
						|
 	entry = rhashtable_lookup(ð->flow_table, &f->cookie,
 | 
						|
@@ -467,11 +449,7 @@ mtk_flow_offload_stats(struct mtk_eth *e
 | 
						|
 	if (!entry)
 | 
						|
 		return -ENOENT;
 | 
						|
 
 | 
						|
-	timestamp = mtk_foe_entry_timestamp(eth->ppe, entry->hash);
 | 
						|
-	if (timestamp < 0)
 | 
						|
-		return -ETIMEDOUT;
 | 
						|
-
 | 
						|
-	idle = mtk_eth_timestamp(eth) - timestamp;
 | 
						|
+	idle = mtk_foe_entry_idle_time(eth->ppe, entry);
 | 
						|
 	f->stats.lastused = jiffies - idle * HZ;
 | 
						|
 
 | 
						|
 	return 0;
 |