mirror of
https://github.com/coolsnowwolf/lede.git
synced 2025-04-16 04:13:31 +00:00
mediatek: add patches for MT7622 WED (wireless ethernet dispatch)
This commit is contained in:
parent
a5f299d925
commit
4ae5d37ca1
@ -0,0 +1,167 @@
|
||||
From ffe387740bbe88dd88bbe04d6375902708003d6e Mon Sep 17 00:00:00 2001
|
||||
From: Felix Fietkau <nbd@nbd.name>
|
||||
Date: Fri, 7 Jul 2017 17:25:00 +0200
|
||||
Subject: net: add packet mangeling
|
||||
|
||||
ar8216 switches have a hardware bug, which renders normal 802.1q support
|
||||
unusable. Packet mangling is required to fix up the vlan for incoming
|
||||
packets.
|
||||
|
||||
Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
---
|
||||
include/linux/netdevice.h | 11 +++++++++++
|
||||
include/linux/skbuff.h | 14 ++++----------
|
||||
net/Kconfig | 6 ++++++
|
||||
net/core/dev.c | 20 +++++++++++++++-----
|
||||
net/core/skbuff.c | 17 +++++++++++++++++
|
||||
net/ethernet/eth.c | 6 ++++++
|
||||
6 files changed, 59 insertions(+), 15 deletions(-)
|
||||
|
||||
--- a/include/linux/netdevice.h
|
||||
+++ b/include/linux/netdevice.h
|
||||
@@ -1625,6 +1625,7 @@ enum netdev_priv_flags {
|
||||
IFF_FAILOVER_SLAVE = 1<<28,
|
||||
IFF_L3MDEV_RX_HANDLER = 1<<29,
|
||||
IFF_LIVE_RENAME_OK = 1<<30,
|
||||
+ IFF_NO_IP_ALIGN = 1<<31,
|
||||
};
|
||||
|
||||
#define IFF_802_1Q_VLAN IFF_802_1Q_VLAN
|
||||
@@ -1657,6 +1658,7 @@ enum netdev_priv_flags {
|
||||
#define IFF_FAILOVER_SLAVE IFF_FAILOVER_SLAVE
|
||||
#define IFF_L3MDEV_RX_HANDLER IFF_L3MDEV_RX_HANDLER
|
||||
#define IFF_LIVE_RENAME_OK IFF_LIVE_RENAME_OK
|
||||
+#define IFF_NO_IP_ALIGN IFF_NO_IP_ALIGN
|
||||
|
||||
/* Specifies the type of the struct net_device::ml_priv pointer */
|
||||
enum netdev_ml_priv_type {
|
||||
@@ -1997,6 +1999,11 @@ struct net_device {
|
||||
const struct tlsdev_ops *tlsdev_ops;
|
||||
#endif
|
||||
|
||||
+#ifdef CONFIG_ETHERNET_PACKET_MANGLE
|
||||
+ void (*eth_mangle_rx)(struct net_device *dev, struct sk_buff *skb);
|
||||
+ struct sk_buff *(*eth_mangle_tx)(struct net_device *dev, struct sk_buff *skb);
|
||||
+#endif
|
||||
+
|
||||
const struct header_ops *header_ops;
|
||||
|
||||
unsigned int flags;
|
||||
@@ -2087,6 +2094,10 @@ struct net_device {
|
||||
struct mpls_dev __rcu *mpls_ptr;
|
||||
#endif
|
||||
|
||||
+#ifdef CONFIG_ETHERNET_PACKET_MANGLE
|
||||
+ void *phy_ptr; /* PHY device specific data */
|
||||
+#endif
|
||||
+
|
||||
/*
|
||||
* Cache lines mostly used on receive path (including eth_type_trans())
|
||||
*/
|
||||
--- a/include/linux/skbuff.h
|
||||
+++ b/include/linux/skbuff.h
|
||||
@@ -2710,6 +2710,10 @@ static inline int pskb_trim(struct sk_bu
|
||||
return (len < skb->len) ? __pskb_trim(skb, len) : 0;
|
||||
}
|
||||
|
||||
+extern struct sk_buff *__netdev_alloc_skb_ip_align(struct net_device *dev,
|
||||
+ unsigned int length, gfp_t gfp);
|
||||
+
|
||||
+
|
||||
/**
|
||||
* pskb_trim_unique - remove end from a paged unique (not cloned) buffer
|
||||
* @skb: buffer to alter
|
||||
@@ -2841,16 +2845,6 @@ static inline struct sk_buff *dev_alloc_
|
||||
}
|
||||
|
||||
|
||||
-static inline struct sk_buff *__netdev_alloc_skb_ip_align(struct net_device *dev,
|
||||
- unsigned int length, gfp_t gfp)
|
||||
-{
|
||||
- struct sk_buff *skb = __netdev_alloc_skb(dev, length + NET_IP_ALIGN, gfp);
|
||||
-
|
||||
- if (NET_IP_ALIGN && skb)
|
||||
- skb_reserve(skb, NET_IP_ALIGN);
|
||||
- return skb;
|
||||
-}
|
||||
-
|
||||
static inline struct sk_buff *netdev_alloc_skb_ip_align(struct net_device *dev,
|
||||
unsigned int length)
|
||||
{
|
||||
--- a/net/Kconfig
|
||||
+++ b/net/Kconfig
|
||||
@@ -26,6 +26,12 @@ menuconfig NET
|
||||
|
||||
if NET
|
||||
|
||||
+config ETHERNET_PACKET_MANGLE
|
||||
+ bool
|
||||
+ help
|
||||
+ This option can be selected by phy drivers that need to mangle
|
||||
+ packets going in or out of an ethernet device.
|
||||
+
|
||||
config WANT_COMPAT_NETLINK_MESSAGES
|
||||
bool
|
||||
help
|
||||
--- a/net/core/dev.c
|
||||
+++ b/net/core/dev.c
|
||||
@@ -3650,6 +3650,11 @@ static int xmit_one(struct sk_buff *skb,
|
||||
if (dev_nit_active(dev))
|
||||
dev_queue_xmit_nit(skb, dev);
|
||||
|
||||
+#ifdef CONFIG_ETHERNET_PACKET_MANGLE
|
||||
+ if (dev->eth_mangle_tx && !(skb = dev->eth_mangle_tx(dev, skb)))
|
||||
+ return NETDEV_TX_OK;
|
||||
+#endif
|
||||
+
|
||||
len = skb->len;
|
||||
PRANDOM_ADD_NOISE(skb, dev, txq, len + jiffies);
|
||||
trace_net_dev_start_xmit(skb, dev);
|
||||
--- a/net/core/skbuff.c
|
||||
+++ b/net/core/skbuff.c
|
||||
@@ -60,6 +60,7 @@
|
||||
#include <linux/prefetch.h>
|
||||
#include <linux/if_vlan.h>
|
||||
#include <linux/mpls.h>
|
||||
+#include <linux/if.h>
|
||||
|
||||
#include <net/protocol.h>
|
||||
#include <net/dst.h>
|
||||
@@ -553,6 +554,22 @@ skb_fail:
|
||||
}
|
||||
EXPORT_SYMBOL(__napi_alloc_skb);
|
||||
|
||||
+struct sk_buff *__netdev_alloc_skb_ip_align(struct net_device *dev,
|
||||
+ unsigned int length, gfp_t gfp)
|
||||
+{
|
||||
+ struct sk_buff *skb = __netdev_alloc_skb(dev, length + NET_IP_ALIGN, gfp);
|
||||
+
|
||||
+#ifdef CONFIG_ETHERNET_PACKET_MANGLE
|
||||
+ if (dev && (dev->priv_flags & IFF_NO_IP_ALIGN))
|
||||
+ return skb;
|
||||
+#endif
|
||||
+
|
||||
+ if (NET_IP_ALIGN && skb)
|
||||
+ skb_reserve(skb, NET_IP_ALIGN);
|
||||
+ return skb;
|
||||
+}
|
||||
+EXPORT_SYMBOL(__netdev_alloc_skb_ip_align);
|
||||
+
|
||||
void skb_add_rx_frag(struct sk_buff *skb, int i, struct page *page, int off,
|
||||
int size, unsigned int truesize)
|
||||
{
|
||||
--- a/net/ethernet/eth.c
|
||||
+++ b/net/ethernet/eth.c
|
||||
@@ -171,6 +171,12 @@ __be16 eth_type_trans(struct sk_buff *sk
|
||||
const struct ethhdr *eth;
|
||||
|
||||
skb->dev = dev;
|
||||
+
|
||||
+#ifdef CONFIG_ETHERNET_PACKET_MANGLE
|
||||
+ if (dev->eth_mangle_rx)
|
||||
+ dev->eth_mangle_rx(dev, skb);
|
||||
+#endif
|
||||
+
|
||||
skb_reset_mac_header(skb);
|
||||
|
||||
eth = (struct ethhdr *)skb->data;
|
@ -71,7 +71,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
*/
|
||||
--- a/include/linux/skbuff.h
|
||||
+++ b/include/linux/skbuff.h
|
||||
@@ -2761,6 +2761,10 @@ static inline int pskb_trim(struct sk_bu
|
||||
@@ -2771,6 +2771,10 @@ static inline int pskb_trim(struct sk_bu
|
||||
return (len < skb->len) ? __pskb_trim(skb, len) : 0;
|
||||
}
|
||||
|
||||
@ -82,7 +82,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
/**
|
||||
* pskb_trim_unique - remove end from a paged unique (not cloned) buffer
|
||||
* @skb: buffer to alter
|
||||
@@ -2911,16 +2915,6 @@ static inline struct sk_buff *dev_alloc_
|
||||
@@ -2921,16 +2925,6 @@ static inline struct sk_buff *dev_alloc_
|
||||
}
|
||||
|
||||
|
||||
|
@ -0,0 +1,327 @@
|
||||
From: Felix Fietkau <nbd@nbd.name>
|
||||
Date: Sat, 5 Feb 2022 17:59:07 +0100
|
||||
Subject: [PATCH] net: ethernet: mtk_eth_soc: add support for coherent
|
||||
DMA
|
||||
|
||||
It improves performance by eliminating the need for a cache flush on rx and tx
|
||||
In preparation for supporting WED (Wireless Ethernet Dispatch), also add a
|
||||
function for disabling coherent DMA at runtime.
|
||||
|
||||
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
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_mdio.h>
|
||||
#include <linux/of_net.h>
|
||||
+#include <linux/of_address.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/clk.h>
|
||||
@@ -821,7 +822,7 @@ static int mtk_init_fq_dma(struct mtk_et
|
||||
dma_addr_t dma_addr;
|
||||
int i;
|
||||
|
||||
- eth->scratch_ring = dma_alloc_coherent(eth->dev,
|
||||
+ eth->scratch_ring = dma_alloc_coherent(eth->dma_dev,
|
||||
cnt * sizeof(struct mtk_tx_dma),
|
||||
ð->phy_scratch_ring,
|
||||
GFP_ATOMIC);
|
||||
@@ -833,10 +834,10 @@ static int mtk_init_fq_dma(struct mtk_et
|
||||
if (unlikely(!eth->scratch_head))
|
||||
return -ENOMEM;
|
||||
|
||||
- dma_addr = dma_map_single(eth->dev,
|
||||
+ dma_addr = dma_map_single(eth->dma_dev,
|
||||
eth->scratch_head, cnt * MTK_QDMA_PAGE_SIZE,
|
||||
DMA_FROM_DEVICE);
|
||||
- if (unlikely(dma_mapping_error(eth->dev, dma_addr)))
|
||||
+ if (unlikely(dma_mapping_error(eth->dma_dev, dma_addr)))
|
||||
return -ENOMEM;
|
||||
|
||||
phy_ring_tail = eth->phy_scratch_ring +
|
||||
@@ -890,26 +891,26 @@ static void mtk_tx_unmap(struct mtk_eth
|
||||
{
|
||||
if (MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA)) {
|
||||
if (tx_buf->flags & MTK_TX_FLAGS_SINGLE0) {
|
||||
- dma_unmap_single(eth->dev,
|
||||
+ dma_unmap_single(eth->dma_dev,
|
||||
dma_unmap_addr(tx_buf, dma_addr0),
|
||||
dma_unmap_len(tx_buf, dma_len0),
|
||||
DMA_TO_DEVICE);
|
||||
} else if (tx_buf->flags & MTK_TX_FLAGS_PAGE0) {
|
||||
- dma_unmap_page(eth->dev,
|
||||
+ dma_unmap_page(eth->dma_dev,
|
||||
dma_unmap_addr(tx_buf, dma_addr0),
|
||||
dma_unmap_len(tx_buf, dma_len0),
|
||||
DMA_TO_DEVICE);
|
||||
}
|
||||
} else {
|
||||
if (dma_unmap_len(tx_buf, dma_len0)) {
|
||||
- dma_unmap_page(eth->dev,
|
||||
+ dma_unmap_page(eth->dma_dev,
|
||||
dma_unmap_addr(tx_buf, dma_addr0),
|
||||
dma_unmap_len(tx_buf, dma_len0),
|
||||
DMA_TO_DEVICE);
|
||||
}
|
||||
|
||||
if (dma_unmap_len(tx_buf, dma_len1)) {
|
||||
- dma_unmap_page(eth->dev,
|
||||
+ dma_unmap_page(eth->dma_dev,
|
||||
dma_unmap_addr(tx_buf, dma_addr1),
|
||||
dma_unmap_len(tx_buf, dma_len1),
|
||||
DMA_TO_DEVICE);
|
||||
@@ -987,9 +988,9 @@ static int mtk_tx_map(struct sk_buff *sk
|
||||
if (skb_vlan_tag_present(skb))
|
||||
txd4 |= TX_DMA_INS_VLAN | skb_vlan_tag_get(skb);
|
||||
|
||||
- mapped_addr = dma_map_single(eth->dev, skb->data,
|
||||
+ mapped_addr = dma_map_single(eth->dma_dev, skb->data,
|
||||
skb_headlen(skb), DMA_TO_DEVICE);
|
||||
- if (unlikely(dma_mapping_error(eth->dev, mapped_addr)))
|
||||
+ if (unlikely(dma_mapping_error(eth->dma_dev, mapped_addr)))
|
||||
return -ENOMEM;
|
||||
|
||||
WRITE_ONCE(itxd->txd1, mapped_addr);
|
||||
@@ -1028,10 +1029,10 @@ static int mtk_tx_map(struct sk_buff *sk
|
||||
|
||||
|
||||
frag_map_size = min(frag_size, MTK_TX_DMA_BUF_LEN);
|
||||
- mapped_addr = skb_frag_dma_map(eth->dev, frag, offset,
|
||||
+ mapped_addr = skb_frag_dma_map(eth->dma_dev, frag, offset,
|
||||
frag_map_size,
|
||||
DMA_TO_DEVICE);
|
||||
- if (unlikely(dma_mapping_error(eth->dev, mapped_addr)))
|
||||
+ if (unlikely(dma_mapping_error(eth->dma_dev, mapped_addr)))
|
||||
goto err_dma;
|
||||
|
||||
if (i == nr_frags - 1 &&
|
||||
@@ -1309,18 +1310,18 @@ static int mtk_poll_rx(struct napi_struc
|
||||
netdev->stats.rx_dropped++;
|
||||
goto release_desc;
|
||||
}
|
||||
- dma_addr = dma_map_single(eth->dev,
|
||||
+ dma_addr = dma_map_single(eth->dma_dev,
|
||||
new_data + NET_SKB_PAD +
|
||||
eth->ip_align,
|
||||
ring->buf_size,
|
||||
DMA_FROM_DEVICE);
|
||||
- if (unlikely(dma_mapping_error(eth->dev, dma_addr))) {
|
||||
+ if (unlikely(dma_mapping_error(eth->dma_dev, dma_addr))) {
|
||||
skb_free_frag(new_data);
|
||||
netdev->stats.rx_dropped++;
|
||||
goto release_desc;
|
||||
}
|
||||
|
||||
- dma_unmap_single(eth->dev, trxd.rxd1,
|
||||
+ dma_unmap_single(eth->dma_dev, trxd.rxd1,
|
||||
ring->buf_size, DMA_FROM_DEVICE);
|
||||
|
||||
/* receive data */
|
||||
@@ -1593,7 +1594,7 @@ static int mtk_tx_alloc(struct mtk_eth *
|
||||
if (!ring->buf)
|
||||
goto no_tx_mem;
|
||||
|
||||
- ring->dma = dma_alloc_coherent(eth->dev, MTK_DMA_SIZE * sz,
|
||||
+ ring->dma = dma_alloc_coherent(eth->dma_dev, MTK_DMA_SIZE * sz,
|
||||
&ring->phys, GFP_ATOMIC);
|
||||
if (!ring->dma)
|
||||
goto no_tx_mem;
|
||||
@@ -1611,7 +1612,7 @@ static int mtk_tx_alloc(struct mtk_eth *
|
||||
* descriptors in ring->dma_pdma.
|
||||
*/
|
||||
if (!MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA)) {
|
||||
- ring->dma_pdma = dma_alloc_coherent(eth->dev, MTK_DMA_SIZE * sz,
|
||||
+ ring->dma_pdma = dma_alloc_coherent(eth->dma_dev, MTK_DMA_SIZE * sz,
|
||||
&ring->phys_pdma,
|
||||
GFP_ATOMIC);
|
||||
if (!ring->dma_pdma)
|
||||
@@ -1670,7 +1671,7 @@ static void mtk_tx_clean(struct mtk_eth
|
||||
}
|
||||
|
||||
if (ring->dma) {
|
||||
- dma_free_coherent(eth->dev,
|
||||
+ dma_free_coherent(eth->dma_dev,
|
||||
MTK_DMA_SIZE * sizeof(*ring->dma),
|
||||
ring->dma,
|
||||
ring->phys);
|
||||
@@ -1678,7 +1679,7 @@ static void mtk_tx_clean(struct mtk_eth
|
||||
}
|
||||
|
||||
if (ring->dma_pdma) {
|
||||
- dma_free_coherent(eth->dev,
|
||||
+ dma_free_coherent(eth->dma_dev,
|
||||
MTK_DMA_SIZE * sizeof(*ring->dma_pdma),
|
||||
ring->dma_pdma,
|
||||
ring->phys_pdma);
|
||||
@@ -1723,18 +1724,18 @@ static int mtk_rx_alloc(struct mtk_eth *
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
- ring->dma = dma_alloc_coherent(eth->dev,
|
||||
+ ring->dma = dma_alloc_coherent(eth->dma_dev,
|
||||
rx_dma_size * sizeof(*ring->dma),
|
||||
&ring->phys, GFP_ATOMIC);
|
||||
if (!ring->dma)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < rx_dma_size; i++) {
|
||||
- dma_addr_t dma_addr = dma_map_single(eth->dev,
|
||||
+ dma_addr_t dma_addr = dma_map_single(eth->dma_dev,
|
||||
ring->data[i] + NET_SKB_PAD + eth->ip_align,
|
||||
ring->buf_size,
|
||||
DMA_FROM_DEVICE);
|
||||
- if (unlikely(dma_mapping_error(eth->dev, dma_addr)))
|
||||
+ if (unlikely(dma_mapping_error(eth->dma_dev, dma_addr)))
|
||||
return -ENOMEM;
|
||||
ring->dma[i].rxd1 = (unsigned int)dma_addr;
|
||||
|
||||
@@ -1770,7 +1771,7 @@ static void mtk_rx_clean(struct mtk_eth
|
||||
continue;
|
||||
if (!ring->dma[i].rxd1)
|
||||
continue;
|
||||
- dma_unmap_single(eth->dev,
|
||||
+ dma_unmap_single(eth->dma_dev,
|
||||
ring->dma[i].rxd1,
|
||||
ring->buf_size,
|
||||
DMA_FROM_DEVICE);
|
||||
@@ -1781,7 +1782,7 @@ static void mtk_rx_clean(struct mtk_eth
|
||||
}
|
||||
|
||||
if (ring->dma) {
|
||||
- dma_free_coherent(eth->dev,
|
||||
+ dma_free_coherent(eth->dma_dev,
|
||||
ring->dma_size * sizeof(*ring->dma),
|
||||
ring->dma,
|
||||
ring->phys);
|
||||
@@ -2134,7 +2135,7 @@ static void mtk_dma_free(struct mtk_eth
|
||||
if (eth->netdev[i])
|
||||
netdev_reset_queue(eth->netdev[i]);
|
||||
if (eth->scratch_ring) {
|
||||
- dma_free_coherent(eth->dev,
|
||||
+ dma_free_coherent(eth->dma_dev,
|
||||
MTK_DMA_SIZE * sizeof(struct mtk_tx_dma),
|
||||
eth->scratch_ring,
|
||||
eth->phy_scratch_ring);
|
||||
@@ -2482,6 +2483,8 @@ static void mtk_dim_tx(struct work_struc
|
||||
|
||||
static int mtk_hw_init(struct mtk_eth *eth)
|
||||
{
|
||||
+ u32 dma_mask = ETHSYS_DMA_AG_MAP_PDMA | ETHSYS_DMA_AG_MAP_QDMA |
|
||||
+ ETHSYS_DMA_AG_MAP_PPE;
|
||||
int i, val, ret;
|
||||
|
||||
if (test_and_set_bit(MTK_HW_INIT, ð->state))
|
||||
@@ -2494,6 +2497,10 @@ static int mtk_hw_init(struct mtk_eth *e
|
||||
if (ret)
|
||||
goto err_disable_pm;
|
||||
|
||||
+ if (eth->ethsys)
|
||||
+ regmap_update_bits(eth->ethsys, ETHSYS_DMA_AG_MAP, dma_mask,
|
||||
+ of_dma_is_coherent(eth->dma_dev->of_node) * dma_mask);
|
||||
+
|
||||
if (MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628)) {
|
||||
ret = device_reset(eth->dev);
|
||||
if (ret) {
|
||||
@@ -3043,6 +3050,35 @@ free_netdev:
|
||||
return err;
|
||||
}
|
||||
|
||||
+void mtk_eth_set_dma_device(struct mtk_eth *eth, struct device *dma_dev)
|
||||
+{
|
||||
+ struct net_device *dev, *tmp;
|
||||
+ LIST_HEAD(dev_list);
|
||||
+ int i;
|
||||
+
|
||||
+ rtnl_lock();
|
||||
+
|
||||
+ for (i = 0; i < MTK_MAC_COUNT; i++) {
|
||||
+ dev = eth->netdev[i];
|
||||
+
|
||||
+ if (!dev || !(dev->flags & IFF_UP))
|
||||
+ continue;
|
||||
+
|
||||
+ list_add_tail(&dev->close_list, &dev_list);
|
||||
+ }
|
||||
+
|
||||
+ dev_close_many(&dev_list, false);
|
||||
+
|
||||
+ eth->dma_dev = dma_dev;
|
||||
+
|
||||
+ list_for_each_entry_safe(dev, tmp, &dev_list, close_list) {
|
||||
+ list_del_init(&dev->close_list);
|
||||
+ dev_open(dev, NULL);
|
||||
+ }
|
||||
+
|
||||
+ rtnl_unlock();
|
||||
+}
|
||||
+
|
||||
static int mtk_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *mac_np;
|
||||
@@ -3056,6 +3092,7 @@ static int mtk_probe(struct platform_dev
|
||||
eth->soc = of_device_get_match_data(&pdev->dev);
|
||||
|
||||
eth->dev = &pdev->dev;
|
||||
+ eth->dma_dev = &pdev->dev;
|
||||
eth->base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(eth->base))
|
||||
return PTR_ERR(eth->base);
|
||||
@@ -3104,6 +3141,16 @@ static int mtk_probe(struct platform_dev
|
||||
}
|
||||
}
|
||||
|
||||
+ if (of_dma_is_coherent(pdev->dev.of_node)) {
|
||||
+ struct regmap *cci;
|
||||
+
|
||||
+ cci = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
|
||||
+ "mediatek,cci-control");
|
||||
+ /* enable CPU/bus coherency */
|
||||
+ if (!IS_ERR(cci))
|
||||
+ regmap_write(cci, 0, 3);
|
||||
+ }
|
||||
+
|
||||
if (MTK_HAS_CAPS(eth->soc->caps, MTK_SGMII)) {
|
||||
eth->sgmii = devm_kzalloc(eth->dev, sizeof(*eth->sgmii),
|
||||
GFP_KERNEL);
|
||||
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
|
||||
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
|
||||
@@ -456,6 +456,12 @@
|
||||
#define RSTCTRL_FE BIT(6)
|
||||
#define RSTCTRL_PPE BIT(31)
|
||||
|
||||
+/* ethernet dma channel agent map */
|
||||
+#define ETHSYS_DMA_AG_MAP 0x408
|
||||
+#define ETHSYS_DMA_AG_MAP_PDMA BIT(0)
|
||||
+#define ETHSYS_DMA_AG_MAP_QDMA BIT(1)
|
||||
+#define ETHSYS_DMA_AG_MAP_PPE BIT(2)
|
||||
+
|
||||
/* SGMII subsystem config registers */
|
||||
/* Register to auto-negotiation restart */
|
||||
#define SGMSYS_PCS_CONTROL_1 0x0
|
||||
@@ -873,6 +879,7 @@ struct mtk_sgmii {
|
||||
/* struct mtk_eth - This is the main datasructure for holding the state
|
||||
* of the driver
|
||||
* @dev: The device pointer
|
||||
+ * @dev: The device pointer used for dma mapping/alloc
|
||||
* @base: The mapped register i/o base
|
||||
* @page_lock: Make sure that register operations are atomic
|
||||
* @tx_irq__lock: Make sure that IRQ register operations are atomic
|
||||
@@ -916,6 +923,7 @@ struct mtk_sgmii {
|
||||
|
||||
struct mtk_eth {
|
||||
struct device *dev;
|
||||
+ struct device *dma_dev;
|
||||
void __iomem *base;
|
||||
spinlock_t page_lock;
|
||||
spinlock_t tx_irq_lock;
|
||||
@@ -1014,6 +1022,7 @@ int mtk_gmac_rgmii_path_setup(struct mtk
|
||||
int mtk_eth_offload_init(struct mtk_eth *eth);
|
||||
int mtk_eth_setup_tc(struct net_device *dev, enum tc_setup_type type,
|
||||
void *type_data);
|
||||
+void mtk_eth_set_dma_device(struct mtk_eth *eth, struct device *dma_dev);
|
||||
|
||||
|
||||
#endif /* MTK_ETH_H */
|
@ -0,0 +1,30 @@
|
||||
From: Felix Fietkau <nbd@nbd.name>
|
||||
Date: Mon, 7 Feb 2022 10:27:22 +0100
|
||||
Subject: [PATCH] arm64: dts: mediatek: mt7622: add support for coherent
|
||||
DMA
|
||||
|
||||
It improves performance by eliminating the need for a cache flush on rx and tx
|
||||
|
||||
Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
---
|
||||
|
||||
--- a/arch/arm64/boot/dts/mediatek/mt7622.dtsi
|
||||
+++ b/arch/arm64/boot/dts/mediatek/mt7622.dtsi
|
||||
@@ -357,7 +357,7 @@
|
||||
};
|
||||
|
||||
cci_control2: slave-if@5000 {
|
||||
- compatible = "arm,cci-400-ctrl-if";
|
||||
+ compatible = "arm,cci-400-ctrl-if", "syscon";
|
||||
interface-type = "ace";
|
||||
reg = <0x5000 0x1000>;
|
||||
};
|
||||
@@ -936,6 +936,8 @@
|
||||
power-domains = <&scpsys MT7622_POWER_DOMAIN_ETHSYS>;
|
||||
mediatek,ethsys = <ðsys>;
|
||||
mediatek,sgmiisys = <&sgmiisys>;
|
||||
+ mediatek,cci-control = <&cci_control2>;
|
||||
+ dma-coherent;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
status = "disabled";
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,269 @@
|
||||
From: Felix Fietkau <nbd@nbd.name>
|
||||
Date: Sat, 5 Feb 2022 18:29:22 +0100
|
||||
Subject: [PATCH] net: ethernet: mtk_eth_soc: implement flow offloading
|
||||
to WED devices
|
||||
|
||||
This allows hardware flow offloading from Ethernet to WLAN on MT7622 SoC
|
||||
|
||||
Co-developed-by: Lorenzo Bianconi <lorenzo@kernel.org>
|
||||
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
|
||||
Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
---
|
||||
|
||||
--- a/drivers/net/ethernet/mediatek/mtk_ppe.c
|
||||
+++ b/drivers/net/ethernet/mediatek/mtk_ppe.c
|
||||
@@ -329,6 +329,24 @@ int mtk_foe_entry_set_pppoe(struct mtk_f
|
||||
return 0;
|
||||
}
|
||||
|
||||
+int mtk_foe_entry_set_wdma(struct mtk_foe_entry *entry, int wdma_idx, int txq,
|
||||
+ int bss, int wcid)
|
||||
+{
|
||||
+ struct mtk_foe_mac_info *l2 = mtk_foe_entry_l2(entry);
|
||||
+ u32 *ib2 = mtk_foe_entry_ib2(entry);
|
||||
+
|
||||
+ *ib2 &= ~MTK_FOE_IB2_PORT_MG;
|
||||
+ *ib2 |= MTK_FOE_IB2_WDMA_WINFO;
|
||||
+ if (wdma_idx)
|
||||
+ *ib2 |= MTK_FOE_IB2_WDMA_DEVIDX;
|
||||
+
|
||||
+ l2->vlan2 = FIELD_PREP(MTK_FOE_VLAN2_WINFO_BSS, bss) |
|
||||
+ FIELD_PREP(MTK_FOE_VLAN2_WINFO_WCID, wcid) |
|
||||
+ FIELD_PREP(MTK_FOE_VLAN2_WINFO_RING, txq);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
static inline bool mtk_foe_entry_usable(struct mtk_foe_entry *entry)
|
||||
{
|
||||
return !(entry->ib1 & MTK_FOE_IB1_STATIC) &&
|
||||
--- a/drivers/net/ethernet/mediatek/mtk_ppe.h
|
||||
+++ b/drivers/net/ethernet/mediatek/mtk_ppe.h
|
||||
@@ -48,9 +48,9 @@ enum {
|
||||
#define MTK_FOE_IB2_DEST_PORT GENMASK(7, 5)
|
||||
#define MTK_FOE_IB2_MULTICAST BIT(8)
|
||||
|
||||
-#define MTK_FOE_IB2_WHNAT_QID2 GENMASK(13, 12)
|
||||
-#define MTK_FOE_IB2_WHNAT_DEVIDX BIT(16)
|
||||
-#define MTK_FOE_IB2_WHNAT_NAT BIT(17)
|
||||
+#define MTK_FOE_IB2_WDMA_QID2 GENMASK(13, 12)
|
||||
+#define MTK_FOE_IB2_WDMA_DEVIDX BIT(16)
|
||||
+#define MTK_FOE_IB2_WDMA_WINFO BIT(17)
|
||||
|
||||
#define MTK_FOE_IB2_PORT_MG GENMASK(17, 12)
|
||||
|
||||
@@ -58,9 +58,9 @@ enum {
|
||||
|
||||
#define MTK_FOE_IB2_DSCP GENMASK(31, 24)
|
||||
|
||||
-#define MTK_FOE_VLAN2_WHNAT_BSS GEMMASK(5, 0)
|
||||
-#define MTK_FOE_VLAN2_WHNAT_WCID GENMASK(13, 6)
|
||||
-#define MTK_FOE_VLAN2_WHNAT_RING GENMASK(15, 14)
|
||||
+#define MTK_FOE_VLAN2_WINFO_BSS GENMASK(5, 0)
|
||||
+#define MTK_FOE_VLAN2_WINFO_WCID GENMASK(13, 6)
|
||||
+#define MTK_FOE_VLAN2_WINFO_RING GENMASK(15, 14)
|
||||
|
||||
enum {
|
||||
MTK_FOE_STATE_INVALID,
|
||||
@@ -281,6 +281,8 @@ int mtk_foe_entry_set_ipv6_tuple(struct
|
||||
int mtk_foe_entry_set_dsa(struct mtk_foe_entry *entry, int port);
|
||||
int mtk_foe_entry_set_vlan(struct mtk_foe_entry *entry, int vid);
|
||||
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_ppe_debugfs_init(struct mtk_ppe *ppe);
|
||||
--- a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
|
||||
+++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <net/pkt_cls.h>
|
||||
#include <net/dsa.h>
|
||||
#include "mtk_eth_soc.h"
|
||||
+#include "mtk_wed.h"
|
||||
|
||||
struct mtk_flow_data {
|
||||
struct ethhdr eth;
|
||||
@@ -40,6 +41,7 @@ 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 = {
|
||||
@@ -81,6 +83,35 @@ mtk_flow_offload_mangle_eth(const struct
|
||||
memcpy(dest, src, act->mangle.mask ? 2 : 4);
|
||||
}
|
||||
|
||||
+static int
|
||||
+mtk_flow_get_wdma_info(struct net_device *dev, const u8 *addr, struct mtk_wdma_info *info)
|
||||
+{
|
||||
+ struct net_device_path_ctx ctx = {
|
||||
+ .dev = dev,
|
||||
+ .daddr = addr,
|
||||
+ };
|
||||
+ struct net_device_path path = {};
|
||||
+
|
||||
+ if (!IS_ENABLED(CONFIG_NET_MEDIATEK_SOC_WED))
|
||||
+ return -1;
|
||||
+
|
||||
+ if (!dev->netdev_ops->ndo_fill_forward_path)
|
||||
+ return -1;
|
||||
+
|
||||
+ if (dev->netdev_ops->ndo_fill_forward_path(&ctx, &path))
|
||||
+ return -1;
|
||||
+
|
||||
+ if (path.type != DEV_PATH_MTK_WDMA)
|
||||
+ return -1;
|
||||
+
|
||||
+ info->wdma_idx = path.mtk_wdma.wdma_idx;
|
||||
+ info->queue = path.mtk_wdma.queue;
|
||||
+ info->bss = path.mtk_wdma.bss;
|
||||
+ info->wcid = path.mtk_wdma.wcid;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
|
||||
static int
|
||||
mtk_flow_mangle_ports(const struct flow_action_entry *act,
|
||||
@@ -150,10 +181,20 @@ mtk_flow_get_dsa_port(struct net_device
|
||||
|
||||
static int
|
||||
mtk_flow_set_output_device(struct mtk_eth *eth, struct mtk_foe_entry *foe,
|
||||
- struct net_device *dev)
|
||||
+ struct net_device *dev, const u8 *dest_mac,
|
||||
+ int *wed_index)
|
||||
{
|
||||
+ struct mtk_wdma_info info = {};
|
||||
int pse_port, dsa_port;
|
||||
|
||||
+ if (mtk_flow_get_wdma_info(dev, dest_mac, &info) == 0) {
|
||||
+ mtk_foe_entry_set_wdma(foe, info.wdma_idx, info.queue, info.bss,
|
||||
+ info.wcid);
|
||||
+ pse_port = 3;
|
||||
+ *wed_index = info.wdma_idx;
|
||||
+ goto out;
|
||||
+ }
|
||||
+
|
||||
dsa_port = mtk_flow_get_dsa_port(&dev);
|
||||
if (dsa_port >= 0)
|
||||
mtk_foe_entry_set_dsa(foe, dsa_port);
|
||||
@@ -165,6 +206,7 @@ mtk_flow_set_output_device(struct mtk_et
|
||||
else
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
+out:
|
||||
mtk_foe_entry_set_pse_port(foe, pse_port);
|
||||
|
||||
return 0;
|
||||
@@ -180,6 +222,7 @@ mtk_flow_offload_replace(struct mtk_eth
|
||||
struct net_device *odev = NULL;
|
||||
struct mtk_flow_entry *entry;
|
||||
int offload_type = 0;
|
||||
+ int wed_index = -1;
|
||||
u16 addr_type = 0;
|
||||
u32 timestamp;
|
||||
u8 l4proto = 0;
|
||||
@@ -327,10 +370,14 @@ mtk_flow_offload_replace(struct mtk_eth
|
||||
if (data.pppoe.num == 1)
|
||||
mtk_foe_entry_set_pppoe(&foe, data.pppoe.sid);
|
||||
|
||||
- err = mtk_flow_set_output_device(eth, &foe, odev);
|
||||
+ err = mtk_flow_set_output_device(eth, &foe, odev, data.eth.h_dest,
|
||||
+ &wed_index);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
+ if (wed_index >= 0 && (err = mtk_wed_flow_add(wed_index)) < 0)
|
||||
+ return err;
|
||||
+
|
||||
entry = kzalloc(sizeof(*entry), GFP_KERNEL);
|
||||
if (!entry)
|
||||
return -ENOMEM;
|
||||
@@ -344,6 +391,7 @@ mtk_flow_offload_replace(struct mtk_eth
|
||||
}
|
||||
|
||||
entry->hash = hash;
|
||||
+ entry->wed_index = wed_index;
|
||||
err = rhashtable_insert_fast(ð->flow_table, &entry->node,
|
||||
mtk_flow_ht_params);
|
||||
if (err < 0)
|
||||
@@ -354,6 +402,8 @@ clear_flow:
|
||||
mtk_foe_entry_clear(ð->ppe, hash);
|
||||
free:
|
||||
kfree(entry);
|
||||
+ if (wed_index >= 0)
|
||||
+ mtk_wed_flow_remove(wed_index);
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -370,6 +420,8 @@ mtk_flow_offload_destroy(struct mtk_eth
|
||||
mtk_foe_entry_clear(ð->ppe, entry->hash);
|
||||
rhashtable_remove_fast(ð->flow_table, &entry->node,
|
||||
mtk_flow_ht_params);
|
||||
+ if (entry->wed_index >= 0)
|
||||
+ mtk_wed_flow_remove(entry->wed_index);
|
||||
kfree(entry);
|
||||
|
||||
return 0;
|
||||
--- a/drivers/net/ethernet/mediatek/mtk_wed.h
|
||||
+++ b/drivers/net/ethernet/mediatek/mtk_wed.h
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <linux/soc/mediatek/mtk_wed.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/regmap.h>
|
||||
+#include <linux/netdevice.h>
|
||||
|
||||
struct mtk_eth;
|
||||
|
||||
@@ -27,6 +28,12 @@ struct mtk_wed_hw {
|
||||
int index;
|
||||
};
|
||||
|
||||
+struct mtk_wdma_info {
|
||||
+ u8 wdma_idx;
|
||||
+ u8 queue;
|
||||
+ u16 wcid;
|
||||
+ u8 bss;
|
||||
+};
|
||||
|
||||
#ifdef CONFIG_NET_MEDIATEK_SOC_WED
|
||||
static inline void
|
||||
--- a/include/linux/netdevice.h
|
||||
+++ b/include/linux/netdevice.h
|
||||
@@ -833,6 +833,7 @@ enum net_device_path_type {
|
||||
DEV_PATH_BRIDGE,
|
||||
DEV_PATH_PPPOE,
|
||||
DEV_PATH_DSA,
|
||||
+ DEV_PATH_MTK_WDMA,
|
||||
};
|
||||
|
||||
struct net_device_path {
|
||||
@@ -858,6 +859,12 @@ struct net_device_path {
|
||||
int port;
|
||||
u16 proto;
|
||||
} dsa;
|
||||
+ struct {
|
||||
+ u8 wdma_idx;
|
||||
+ u8 queue;
|
||||
+ u16 wcid;
|
||||
+ u8 bss;
|
||||
+ } mtk_wdma;
|
||||
};
|
||||
};
|
||||
|
||||
--- a/net/core/dev.c
|
||||
+++ b/net/core/dev.c
|
||||
@@ -883,6 +883,10 @@ int dev_fill_forward_path(const struct n
|
||||
if (WARN_ON_ONCE(last_dev == ctx.dev))
|
||||
return -1;
|
||||
}
|
||||
+
|
||||
+ if (!ctx.dev)
|
||||
+ return ret;
|
||||
+
|
||||
path = dev_fwd_path(stack);
|
||||
if (!path)
|
||||
return -1;
|
@ -0,0 +1,62 @@
|
||||
From: Felix Fietkau <nbd@nbd.name>
|
||||
Date: Sat, 5 Feb 2022 18:36:36 +0100
|
||||
Subject: [PATCH] arm64: dts: mediatek: mt7622: introduce nodes for
|
||||
Wireless Ethernet Dispatch
|
||||
|
||||
Introduce wed0 and wed1 nodes in order to enable offloading forwarding
|
||||
between ethernet and wireless devices on the mt7622 chipset.
|
||||
|
||||
Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
---
|
||||
|
||||
--- a/arch/arm64/boot/dts/mediatek/mt7622.dtsi
|
||||
+++ b/arch/arm64/boot/dts/mediatek/mt7622.dtsi
|
||||
@@ -892,6 +892,11 @@
|
||||
};
|
||||
};
|
||||
|
||||
+ hifsys: syscon@1af00000 {
|
||||
+ compatible = "mediatek,mt7622-hifsys", "syscon";
|
||||
+ reg = <0 0x1af00000 0 0x70>;
|
||||
+ };
|
||||
+
|
||||
ethsys: syscon@1b000000 {
|
||||
compatible = "mediatek,mt7622-ethsys",
|
||||
"syscon";
|
||||
@@ -910,6 +915,26 @@
|
||||
#dma-cells = <1>;
|
||||
};
|
||||
|
||||
+ pcie_mirror: pcie-mirror@10000400 {
|
||||
+ compatible = "mediatek,mt7622-pcie-mirror",
|
||||
+ "syscon";
|
||||
+ reg = <0 0x10000400 0 0x10>;
|
||||
+ };
|
||||
+
|
||||
+ wed0: wed@1020a000 {
|
||||
+ compatible = "mediatek,mt7622-wed",
|
||||
+ "syscon";
|
||||
+ reg = <0 0x1020a000 0 0x1000>;
|
||||
+ interrupts = <GIC_SPI 214 IRQ_TYPE_LEVEL_LOW>;
|
||||
+ };
|
||||
+
|
||||
+ wed1: wed@1020b000 {
|
||||
+ compatible = "mediatek,mt7622-wed",
|
||||
+ "syscon";
|
||||
+ reg = <0 0x1020b000 0 0x1000>;
|
||||
+ interrupts = <GIC_SPI 215 IRQ_TYPE_LEVEL_LOW>;
|
||||
+ };
|
||||
+
|
||||
eth: ethernet@1b100000 {
|
||||
compatible = "mediatek,mt7622-eth",
|
||||
"mediatek,mt2701-eth",
|
||||
@@ -937,6 +962,9 @@
|
||||
mediatek,ethsys = <ðsys>;
|
||||
mediatek,sgmiisys = <&sgmiisys>;
|
||||
mediatek,cci-control = <&cci_control2>;
|
||||
+ mediatek,wed = <&wed0>, <&wed1>;
|
||||
+ mediatek,pcie-mirror = <&pcie_mirror>;
|
||||
+ mediatek,hifsys = <&hifsys>;
|
||||
dma-coherent;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
@ -0,0 +1,79 @@
|
||||
From: David Bentham <db260179@gmail.com>
|
||||
Date: Mon, 21 Feb 2022 15:36:16 +0100
|
||||
Subject: [PATCH] net: ethernet: mtk_eth_soc: add ipv6 flow offload
|
||||
support
|
||||
|
||||
Add the missing IPv6 flow offloading support for routing only.
|
||||
Hardware flow offloading is done by the packet processing engine (PPE)
|
||||
of the Ethernet MAC and as it doesn't support mangling of IPv6 packets,
|
||||
IPv6 NAT cannot be supported.
|
||||
|
||||
Signed-off-by: David Bentham <db260179@gmail.com>
|
||||
Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
---
|
||||
|
||||
--- a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
|
||||
+++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <linux/rhashtable.h>
|
||||
#include <linux/if_ether.h>
|
||||
#include <linux/ip.h>
|
||||
+#include <linux/ipv6.h>
|
||||
#include <net/flow_offload.h>
|
||||
#include <net/pkt_cls.h>
|
||||
#include <net/dsa.h>
|
||||
@@ -21,6 +22,11 @@ struct mtk_flow_data {
|
||||
__be32 src_addr;
|
||||
__be32 dst_addr;
|
||||
} v4;
|
||||
+
|
||||
+ struct {
|
||||
+ struct in6_addr src_addr;
|
||||
+ struct in6_addr dst_addr;
|
||||
+ } v6;
|
||||
};
|
||||
|
||||
__be16 src_port;
|
||||
@@ -66,6 +72,14 @@ mtk_flow_set_ipv4_addr(struct mtk_foe_en
|
||||
data->v4.dst_addr, data->dst_port);
|
||||
}
|
||||
|
||||
+static int
|
||||
+mtk_flow_set_ipv6_addr(struct mtk_foe_entry *foe, struct mtk_flow_data *data)
|
||||
+{
|
||||
+ return mtk_foe_entry_set_ipv6_tuple(foe,
|
||||
+ data->v6.src_addr.s6_addr32, data->src_port,
|
||||
+ data->v6.dst_addr.s6_addr32, data->dst_port);
|
||||
+}
|
||||
+
|
||||
static void
|
||||
mtk_flow_offload_mangle_eth(const struct flow_action_entry *act, void *eth)
|
||||
{
|
||||
@@ -297,6 +311,9 @@ mtk_flow_offload_replace(struct mtk_eth
|
||||
case FLOW_DISSECTOR_KEY_IPV4_ADDRS:
|
||||
offload_type = MTK_PPE_PKT_TYPE_IPV4_HNAPT;
|
||||
break;
|
||||
+ case FLOW_DISSECTOR_KEY_IPV6_ADDRS:
|
||||
+ offload_type = MTK_PPE_PKT_TYPE_IPV6_ROUTE_5T;
|
||||
+ break;
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
@@ -332,6 +349,17 @@ mtk_flow_offload_replace(struct mtk_eth
|
||||
mtk_flow_set_ipv4_addr(&foe, &data, false);
|
||||
}
|
||||
|
||||
+ if (addr_type == FLOW_DISSECTOR_KEY_IPV6_ADDRS) {
|
||||
+ struct flow_match_ipv6_addrs addrs;
|
||||
+
|
||||
+ flow_rule_match_ipv6_addrs(rule, &addrs);
|
||||
+
|
||||
+ data.v6.src_addr = addrs.key->src;
|
||||
+ data.v6.dst_addr = addrs.key->dst;
|
||||
+
|
||||
+ mtk_flow_set_ipv6_addr(&foe, &data);
|
||||
+ }
|
||||
+
|
||||
flow_action_for_each(i, act, &rule->action) {
|
||||
if (act->id != FLOW_ACTION_MANGLE)
|
||||
continue;
|
@ -0,0 +1,29 @@
|
||||
From: Felix Fietkau <nbd@nbd.name>
|
||||
Date: Mon, 21 Feb 2022 15:37:21 +0100
|
||||
Subject: [PATCH] net: ethernet: mtk_eth_soc: support TC_SETUP_BLOCK for
|
||||
PPE offload
|
||||
|
||||
This allows offload entries to be created from user space
|
||||
|
||||
Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
---
|
||||
|
||||
--- a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
|
||||
+++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
|
||||
@@ -564,10 +564,13 @@ mtk_eth_setup_tc_block(struct net_device
|
||||
int mtk_eth_setup_tc(struct net_device *dev, enum tc_setup_type type,
|
||||
void *type_data)
|
||||
{
|
||||
- if (type == TC_SETUP_FT)
|
||||
+ switch (type) {
|
||||
+ case TC_SETUP_BLOCK:
|
||||
+ case TC_SETUP_FT:
|
||||
return mtk_eth_setup_tc_block(dev, type_data);
|
||||
-
|
||||
- return -EOPNOTSUPP;
|
||||
+ default:
|
||||
+ return -EOPNOTSUPP;
|
||||
+ }
|
||||
}
|
||||
|
||||
int mtk_eth_offload_init(struct mtk_eth *eth)
|
@ -0,0 +1,159 @@
|
||||
From: Felix Fietkau <nbd@nbd.name>
|
||||
Date: Mon, 21 Feb 2022 15:38:20 +0100
|
||||
Subject: [PATCH] net: ethernet: mtk_eth_soc: allocate struct mtk_ppe
|
||||
separately
|
||||
|
||||
Preparation for adding more data to it, which will increase its size.
|
||||
|
||||
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
|
||||
@@ -2305,7 +2305,7 @@ static int mtk_open(struct net_device *d
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
- if (eth->soc->offload_version && mtk_ppe_start(ð->ppe) == 0)
|
||||
+ if (eth->soc->offload_version && mtk_ppe_start(eth->ppe) == 0)
|
||||
gdm_config = MTK_GDMA_TO_PPE;
|
||||
|
||||
mtk_gdm_config(eth, gdm_config);
|
||||
@@ -2379,7 +2379,7 @@ static int mtk_stop(struct net_device *d
|
||||
mtk_dma_free(eth);
|
||||
|
||||
if (eth->soc->offload_version)
|
||||
- mtk_ppe_stop(ð->ppe);
|
||||
+ mtk_ppe_stop(eth->ppe);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -3265,10 +3265,11 @@ static int mtk_probe(struct platform_dev
|
||||
}
|
||||
|
||||
if (eth->soc->offload_version) {
|
||||
- err = mtk_ppe_init(ð->ppe, eth->dev,
|
||||
- eth->base + MTK_ETH_PPE_BASE, 2);
|
||||
- if (err)
|
||||
+ eth->ppe = mtk_ppe_init(eth->dev, eth->base + MTK_ETH_PPE_BASE, 2);
|
||||
+ if (!eth->ppe) {
|
||||
+ err = -ENOMEM;
|
||||
goto err_free_dev;
|
||||
+ }
|
||||
|
||||
err = mtk_eth_offload_init(eth);
|
||||
if (err)
|
||||
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
|
||||
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
|
||||
@@ -976,7 +976,7 @@ struct mtk_eth {
|
||||
u32 rx_dma_l4_valid;
|
||||
int ip_align;
|
||||
|
||||
- struct mtk_ppe ppe;
|
||||
+ struct mtk_ppe *ppe;
|
||||
struct rhashtable flow_table;
|
||||
};
|
||||
|
||||
--- a/drivers/net/ethernet/mediatek/mtk_ppe.c
|
||||
+++ b/drivers/net/ethernet/mediatek/mtk_ppe.c
|
||||
@@ -384,10 +384,15 @@ int mtk_foe_entry_commit(struct mtk_ppe
|
||||
return hash;
|
||||
}
|
||||
|
||||
-int mtk_ppe_init(struct mtk_ppe *ppe, struct device *dev, void __iomem *base,
|
||||
+struct mtk_ppe *mtk_ppe_init(struct device *dev, void __iomem *base,
|
||||
int version)
|
||||
{
|
||||
struct mtk_foe_entry *foe;
|
||||
+ struct mtk_ppe *ppe;
|
||||
+
|
||||
+ ppe = devm_kzalloc(dev, sizeof(*ppe), GFP_KERNEL);
|
||||
+ if (!ppe)
|
||||
+ return NULL;
|
||||
|
||||
/* need to allocate a separate device, since it PPE DMA access is
|
||||
* not coherent.
|
||||
@@ -399,13 +404,13 @@ int mtk_ppe_init(struct mtk_ppe *ppe, st
|
||||
foe = dmam_alloc_coherent(ppe->dev, MTK_PPE_ENTRIES * sizeof(*foe),
|
||||
&ppe->foe_phys, GFP_KERNEL);
|
||||
if (!foe)
|
||||
- return -ENOMEM;
|
||||
+ return NULL;
|
||||
|
||||
ppe->foe_table = foe;
|
||||
|
||||
mtk_ppe_debugfs_init(ppe);
|
||||
|
||||
- return 0;
|
||||
+ return ppe;
|
||||
}
|
||||
|
||||
static void mtk_ppe_init_foe_table(struct mtk_ppe *ppe)
|
||||
--- a/drivers/net/ethernet/mediatek/mtk_ppe.h
|
||||
+++ b/drivers/net/ethernet/mediatek/mtk_ppe.h
|
||||
@@ -246,8 +246,7 @@ struct mtk_ppe {
|
||||
void *acct_table;
|
||||
};
|
||||
|
||||
-int mtk_ppe_init(struct mtk_ppe *ppe, struct device *dev, void __iomem *base,
|
||||
- int version);
|
||||
+struct mtk_ppe *mtk_ppe_init(struct device *dev, void __iomem *base, int version);
|
||||
int mtk_ppe_start(struct mtk_ppe *ppe);
|
||||
int mtk_ppe_stop(struct mtk_ppe *ppe);
|
||||
|
||||
--- a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
|
||||
+++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
|
||||
@@ -412,7 +412,7 @@ mtk_flow_offload_replace(struct mtk_eth
|
||||
|
||||
entry->cookie = f->cookie;
|
||||
timestamp = mtk_eth_timestamp(eth);
|
||||
- hash = mtk_foe_entry_commit(ð->ppe, &foe, timestamp);
|
||||
+ hash = mtk_foe_entry_commit(eth->ppe, &foe, timestamp);
|
||||
if (hash < 0) {
|
||||
err = hash;
|
||||
goto free;
|
||||
@@ -427,7 +427,7 @@ mtk_flow_offload_replace(struct mtk_eth
|
||||
|
||||
return 0;
|
||||
clear_flow:
|
||||
- mtk_foe_entry_clear(ð->ppe, hash);
|
||||
+ mtk_foe_entry_clear(eth->ppe, hash);
|
||||
free:
|
||||
kfree(entry);
|
||||
if (wed_index >= 0)
|
||||
@@ -445,7 +445,7 @@ mtk_flow_offload_destroy(struct mtk_eth
|
||||
if (!entry)
|
||||
return -ENOENT;
|
||||
|
||||
- mtk_foe_entry_clear(ð->ppe, entry->hash);
|
||||
+ mtk_foe_entry_clear(eth->ppe, entry->hash);
|
||||
rhashtable_remove_fast(ð->flow_table, &entry->node,
|
||||
mtk_flow_ht_params);
|
||||
if (entry->wed_index >= 0)
|
||||
@@ -467,7 +467,7 @@ mtk_flow_offload_stats(struct mtk_eth *e
|
||||
if (!entry)
|
||||
return -ENOENT;
|
||||
|
||||
- timestamp = mtk_foe_entry_timestamp(ð->ppe, entry->hash);
|
||||
+ timestamp = mtk_foe_entry_timestamp(eth->ppe, entry->hash);
|
||||
if (timestamp < 0)
|
||||
return -ETIMEDOUT;
|
||||
|
||||
@@ -523,7 +523,7 @@ mtk_eth_setup_tc_block(struct net_device
|
||||
struct flow_block_cb *block_cb;
|
||||
flow_setup_cb_t *cb;
|
||||
|
||||
- if (!eth->ppe.foe_table)
|
||||
+ if (!eth->ppe || !eth->ppe->foe_table)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (f->binder_type != FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS)
|
||||
@@ -575,7 +575,7 @@ int mtk_eth_setup_tc(struct net_device *
|
||||
|
||||
int mtk_eth_offload_init(struct mtk_eth *eth)
|
||||
{
|
||||
- if (!eth->ppe.foe_table)
|
||||
+ if (!eth->ppe || !eth->ppe->foe_table)
|
||||
return 0;
|
||||
|
||||
return rhashtable_init(ð->flow_table, &mtk_flow_ht_params);
|
@ -0,0 +1,424 @@
|
||||
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"
|
||||
@@ -1274,7 +1275,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);
|
||||
@@ -1350,6 +1351,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),
|
||||
@@ -3265,7 +3271,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;
|
@ -0,0 +1,44 @@
|
||||
From: Felix Fietkau <nbd@nbd.name>
|
||||
Date: Mon, 21 Feb 2022 15:55:19 +0100
|
||||
Subject: [PATCH] net: ethernet: mtk_eth_soc: remove bridge flow offload
|
||||
type entry support
|
||||
|
||||
According to MediaTek, this feature is not supported in current hardware
|
||||
|
||||
Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
---
|
||||
|
||||
--- a/drivers/net/ethernet/mediatek/mtk_ppe.c
|
||||
+++ b/drivers/net/ethernet/mediatek/mtk_ppe.c
|
||||
@@ -84,13 +84,6 @@ static u32 mtk_ppe_hash_entry(struct mtk
|
||||
u32 hash;
|
||||
|
||||
switch (FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, e->ib1)) {
|
||||
- case MTK_PPE_PKT_TYPE_BRIDGE:
|
||||
- hv1 = e->bridge.src_mac_lo;
|
||||
- hv1 ^= ((e->bridge.src_mac_hi & 0xffff) << 16);
|
||||
- hv2 = e->bridge.src_mac_hi >> 16;
|
||||
- hv2 ^= e->bridge.dest_mac_lo;
|
||||
- hv3 = e->bridge.dest_mac_hi;
|
||||
- break;
|
||||
case MTK_PPE_PKT_TYPE_IPV4_ROUTE:
|
||||
case MTK_PPE_PKT_TYPE_IPV4_HNAPT:
|
||||
hv1 = e->ipv4.orig.ports;
|
||||
@@ -572,7 +565,6 @@ int mtk_ppe_start(struct mtk_ppe *ppe)
|
||||
MTK_PPE_FLOW_CFG_IP4_NAT |
|
||||
MTK_PPE_FLOW_CFG_IP4_NAPT |
|
||||
MTK_PPE_FLOW_CFG_IP4_DSLITE |
|
||||
- MTK_PPE_FLOW_CFG_L2_BRIDGE |
|
||||
MTK_PPE_FLOW_CFG_IP4_NAT_FRAG;
|
||||
ppe_w32(ppe, MTK_PPE_FLOW_CFG, val);
|
||||
|
||||
--- a/drivers/net/ethernet/mediatek/mtk_ppe_debugfs.c
|
||||
+++ b/drivers/net/ethernet/mediatek/mtk_ppe_debugfs.c
|
||||
@@ -32,7 +32,6 @@ static const char *mtk_foe_pkt_type_str(
|
||||
static const char * const type_str[] = {
|
||||
[MTK_PPE_PKT_TYPE_IPV4_HNAPT] = "IPv4 5T",
|
||||
[MTK_PPE_PKT_TYPE_IPV4_ROUTE] = "IPv4 3T",
|
||||
- [MTK_PPE_PKT_TYPE_BRIDGE] = "L2",
|
||||
[MTK_PPE_PKT_TYPE_IPV4_DSLITE] = "DS-LITE",
|
||||
[MTK_PPE_PKT_TYPE_IPV6_ROUTE_3T] = "IPv6 3T",
|
||||
[MTK_PPE_PKT_TYPE_IPV6_ROUTE_5T] = "IPv6 5T",
|
@ -0,0 +1,553 @@
|
||||
From: Felix Fietkau <nbd@nbd.name>
|
||||
Date: Wed, 23 Feb 2022 10:56:34 +0100
|
||||
Subject: [PATCH] net: ethernet: mtk_eth_soc: support creating mac
|
||||
address based offload entries
|
||||
|
||||
This will be used to implement a limited form of bridge offloading.
|
||||
Since the hardware does not support flow table entries with just source
|
||||
and destination MAC address, the driver has to emulate it.
|
||||
|
||||
The hardware automatically creates entries entries for incoming flows, even
|
||||
when they are bridged instead of routed, and reports when packets for these
|
||||
flows have reached the minimum PPS rate for offloading.
|
||||
|
||||
After this happens, we look up the L2 flow offload entry based on the MAC
|
||||
header and fill in the output routing information in the flow table.
|
||||
The dynamically created per-flow entries are automatically removed when
|
||||
either the hardware flowtable entry expires, is replaced, or if the offload
|
||||
rule they belong to is removed
|
||||
|
||||
Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
---
|
||||
|
||||
--- a/drivers/net/ethernet/mediatek/mtk_ppe.c
|
||||
+++ b/drivers/net/ethernet/mediatek/mtk_ppe.c
|
||||
@@ -6,12 +6,22 @@
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/platform_device.h>
|
||||
+#include <linux/if_ether.h>
|
||||
+#include <linux/if_vlan.h>
|
||||
+#include <net/dsa.h>
|
||||
#include "mtk_eth_soc.h"
|
||||
#include "mtk_ppe.h"
|
||||
#include "mtk_ppe_regs.h"
|
||||
|
||||
static DEFINE_SPINLOCK(ppe_lock);
|
||||
|
||||
+static const struct rhashtable_params mtk_flow_l2_ht_params = {
|
||||
+ .head_offset = offsetof(struct mtk_flow_entry, l2_node),
|
||||
+ .key_offset = offsetof(struct mtk_flow_entry, data.bridge),
|
||||
+ .key_len = offsetof(struct mtk_foe_bridge, key_end),
|
||||
+ .automatic_shrinking = true,
|
||||
+};
|
||||
+
|
||||
static void ppe_w32(struct mtk_ppe *ppe, u32 reg, u32 val)
|
||||
{
|
||||
writel(val, ppe->base + reg);
|
||||
@@ -123,6 +133,9 @@ mtk_foe_entry_l2(struct mtk_foe_entry *e
|
||||
{
|
||||
int type = FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, entry->ib1);
|
||||
|
||||
+ if (type == MTK_PPE_PKT_TYPE_BRIDGE)
|
||||
+ return &entry->bridge.l2;
|
||||
+
|
||||
if (type >= MTK_PPE_PKT_TYPE_IPV4_DSLITE)
|
||||
return &entry->ipv6.l2;
|
||||
|
||||
@@ -134,6 +147,9 @@ mtk_foe_entry_ib2(struct mtk_foe_entry *
|
||||
{
|
||||
int type = FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, entry->ib1);
|
||||
|
||||
+ if (type == MTK_PPE_PKT_TYPE_BRIDGE)
|
||||
+ return &entry->bridge.ib2;
|
||||
+
|
||||
if (type >= MTK_PPE_PKT_TYPE_IPV4_DSLITE)
|
||||
return &entry->ipv6.ib2;
|
||||
|
||||
@@ -168,7 +184,12 @@ int mtk_foe_entry_prepare(struct mtk_foe
|
||||
if (type == MTK_PPE_PKT_TYPE_IPV6_ROUTE_3T)
|
||||
entry->ipv6.ports = ports_pad;
|
||||
|
||||
- if (type >= MTK_PPE_PKT_TYPE_IPV4_DSLITE) {
|
||||
+ if (type == MTK_PPE_PKT_TYPE_BRIDGE) {
|
||||
+ ether_addr_copy(entry->bridge.src_mac, src_mac);
|
||||
+ ether_addr_copy(entry->bridge.dest_mac, dest_mac);
|
||||
+ entry->bridge.ib2 = val;
|
||||
+ l2 = &entry->bridge.l2;
|
||||
+ } else if (type >= MTK_PPE_PKT_TYPE_IPV4_DSLITE) {
|
||||
entry->ipv6.ib2 = val;
|
||||
l2 = &entry->ipv6.l2;
|
||||
} else {
|
||||
@@ -372,12 +393,96 @@ mtk_flow_entry_match(struct mtk_flow_ent
|
||||
}
|
||||
|
||||
static void
|
||||
+__mtk_foe_entry_clear(struct mtk_ppe *ppe, struct mtk_flow_entry *entry)
|
||||
+{
|
||||
+ struct hlist_head *head;
|
||||
+ struct hlist_node *tmp;
|
||||
+
|
||||
+ if (entry->type == MTK_FLOW_TYPE_L2) {
|
||||
+ rhashtable_remove_fast(&ppe->l2_flows, &entry->l2_node,
|
||||
+ mtk_flow_l2_ht_params);
|
||||
+
|
||||
+ head = &entry->l2_flows;
|
||||
+ hlist_for_each_entry_safe(entry, tmp, head, l2_data.list)
|
||||
+ __mtk_foe_entry_clear(ppe, entry);
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ 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;
|
||||
+
|
||||
+ if (entry->type != MTK_FLOW_TYPE_L2_SUBFLOW)
|
||||
+ return;
|
||||
+
|
||||
+ hlist_del_init(&entry->l2_data.list);
|
||||
+ kfree(entry);
|
||||
+}
|
||||
+
|
||||
+static int __mtk_foe_entry_idle_time(struct mtk_ppe *ppe, u32 ib1)
|
||||
+{
|
||||
+ u16 timestamp;
|
||||
+ u16 now;
|
||||
+
|
||||
+ now = mtk_eth_timestamp(ppe->eth) & MTK_FOE_IB1_BIND_TIMESTAMP;
|
||||
+ timestamp = ib1 & MTK_FOE_IB1_BIND_TIMESTAMP;
|
||||
+
|
||||
+ if (timestamp > now)
|
||||
+ return MTK_FOE_IB1_BIND_TIMESTAMP + 1 - timestamp + now;
|
||||
+ else
|
||||
+ return now - timestamp;
|
||||
+}
|
||||
+
|
||||
+static void
|
||||
+mtk_flow_entry_update_l2(struct mtk_ppe *ppe, struct mtk_flow_entry *entry)
|
||||
+{
|
||||
+ struct mtk_flow_entry *cur;
|
||||
+ struct mtk_foe_entry *hwe;
|
||||
+ struct hlist_node *tmp;
|
||||
+ int idle;
|
||||
+
|
||||
+ idle = __mtk_foe_entry_idle_time(ppe, entry->data.ib1);
|
||||
+ hlist_for_each_entry_safe(cur, tmp, &entry->l2_flows, l2_data.list) {
|
||||
+ int cur_idle;
|
||||
+ u32 ib1;
|
||||
+
|
||||
+ hwe = &ppe->foe_table[cur->hash];
|
||||
+ ib1 = READ_ONCE(hwe->ib1);
|
||||
+
|
||||
+ if (FIELD_GET(MTK_FOE_IB1_STATE, ib1) != MTK_FOE_STATE_BIND) {
|
||||
+ cur->hash = 0xffff;
|
||||
+ __mtk_foe_entry_clear(ppe, cur);
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ cur_idle = __mtk_foe_entry_idle_time(ppe, ib1);
|
||||
+ if (cur_idle >= idle)
|
||||
+ continue;
|
||||
+
|
||||
+ idle = cur_idle;
|
||||
+ entry->data.ib1 &= ~MTK_FOE_IB1_BIND_TIMESTAMP;
|
||||
+ entry->data.ib1 |= hwe->ib1 & MTK_FOE_IB1_BIND_TIMESTAMP;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+static void
|
||||
mtk_flow_entry_update(struct mtk_ppe *ppe, struct mtk_flow_entry *entry)
|
||||
{
|
||||
struct mtk_foe_entry *hwe;
|
||||
struct mtk_foe_entry foe;
|
||||
|
||||
spin_lock_bh(&ppe_lock);
|
||||
+
|
||||
+ if (entry->type == MTK_FLOW_TYPE_L2) {
|
||||
+ mtk_flow_entry_update_l2(ppe, entry);
|
||||
+ goto out;
|
||||
+ }
|
||||
+
|
||||
if (entry->hash == 0xffff)
|
||||
goto out;
|
||||
|
||||
@@ -419,21 +524,28 @@ __mtk_foe_entry_commit(struct mtk_ppe *p
|
||||
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;
|
||||
+ __mtk_foe_entry_clear(ppe, entry);
|
||||
spin_unlock_bh(&ppe_lock);
|
||||
}
|
||||
|
||||
+static int
|
||||
+mtk_foe_entry_commit_l2(struct mtk_ppe *ppe, struct mtk_flow_entry *entry)
|
||||
+{
|
||||
+ entry->type = MTK_FLOW_TYPE_L2;
|
||||
+
|
||||
+ return rhashtable_insert_fast(&ppe->l2_flows, &entry->l2_node,
|
||||
+ mtk_flow_l2_ht_params);
|
||||
+}
|
||||
+
|
||||
int mtk_foe_entry_commit(struct mtk_ppe *ppe, struct mtk_flow_entry *entry)
|
||||
{
|
||||
- u32 hash = mtk_ppe_hash_entry(&entry->data);
|
||||
+ int type = FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, entry->data.ib1);
|
||||
+ u32 hash;
|
||||
+
|
||||
+ if (type == MTK_PPE_PKT_TYPE_BRIDGE)
|
||||
+ return mtk_foe_entry_commit_l2(ppe, entry);
|
||||
|
||||
+ 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]);
|
||||
@@ -442,18 +554,72 @@ int mtk_foe_entry_commit(struct mtk_ppe
|
||||
return 0;
|
||||
}
|
||||
|
||||
+static void
|
||||
+mtk_foe_entry_commit_subflow(struct mtk_ppe *ppe, struct mtk_flow_entry *entry,
|
||||
+ u16 hash)
|
||||
+{
|
||||
+ struct mtk_flow_entry *flow_info;
|
||||
+ struct mtk_foe_entry foe, *hwe;
|
||||
+ struct mtk_foe_mac_info *l2;
|
||||
+ u32 ib1_mask = MTK_FOE_IB1_PACKET_TYPE | MTK_FOE_IB1_UDP;
|
||||
+ int type;
|
||||
+
|
||||
+ flow_info = kzalloc(offsetof(struct mtk_flow_entry, l2_data.end),
|
||||
+ GFP_ATOMIC);
|
||||
+ if (!flow_info)
|
||||
+ return;
|
||||
+
|
||||
+ flow_info->l2_data.base_flow = entry;
|
||||
+ flow_info->type = MTK_FLOW_TYPE_L2_SUBFLOW;
|
||||
+ flow_info->hash = hash;
|
||||
+ hlist_add_head(&flow_info->list, &ppe->foe_flow[hash / 2]);
|
||||
+ hlist_add_head(&flow_info->l2_data.list, &entry->l2_flows);
|
||||
+
|
||||
+ hwe = &ppe->foe_table[hash];
|
||||
+ memcpy(&foe, hwe, sizeof(foe));
|
||||
+ foe.ib1 &= ib1_mask;
|
||||
+ foe.ib1 |= entry->data.ib1 & ~ib1_mask;
|
||||
+
|
||||
+ l2 = mtk_foe_entry_l2(&foe);
|
||||
+ memcpy(l2, &entry->data.bridge.l2, sizeof(*l2));
|
||||
+
|
||||
+ type = FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, foe.ib1);
|
||||
+ if (type == MTK_PPE_PKT_TYPE_IPV4_HNAPT)
|
||||
+ memcpy(&foe.ipv4.new, &foe.ipv4.orig, sizeof(foe.ipv4.new));
|
||||
+ else if (type >= MTK_PPE_PKT_TYPE_IPV6_ROUTE_3T && l2->etype == ETH_P_IP)
|
||||
+ l2->etype = ETH_P_IPV6;
|
||||
+
|
||||
+ *mtk_foe_entry_ib2(&foe) = entry->data.bridge.ib2;
|
||||
+
|
||||
+ __mtk_foe_entry_commit(ppe, &foe, hash);
|
||||
+}
|
||||
+
|
||||
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];
|
||||
+ struct mtk_flow_entry *entry;
|
||||
+ struct mtk_foe_bridge key = {};
|
||||
+ struct ethhdr *eh;
|
||||
bool found = false;
|
||||
-
|
||||
- if (hlist_empty(head))
|
||||
- return;
|
||||
+ u8 *tag;
|
||||
|
||||
spin_lock_bh(&ppe_lock);
|
||||
+
|
||||
+ if (FIELD_GET(MTK_FOE_IB1_STATE, hwe->ib1) == MTK_FOE_STATE_BIND)
|
||||
+ goto out;
|
||||
+
|
||||
hlist_for_each_entry(entry, head, list) {
|
||||
+ if (entry->type == MTK_FLOW_TYPE_L2_SUBFLOW) {
|
||||
+ if (unlikely(FIELD_GET(MTK_FOE_IB1_STATE, hwe->ib1) ==
|
||||
+ MTK_FOE_STATE_BIND))
|
||||
+ continue;
|
||||
+
|
||||
+ entry->hash = 0xffff;
|
||||
+ __mtk_foe_entry_clear(ppe, entry);
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
if (found || !mtk_flow_entry_match(entry, hwe)) {
|
||||
if (entry->hash != 0xffff)
|
||||
entry->hash = 0xffff;
|
||||
@@ -464,21 +630,50 @@ void __mtk_ppe_check_skb(struct mtk_ppe
|
||||
__mtk_foe_entry_commit(ppe, &entry->data, hash);
|
||||
found = true;
|
||||
}
|
||||
+
|
||||
+ if (found)
|
||||
+ goto out;
|
||||
+
|
||||
+ eh = eth_hdr(skb);
|
||||
+ ether_addr_copy(key.dest_mac, eh->h_dest);
|
||||
+ ether_addr_copy(key.src_mac, eh->h_source);
|
||||
+ tag = skb->data - 2;
|
||||
+ key.vlan = 0;
|
||||
+ switch (skb->protocol) {
|
||||
+#if IS_ENABLED(CONFIG_NET_DSA)
|
||||
+ case htons(ETH_P_XDSA):
|
||||
+ if (!netdev_uses_dsa(skb->dev) ||
|
||||
+ skb->dev->dsa_ptr->tag_ops->proto != DSA_TAG_PROTO_MTK)
|
||||
+ goto out;
|
||||
+
|
||||
+ tag += 4;
|
||||
+ if (get_unaligned_be16(tag) != ETH_P_8021Q)
|
||||
+ break;
|
||||
+
|
||||
+ fallthrough;
|
||||
+#endif
|
||||
+ case htons(ETH_P_8021Q):
|
||||
+ key.vlan = get_unaligned_be16(tag + 2) & VLAN_VID_MASK;
|
||||
+ break;
|
||||
+ default:
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ entry = rhashtable_lookup_fast(&ppe->l2_flows, &key, mtk_flow_l2_ht_params);
|
||||
+ if (!entry)
|
||||
+ goto out;
|
||||
+
|
||||
+ mtk_foe_entry_commit_subflow(ppe, entry, hash);
|
||||
+
|
||||
+out:
|
||||
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;
|
||||
+ return __mtk_foe_entry_idle_time(ppe, entry->data.ib1);
|
||||
}
|
||||
|
||||
struct mtk_ppe *mtk_ppe_init(struct mtk_eth *eth, void __iomem *base,
|
||||
@@ -492,6 +687,8 @@ struct mtk_ppe *mtk_ppe_init(struct mtk_
|
||||
if (!ppe)
|
||||
return NULL;
|
||||
|
||||
+ rhashtable_init(&ppe->l2_flows, &mtk_flow_l2_ht_params);
|
||||
+
|
||||
/* need to allocate a separate device, since it PPE DMA access is
|
||||
* not coherent.
|
||||
*/
|
||||
--- a/drivers/net/ethernet/mediatek/mtk_ppe.h
|
||||
+++ b/drivers/net/ethernet/mediatek/mtk_ppe.h
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/bitfield.h>
|
||||
+#include <linux/rhashtable.h>
|
||||
|
||||
#define MTK_ETH_PPE_BASE 0xc00
|
||||
|
||||
@@ -84,19 +85,16 @@ struct mtk_foe_mac_info {
|
||||
u16 src_mac_lo;
|
||||
};
|
||||
|
||||
+/* software-only entry type */
|
||||
struct mtk_foe_bridge {
|
||||
- u32 dest_mac_hi;
|
||||
-
|
||||
- u16 src_mac_lo;
|
||||
- u16 dest_mac_lo;
|
||||
+ u8 dest_mac[ETH_ALEN];
|
||||
+ u8 src_mac[ETH_ALEN];
|
||||
+ u16 vlan;
|
||||
|
||||
- u32 src_mac_hi;
|
||||
+ struct {} key_end;
|
||||
|
||||
u32 ib2;
|
||||
|
||||
- u32 _rsv[5];
|
||||
-
|
||||
- u32 udf_tsid;
|
||||
struct mtk_foe_mac_info l2;
|
||||
};
|
||||
|
||||
@@ -235,13 +233,33 @@ enum {
|
||||
MTK_PPE_CPU_REASON_INVALID = 0x1f,
|
||||
};
|
||||
|
||||
+enum {
|
||||
+ MTK_FLOW_TYPE_L4,
|
||||
+ MTK_FLOW_TYPE_L2,
|
||||
+ MTK_FLOW_TYPE_L2_SUBFLOW,
|
||||
+};
|
||||
+
|
||||
struct mtk_flow_entry {
|
||||
+ union {
|
||||
+ struct hlist_node list;
|
||||
+ struct {
|
||||
+ struct rhash_head l2_node;
|
||||
+ struct hlist_head l2_flows;
|
||||
+ };
|
||||
+ };
|
||||
+ u8 type;
|
||||
+ s8 wed_index;
|
||||
+ u16 hash;
|
||||
+ union {
|
||||
+ struct mtk_foe_entry data;
|
||||
+ struct {
|
||||
+ struct mtk_flow_entry *base_flow;
|
||||
+ struct hlist_node list;
|
||||
+ struct {} end;
|
||||
+ } l2_data;
|
||||
+ };
|
||||
struct rhash_head node;
|
||||
- struct hlist_node list;
|
||||
unsigned long cookie;
|
||||
- struct mtk_foe_entry data;
|
||||
- u16 hash;
|
||||
- s8 wed_index;
|
||||
};
|
||||
|
||||
struct mtk_ppe {
|
||||
@@ -256,6 +274,8 @@ struct mtk_ppe {
|
||||
u16 foe_check_time[MTK_PPE_ENTRIES];
|
||||
struct hlist_head foe_flow[MTK_PPE_ENTRIES / 2];
|
||||
|
||||
+ struct rhashtable l2_flows;
|
||||
+
|
||||
void *acct_table;
|
||||
};
|
||||
|
||||
--- a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
|
||||
+++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
|
||||
@@ -32,6 +32,8 @@ struct mtk_flow_data {
|
||||
__be16 src_port;
|
||||
__be16 dst_port;
|
||||
|
||||
+ u16 vlan_in;
|
||||
+
|
||||
struct {
|
||||
u16 id;
|
||||
__be16 proto;
|
||||
@@ -258,9 +260,45 @@ mtk_flow_offload_replace(struct mtk_eth
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
+ switch (addr_type) {
|
||||
+ case 0:
|
||||
+ offload_type = MTK_PPE_PKT_TYPE_BRIDGE;
|
||||
+ if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ETH_ADDRS)) {
|
||||
+ struct flow_match_eth_addrs match;
|
||||
+
|
||||
+ flow_rule_match_eth_addrs(rule, &match);
|
||||
+ memcpy(data.eth.h_dest, match.key->dst, ETH_ALEN);
|
||||
+ memcpy(data.eth.h_source, match.key->src, ETH_ALEN);
|
||||
+ } else {
|
||||
+ return -EOPNOTSUPP;
|
||||
+ }
|
||||
+
|
||||
+ if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_VLAN)) {
|
||||
+ struct flow_match_vlan match;
|
||||
+
|
||||
+ flow_rule_match_vlan(rule, &match);
|
||||
+
|
||||
+ if (match.key->vlan_tpid != cpu_to_be16(ETH_P_8021Q))
|
||||
+ return -EOPNOTSUPP;
|
||||
+
|
||||
+ data.vlan_in = match.key->vlan_id;
|
||||
+ }
|
||||
+ break;
|
||||
+ case FLOW_DISSECTOR_KEY_IPV4_ADDRS:
|
||||
+ offload_type = MTK_PPE_PKT_TYPE_IPV4_HNAPT;
|
||||
+ break;
|
||||
+ case FLOW_DISSECTOR_KEY_IPV6_ADDRS:
|
||||
+ offload_type = MTK_PPE_PKT_TYPE_IPV6_ROUTE_5T;
|
||||
+ break;
|
||||
+ default:
|
||||
+ return -EOPNOTSUPP;
|
||||
+ }
|
||||
+
|
||||
flow_action_for_each(i, act, &rule->action) {
|
||||
switch (act->id) {
|
||||
case FLOW_ACTION_MANGLE:
|
||||
+ if (offload_type == MTK_PPE_PKT_TYPE_BRIDGE)
|
||||
+ return -EOPNOTSUPP;
|
||||
if (act->mangle.htype == FLOW_ACT_MANGLE_HDR_TYPE_ETH)
|
||||
mtk_flow_offload_mangle_eth(act, &data.eth);
|
||||
break;
|
||||
@@ -292,17 +330,6 @@ mtk_flow_offload_replace(struct mtk_eth
|
||||
}
|
||||
}
|
||||
|
||||
- switch (addr_type) {
|
||||
- case FLOW_DISSECTOR_KEY_IPV4_ADDRS:
|
||||
- offload_type = MTK_PPE_PKT_TYPE_IPV4_HNAPT;
|
||||
- break;
|
||||
- case FLOW_DISSECTOR_KEY_IPV6_ADDRS:
|
||||
- offload_type = MTK_PPE_PKT_TYPE_IPV6_ROUTE_5T;
|
||||
- break;
|
||||
- default:
|
||||
- return -EOPNOTSUPP;
|
||||
- }
|
||||
-
|
||||
if (!is_valid_ether_addr(data.eth.h_source) ||
|
||||
!is_valid_ether_addr(data.eth.h_dest))
|
||||
return -EINVAL;
|
||||
@@ -316,10 +343,13 @@ mtk_flow_offload_replace(struct mtk_eth
|
||||
if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_PORTS)) {
|
||||
struct flow_match_ports ports;
|
||||
|
||||
+ if (offload_type == MTK_PPE_PKT_TYPE_BRIDGE)
|
||||
+ return -EOPNOTSUPP;
|
||||
+
|
||||
flow_rule_match_ports(rule, &ports);
|
||||
data.src_port = ports.key->src;
|
||||
data.dst_port = ports.key->dst;
|
||||
- } else {
|
||||
+ } else if (offload_type != MTK_PPE_PKT_TYPE_BRIDGE) {
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
@@ -349,6 +379,9 @@ mtk_flow_offload_replace(struct mtk_eth
|
||||
if (act->id != FLOW_ACTION_MANGLE)
|
||||
continue;
|
||||
|
||||
+ if (offload_type == MTK_PPE_PKT_TYPE_BRIDGE)
|
||||
+ return -EOPNOTSUPP;
|
||||
+
|
||||
switch (act->mangle.htype) {
|
||||
case FLOW_ACT_MANGLE_HDR_TYPE_TCP:
|
||||
case FLOW_ACT_MANGLE_HDR_TYPE_UDP:
|
||||
@@ -374,6 +407,9 @@ mtk_flow_offload_replace(struct mtk_eth
|
||||
return err;
|
||||
}
|
||||
|
||||
+ if (offload_type == MTK_PPE_PKT_TYPE_BRIDGE)
|
||||
+ foe.bridge.vlan = data.vlan_in;
|
||||
+
|
||||
if (data.vlan.num == 1) {
|
||||
if (data.vlan.proto != htons(ETH_P_8021Q))
|
||||
return -EOPNOTSUPP;
|
@ -166,7 +166,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
u16 addr_type = 0;
|
||||
u32 timestamp;
|
||||
u8 l4proto = 0;
|
||||
@@ -329,10 +372,14 @@ mtk_flow_offload_replace(struct mtk_eth
|
||||
@@ -326,10 +369,14 @@ mtk_flow_offload_replace(struct mtk_eth
|
||||
if (data.pppoe.num == 1)
|
||||
mtk_foe_entry_set_pppoe(&foe, data.pppoe.sid);
|
||||
|
||||
@ -182,7 +182,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
entry = kzalloc(sizeof(*entry), GFP_KERNEL);
|
||||
if (!entry)
|
||||
return -ENOMEM;
|
||||
@@ -346,6 +393,7 @@ mtk_flow_offload_replace(struct mtk_eth
|
||||
@@ -343,6 +390,7 @@ mtk_flow_offload_replace(struct mtk_eth
|
||||
}
|
||||
|
||||
entry->hash = hash;
|
||||
@ -190,7 +190,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
err = rhashtable_insert_fast(ð->flow_table, &entry->node,
|
||||
mtk_flow_ht_params);
|
||||
if (err < 0)
|
||||
@@ -356,6 +404,8 @@ clear_flow:
|
||||
@@ -353,6 +401,8 @@ clear_flow:
|
||||
mtk_foe_entry_clear(ð->ppe, hash);
|
||||
free:
|
||||
kfree(entry);
|
||||
@ -199,7 +199,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -372,6 +422,8 @@ mtk_flow_offload_destroy(struct mtk_eth
|
||||
@@ -369,6 +419,8 @@ mtk_flow_offload_destroy(struct mtk_eth
|
||||
mtk_foe_entry_clear(ð->ppe, entry->hash);
|
||||
rhashtable_remove_fast(ð->flow_table, &entry->node,
|
||||
mtk_flow_ht_params);
|
||||
|
@ -49,7 +49,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
static void
|
||||
mtk_flow_offload_mangle_eth(const struct flow_action_entry *act, void *eth)
|
||||
{
|
||||
@@ -299,6 +313,9 @@ mtk_flow_offload_replace(struct mtk_eth
|
||||
@@ -296,6 +310,9 @@ mtk_flow_offload_replace(struct mtk_eth
|
||||
case FLOW_DISSECTOR_KEY_IPV4_ADDRS:
|
||||
offload_type = MTK_PPE_PKT_TYPE_IPV4_HNAPT;
|
||||
break;
|
||||
@ -59,7 +59,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
@@ -334,6 +351,17 @@ mtk_flow_offload_replace(struct mtk_eth
|
||||
@@ -331,6 +348,17 @@ mtk_flow_offload_replace(struct mtk_eth
|
||||
mtk_flow_set_ipv4_addr(&foe, &data, false);
|
||||
}
|
||||
|
||||
|
@ -10,7 +10,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
|
||||
--- a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
|
||||
+++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
|
||||
@@ -566,10 +566,13 @@ mtk_eth_setup_tc_block(struct net_device
|
||||
@@ -563,10 +563,13 @@ mtk_eth_setup_tc_block(struct net_device
|
||||
int mtk_eth_setup_tc(struct net_device *dev, enum tc_setup_type type,
|
||||
void *type_data)
|
||||
{
|
||||
|
@ -103,7 +103,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
|
||||
--- a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
|
||||
+++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
|
||||
@@ -414,7 +414,7 @@ mtk_flow_offload_replace(struct mtk_eth
|
||||
@@ -411,7 +411,7 @@ mtk_flow_offload_replace(struct mtk_eth
|
||||
|
||||
entry->cookie = f->cookie;
|
||||
timestamp = mtk_eth_timestamp(eth);
|
||||
@ -112,7 +112,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
if (hash < 0) {
|
||||
err = hash;
|
||||
goto free;
|
||||
@@ -429,7 +429,7 @@ mtk_flow_offload_replace(struct mtk_eth
|
||||
@@ -426,7 +426,7 @@ mtk_flow_offload_replace(struct mtk_eth
|
||||
|
||||
return 0;
|
||||
clear_flow:
|
||||
@ -121,7 +121,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
free:
|
||||
kfree(entry);
|
||||
if (wed_index >= 0)
|
||||
@@ -447,7 +447,7 @@ mtk_flow_offload_destroy(struct mtk_eth
|
||||
@@ -444,7 +444,7 @@ mtk_flow_offload_destroy(struct mtk_eth
|
||||
if (!entry)
|
||||
return -ENOENT;
|
||||
|
||||
@ -130,7 +130,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
rhashtable_remove_fast(ð->flow_table, &entry->node,
|
||||
mtk_flow_ht_params);
|
||||
if (entry->wed_index >= 0)
|
||||
@@ -469,7 +469,7 @@ mtk_flow_offload_stats(struct mtk_eth *e
|
||||
@@ -466,7 +466,7 @@ mtk_flow_offload_stats(struct mtk_eth *e
|
||||
if (!entry)
|
||||
return -ENOENT;
|
||||
|
||||
@ -139,7 +139,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
if (timestamp < 0)
|
||||
return -ETIMEDOUT;
|
||||
|
||||
@@ -525,7 +525,7 @@ mtk_eth_setup_tc_block(struct net_device
|
||||
@@ -522,7 +522,7 @@ mtk_eth_setup_tc_block(struct net_device
|
||||
struct flow_block_cb *block_cb;
|
||||
flow_setup_cb_t *cb;
|
||||
|
||||
@ -148,7 +148,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (f->binder_type != FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS)
|
||||
@@ -577,7 +577,7 @@ int mtk_eth_setup_tc(struct net_device *
|
||||
@@ -574,7 +574,7 @@ int mtk_eth_setup_tc(struct net_device *
|
||||
|
||||
int mtk_eth_offload_init(struct mtk_eth *eth)
|
||||
{
|
||||
|
@ -360,7 +360,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
int i;
|
||||
|
||||
if (rhashtable_lookup(ð->flow_table, &f->cookie, mtk_flow_ht_params))
|
||||
@@ -413,23 +398,21 @@ mtk_flow_offload_replace(struct mtk_eth
|
||||
@@ -410,23 +395,21 @@ mtk_flow_offload_replace(struct mtk_eth
|
||||
return -ENOMEM;
|
||||
|
||||
entry->cookie = f->cookie;
|
||||
@ -392,7 +392,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
free:
|
||||
kfree(entry);
|
||||
if (wed_index >= 0)
|
||||
@@ -447,7 +430,7 @@ mtk_flow_offload_destroy(struct mtk_eth
|
||||
@@ -444,7 +427,7 @@ mtk_flow_offload_destroy(struct mtk_eth
|
||||
if (!entry)
|
||||
return -ENOENT;
|
||||
|
||||
@ -401,7 +401,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
rhashtable_remove_fast(ð->flow_table, &entry->node,
|
||||
mtk_flow_ht_params);
|
||||
if (entry->wed_index >= 0)
|
||||
@@ -461,7 +444,6 @@ static int
|
||||
@@ -458,7 +441,6 @@ static int
|
||||
mtk_flow_offload_stats(struct mtk_eth *eth, struct flow_cls_offload *f)
|
||||
{
|
||||
struct mtk_flow_entry *entry;
|
||||
@ -409,7 +409,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
u32 idle;
|
||||
|
||||
entry = rhashtable_lookup(ð->flow_table, &f->cookie,
|
||||
@@ -469,11 +451,7 @@ mtk_flow_offload_stats(struct mtk_eth *e
|
||||
@@ -466,11 +448,7 @@ mtk_flow_offload_stats(struct mtk_eth *e
|
||||
if (!entry)
|
||||
return -ENOENT;
|
||||
|
||||
|
@ -452,7 +452,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
struct {
|
||||
u16 id;
|
||||
__be16 proto;
|
||||
@@ -260,9 +262,45 @@ mtk_flow_offload_replace(struct mtk_eth
|
||||
@@ -257,9 +259,45 @@ mtk_flow_offload_replace(struct mtk_eth
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
@ -498,7 +498,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
if (act->mangle.htype == FLOW_ACT_MANGLE_HDR_TYPE_ETH)
|
||||
mtk_flow_offload_mangle_eth(act, &data.eth);
|
||||
break;
|
||||
@@ -294,17 +332,6 @@ mtk_flow_offload_replace(struct mtk_eth
|
||||
@@ -291,17 +329,6 @@ mtk_flow_offload_replace(struct mtk_eth
|
||||
}
|
||||
}
|
||||
|
||||
@ -516,7 +516,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
if (!is_valid_ether_addr(data.eth.h_source) ||
|
||||
!is_valid_ether_addr(data.eth.h_dest))
|
||||
return -EINVAL;
|
||||
@@ -318,10 +345,13 @@ mtk_flow_offload_replace(struct mtk_eth
|
||||
@@ -315,10 +342,13 @@ mtk_flow_offload_replace(struct mtk_eth
|
||||
if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_PORTS)) {
|
||||
struct flow_match_ports ports;
|
||||
|
||||
@ -531,7 +531,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
@@ -351,6 +381,9 @@ mtk_flow_offload_replace(struct mtk_eth
|
||||
@@ -348,6 +378,9 @@ mtk_flow_offload_replace(struct mtk_eth
|
||||
if (act->id != FLOW_ACTION_MANGLE)
|
||||
continue;
|
||||
|
||||
@ -541,7 +541,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
switch (act->mangle.htype) {
|
||||
case FLOW_ACT_MANGLE_HDR_TYPE_TCP:
|
||||
case FLOW_ACT_MANGLE_HDR_TYPE_UDP:
|
||||
@@ -376,6 +409,9 @@ mtk_flow_offload_replace(struct mtk_eth
|
||||
@@ -373,6 +406,9 @@ mtk_flow_offload_replace(struct mtk_eth
|
||||
return err;
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,4 @@
|
||||
CONFIG_64BIT=y
|
||||
CONFIG_AF_UNIX_OOB=y
|
||||
# CONFIG_AHCI_MTK is not set
|
||||
CONFIG_AQUANTIA_PHY=y
|
||||
CONFIG_ARCH_DMA_ADDR_T_64BIT=y
|
||||
@ -26,8 +25,6 @@ CONFIG_ARM64_MODULE_PLTS=y
|
||||
CONFIG_ARM64_PAGE_SHIFT=12
|
||||
CONFIG_ARM64_PA_BITS=48
|
||||
CONFIG_ARM64_PA_BITS_48=y
|
||||
# CONFIG_ARM64_PTR_AUTH is not set
|
||||
# CONFIG_ARM64_SVE is not set
|
||||
# CONFIG_ARM64_SW_TTBR0_PAN is not set
|
||||
CONFIG_ARM64_TAGGED_ADDR_ABI=y
|
||||
CONFIG_ARM64_VA_BITS=39
|
||||
@ -46,7 +43,6 @@ CONFIG_ARM_PMU=y
|
||||
CONFIG_ARM_PSCI_FW=y
|
||||
CONFIG_ATA=y
|
||||
CONFIG_AUDIT_ARCH_COMPAT_GENERIC=y
|
||||
CONFIG_BINARY_PRINTF=y
|
||||
CONFIG_BLK_DEV_LOOP=y
|
||||
CONFIG_BLK_DEV_SD=y
|
||||
CONFIG_BLK_MQ_PCI=y
|
||||
@ -220,7 +216,6 @@ CONFIG_LEDS_UBNT_LEDBAR=y
|
||||
CONFIG_LIBFDT=y
|
||||
CONFIG_LOCK_DEBUGGING_SUPPORT=y
|
||||
CONFIG_LOCK_SPIN_ON_OWNER=y
|
||||
CONFIG_LTO_NONE=y
|
||||
CONFIG_LZO_COMPRESS=y
|
||||
CONFIG_LZO_DECOMPRESS=y
|
||||
CONFIG_MAGIC_SYSRQ=y
|
||||
@ -278,7 +273,6 @@ CONFIG_NET_FLOW_LIMIT=y
|
||||
CONFIG_NET_MEDIATEK_SOC=y
|
||||
CONFIG_NET_MEDIATEK_SOC_WED=y
|
||||
CONFIG_NET_SELFTESTS=y
|
||||
CONFIG_NET_SOCK_MSG=y
|
||||
CONFIG_NET_SWITCHDEV=y
|
||||
CONFIG_NET_VENDOR_MEDIATEK=y
|
||||
CONFIG_NLS=y
|
||||
|
@ -1,4 +1,3 @@
|
||||
CONFIG_AF_UNIX_OOB=y
|
||||
# CONFIG_AIO is not set
|
||||
CONFIG_ALIGNMENT_TRAP=y
|
||||
CONFIG_ARCH_32BIT_OFF_T=y
|
||||
@ -41,7 +40,6 @@ CONFIG_BACKLIGHT_CLASS_DEVICE=y
|
||||
CONFIG_BACKLIGHT_GPIO=y
|
||||
CONFIG_BACKLIGHT_LED=y
|
||||
CONFIG_BACKLIGHT_PWM=y
|
||||
CONFIG_BINARY_PRINTF=y
|
||||
CONFIG_BINFMT_FLAT_ARGVP_ENVP_ON_STACK=y
|
||||
CONFIG_BLK_DEV_LOOP=y
|
||||
CONFIG_BLK_MQ_PCI=y
|
||||
@ -342,7 +340,6 @@ CONFIG_LOGO=y
|
||||
CONFIG_LOGO_LINUX_CLUT224=y
|
||||
# CONFIG_LOGO_LINUX_MONO is not set
|
||||
# CONFIG_LOGO_LINUX_VGA16 is not set
|
||||
CONFIG_LTO_NONE=y
|
||||
CONFIG_LZO_COMPRESS=y
|
||||
CONFIG_LZO_DECOMPRESS=y
|
||||
# CONFIG_MACH_MT2701 is not set
|
||||
@ -422,7 +419,6 @@ CONFIG_NET_FLOW_LIMIT=y
|
||||
CONFIG_NET_MEDIATEK_SOC=y
|
||||
CONFIG_NET_MEDIATEK_SOC_WED=y
|
||||
CONFIG_NET_SELFTESTS=y
|
||||
CONFIG_NET_SOCK_MSG=y
|
||||
CONFIG_NET_SWITCHDEV=y
|
||||
CONFIG_NET_VENDOR_MEDIATEK=y
|
||||
# CONFIG_NET_VENDOR_WIZNET is not set
|
||||
@ -593,9 +589,7 @@ CONFIG_USB_HIDDEV=y
|
||||
CONFIG_USB_INVENTRA_DMA=y
|
||||
CONFIG_USB_LIBCOMPOSITE=y
|
||||
CONFIG_USB_MUSB_DUAL_ROLE=y
|
||||
# CONFIG_USB_MUSB_GADGET is not set
|
||||
CONFIG_USB_MUSB_HDRC=y
|
||||
# CONFIG_USB_MUSB_HOST is not set
|
||||
CONFIG_USB_MUSB_MEDIATEK=y
|
||||
CONFIG_USB_OTG=y
|
||||
CONFIG_USB_PHY=y
|
||||
|
@ -1,831 +1,3 @@
|
||||
--- a/drivers/mtd/nand/Kconfig
|
||||
+++ b/drivers/mtd/nand/Kconfig
|
||||
@@ -15,6 +15,10 @@ config MTD_NAND_ECC
|
||||
bool
|
||||
depends on MTD_NAND_CORE
|
||||
|
||||
+config MTD_NAND_MTK_BMT
|
||||
+ bool "Support MediaTek NAND Bad-block Management Table"
|
||||
+ default n
|
||||
+
|
||||
endmenu
|
||||
|
||||
endmenu
|
||||
--- a/drivers/mtd/nand/Makefile
|
||||
+++ b/drivers/mtd/nand/Makefile
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
nandcore-objs := core.o bbt.o
|
||||
obj-$(CONFIG_MTD_NAND_CORE) += nandcore.o
|
||||
+obj-$(CONFIG_MTD_NAND_MTK_BMT) += mtk_bmt.o
|
||||
|
||||
obj-y += onenand/
|
||||
obj-y += raw/
|
||||
--- /dev/null
|
||||
+++ b/drivers/mtd/nand/mtk_bmt.c
|
||||
@@ -0,0 +1,781 @@
|
||||
+/*
|
||||
+ * Copyright (c) 2017 MediaTek Inc.
|
||||
+ * Author: Xiangsheng Hou <xiangsheng.hou@mediatek.com>
|
||||
+ * Copyright (c) 2020 Felix Fietkau <nbd@nbd.name>
|
||||
+ *
|
||||
+ * This program is free software; you can redistribute it and/or modify
|
||||
+ * it under the terms of the GNU General Public License version 2 as
|
||||
+ * published by the Free Software Foundation.
|
||||
+ *
|
||||
+ * This program is distributed in the hope that it will be useful,
|
||||
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
+ * GNU General Public License for more details.
|
||||
+ */
|
||||
+
|
||||
+#include <linux/slab.h>
|
||||
+#include <linux/gfp.h>
|
||||
+#include <linux/kernel.h>
|
||||
+#include <linux/of.h>
|
||||
+#include <linux/mtd/nand.h>
|
||||
+#include <linux/mtd/partitions.h>
|
||||
+#include <linux/mtd/mtk_bmt.h>
|
||||
+#include <linux/module.h>
|
||||
+#include <linux/debugfs.h>
|
||||
+
|
||||
+#define MAIN_SIGNATURE_OFFSET 0
|
||||
+#define OOB_SIGNATURE_OFFSET 1
|
||||
+#define BBPOOL_RATIO 2
|
||||
+
|
||||
+#define BBT_LOG(fmt, ...) pr_debug("[BBT][%s|%d] "fmt"\n", __func__, __LINE__, ##__VA_ARGS__)
|
||||
+
|
||||
+/* Maximum 8k blocks */
|
||||
+#define BB_TABLE_MAX bmtd.table_size
|
||||
+#define BMT_TABLE_MAX (BB_TABLE_MAX * BBPOOL_RATIO / 100)
|
||||
+#define BMT_TBL_DEF_VAL 0x0
|
||||
+
|
||||
+/*
|
||||
+ * Burner Bad Block Table
|
||||
+ * --------- Only support SLC Nand Chips!!!!!!!!!!! ----------
|
||||
+ */
|
||||
+
|
||||
+struct bbbt {
|
||||
+ char signature[3];
|
||||
+ /* This version is used to distinguish the legacy and new algorithm */
|
||||
+#define BBMT_VERSION 2
|
||||
+ unsigned char version;
|
||||
+ /* Below 2 tables will be written in SLC */
|
||||
+ u16 bb_tbl[];
|
||||
+};
|
||||
+
|
||||
+struct bbmt {
|
||||
+ u16 block;
|
||||
+#define NO_MAPPED 0
|
||||
+#define NORMAL_MAPPED 1
|
||||
+#define BMT_MAPPED 2
|
||||
+ u16 mapped;
|
||||
+};
|
||||
+
|
||||
+static struct bmt_desc {
|
||||
+ struct mtd_info *mtd;
|
||||
+
|
||||
+ int (*_read_oob) (struct mtd_info *mtd, loff_t from,
|
||||
+ struct mtd_oob_ops *ops);
|
||||
+ int (*_write_oob) (struct mtd_info *mtd, loff_t to,
|
||||
+ struct mtd_oob_ops *ops);
|
||||
+ const struct nand_ops *nand_ops;
|
||||
+
|
||||
+ struct bbbt *bbt;
|
||||
+
|
||||
+ struct dentry *debugfs_dir;
|
||||
+
|
||||
+ u32 table_size;
|
||||
+ u32 pg_size;
|
||||
+ u32 blk_size;
|
||||
+ u16 pg_shift;
|
||||
+ u16 blk_shift;
|
||||
+ /* bbt logical address */
|
||||
+ u16 pool_lba;
|
||||
+ /* bbt physical address */
|
||||
+ u16 pool_pba;
|
||||
+ /* Maximum count of bad blocks that the vendor guaranteed */
|
||||
+ u16 bb_max;
|
||||
+ /* Total blocks of the Nand Chip */
|
||||
+ u16 total_blks;
|
||||
+ /* The block(n) BMT is located at (bmt_tbl[n]) */
|
||||
+ u16 bmt_blk_idx;
|
||||
+ /* How many pages needs to store 'struct bbbt' */
|
||||
+ u32 bmt_pgs;
|
||||
+
|
||||
+ /* to compensate for driver level remapping */
|
||||
+ u8 oob_offset;
|
||||
+} bmtd = {0};
|
||||
+
|
||||
+static unsigned char *nand_bbt_buf;
|
||||
+static unsigned char *nand_data_buf;
|
||||
+
|
||||
+/* -------- Unit conversions -------- */
|
||||
+static inline u32 blk_pg(u16 block)
|
||||
+{
|
||||
+ return (u32)(block << (bmtd.blk_shift - bmtd.pg_shift));
|
||||
+}
|
||||
+
|
||||
+/* -------- Nand operations wrapper -------- */
|
||||
+static inline int
|
||||
+bbt_nand_read(u32 page, unsigned char *dat, int dat_len,
|
||||
+ unsigned char *fdm, int fdm_len)
|
||||
+{
|
||||
+ struct mtd_oob_ops ops = {
|
||||
+ .mode = MTD_OPS_PLACE_OOB,
|
||||
+ .ooboffs = bmtd.oob_offset,
|
||||
+ .oobbuf = fdm,
|
||||
+ .ooblen = fdm_len,
|
||||
+ .datbuf = dat,
|
||||
+ .len = dat_len,
|
||||
+ };
|
||||
+
|
||||
+ return bmtd._read_oob(bmtd.mtd, page << bmtd.pg_shift, &ops);
|
||||
+}
|
||||
+
|
||||
+static inline int bbt_nand_erase(u16 block)
|
||||
+{
|
||||
+ struct nand_device *nand = mtd_to_nanddev(bmtd.mtd);
|
||||
+ loff_t addr = (loff_t)block << bmtd.blk_shift;
|
||||
+ struct nand_pos pos;
|
||||
+
|
||||
+ nanddev_offs_to_pos(nand, addr, &pos);
|
||||
+ return bmtd.nand_ops->erase(nand, &pos);
|
||||
+}
|
||||
+
|
||||
+/* -------- Bad Blocks Management -------- */
|
||||
+static inline struct bbmt *bmt_tbl(struct bbbt *bbbt)
|
||||
+{
|
||||
+ return (struct bbmt *)&bbbt->bb_tbl[bmtd.table_size];
|
||||
+}
|
||||
+
|
||||
+static int
|
||||
+read_bmt(u16 block, unsigned char *dat, unsigned char *fdm, int fdm_len)
|
||||
+{
|
||||
+ u32 len = bmtd.bmt_pgs << bmtd.pg_shift;
|
||||
+
|
||||
+ return bbt_nand_read(blk_pg(block), dat, len, fdm, fdm_len);
|
||||
+}
|
||||
+
|
||||
+static int write_bmt(u16 block, unsigned char *dat)
|
||||
+{
|
||||
+ struct mtd_oob_ops ops = {
|
||||
+ .mode = MTD_OPS_PLACE_OOB,
|
||||
+ .ooboffs = OOB_SIGNATURE_OFFSET + bmtd.oob_offset,
|
||||
+ .oobbuf = "bmt",
|
||||
+ .ooblen = 3,
|
||||
+ .datbuf = dat,
|
||||
+ .len = bmtd.bmt_pgs << bmtd.pg_shift,
|
||||
+ };
|
||||
+ loff_t addr = (loff_t)block << bmtd.blk_shift;
|
||||
+
|
||||
+ return bmtd._write_oob(bmtd.mtd, addr, &ops);
|
||||
+}
|
||||
+
|
||||
+static u16 find_valid_block(u16 block)
|
||||
+{
|
||||
+ u8 fdm[4];
|
||||
+ int ret;
|
||||
+ int loop = 0;
|
||||
+
|
||||
+retry:
|
||||
+ if (block >= bmtd.total_blks)
|
||||
+ return 0;
|
||||
+
|
||||
+ ret = bbt_nand_read(blk_pg(block), nand_data_buf, bmtd.pg_size,
|
||||
+ fdm, sizeof(fdm));
|
||||
+ /* Read the 1st byte of FDM to judge whether it's a bad
|
||||
+ * or not
|
||||
+ */
|
||||
+ if (ret || fdm[0] != 0xff) {
|
||||
+ pr_info("nand: found bad block 0x%x\n", block);
|
||||
+ if (loop >= bmtd.bb_max) {
|
||||
+ pr_info("nand: FATAL ERR: too many bad blocks!!\n");
|
||||
+ return 0;
|
||||
+ }
|
||||
+
|
||||
+ loop++;
|
||||
+ block++;
|
||||
+ goto retry;
|
||||
+ }
|
||||
+
|
||||
+ return block;
|
||||
+}
|
||||
+
|
||||
+/* Find out all bad blocks, and fill in the mapping table */
|
||||
+static int scan_bad_blocks(struct bbbt *bbt)
|
||||
+{
|
||||
+ int i;
|
||||
+ u16 block = 0;
|
||||
+
|
||||
+ /* First time download, the block0 MUST NOT be a bad block,
|
||||
+ * this is guaranteed by vendor
|
||||
+ */
|
||||
+ bbt->bb_tbl[0] = 0;
|
||||
+
|
||||
+ /*
|
||||
+ * Construct the mapping table of Normal data area(non-PMT/BMTPOOL)
|
||||
+ * G - Good block; B - Bad block
|
||||
+ * ---------------------------
|
||||
+ * physical |G|G|B|G|B|B|G|G|G|G|B|G|B|
|
||||
+ * ---------------------------
|
||||
+ * What bb_tbl[i] looks like:
|
||||
+ * physical block(i):
|
||||
+ * 0 1 2 3 4 5 6 7 8 9 a b c
|
||||
+ * mapped block(bb_tbl[i]):
|
||||
+ * 0 1 3 6 7 8 9 b ......
|
||||
+ * ATTENTION:
|
||||
+ * If new bad block ocurred(n), search bmt_tbl to find
|
||||
+ * a available block(x), and fill in the bb_tbl[n] = x;
|
||||
+ */
|
||||
+ for (i = 1; i < bmtd.pool_lba; i++) {
|
||||
+ bbt->bb_tbl[i] = find_valid_block(bbt->bb_tbl[i - 1] + 1);
|
||||
+ BBT_LOG("bb_tbl[0x%x] = 0x%x", i, bbt->bb_tbl[i]);
|
||||
+ if (bbt->bb_tbl[i] == 0)
|
||||
+ return -1;
|
||||
+ }
|
||||
+
|
||||
+ /* Physical Block start Address of BMT pool */
|
||||
+ bmtd.pool_pba = bbt->bb_tbl[i - 1] + 1;
|
||||
+ if (bmtd.pool_pba >= bmtd.total_blks - 2) {
|
||||
+ pr_info("nand: FATAL ERR: Too many bad blocks!!\n");
|
||||
+ return -1;
|
||||
+ }
|
||||
+
|
||||
+ BBT_LOG("pool_pba=0x%x", bmtd.pool_pba);
|
||||
+ i = 0;
|
||||
+ block = bmtd.pool_pba;
|
||||
+ /*
|
||||
+ * The bmt table is used for runtime bad block mapping
|
||||
+ * G - Good block; B - Bad block
|
||||
+ * ---------------------------
|
||||
+ * physical |G|G|B|G|B|B|G|G|G|G|B|G|B|
|
||||
+ * ---------------------------
|
||||
+ * block: 0 1 2 3 4 5 6 7 8 9 a b c
|
||||
+ * What bmt_tbl[i] looks like in initial state:
|
||||
+ * i:
|
||||
+ * 0 1 2 3 4 5 6 7
|
||||
+ * bmt_tbl[i].block:
|
||||
+ * 0 1 3 6 7 8 9 b
|
||||
+ * bmt_tbl[i].mapped:
|
||||
+ * N N N N N N N B
|
||||
+ * N - Not mapped(Available)
|
||||
+ * M - Mapped
|
||||
+ * B - BMT
|
||||
+ * ATTENTION:
|
||||
+ * BMT always in the last valid block in pool
|
||||
+ */
|
||||
+ while ((block = find_valid_block(block)) != 0) {
|
||||
+ bmt_tbl(bbt)[i].block = block;
|
||||
+ bmt_tbl(bbt)[i].mapped = NO_MAPPED;
|
||||
+ BBT_LOG("bmt_tbl[%d].block = 0x%x", i, block);
|
||||
+ block++;
|
||||
+ i++;
|
||||
+ }
|
||||
+
|
||||
+ /* i - How many available blocks in pool, which is the length of bmt_tbl[]
|
||||
+ * bmtd.bmt_blk_idx - bmt_tbl[bmtd.bmt_blk_idx].block => the BMT block
|
||||
+ */
|
||||
+ bmtd.bmt_blk_idx = i - 1;
|
||||
+ bmt_tbl(bbt)[bmtd.bmt_blk_idx].mapped = BMT_MAPPED;
|
||||
+
|
||||
+ if (i < 1) {
|
||||
+ pr_info("nand: FATAL ERR: no space to store BMT!!\n");
|
||||
+ return -1;
|
||||
+ }
|
||||
+
|
||||
+ pr_info("[BBT] %d available blocks in BMT pool\n", i);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static bool is_valid_bmt(unsigned char *buf, unsigned char *fdm)
|
||||
+{
|
||||
+ struct bbbt *bbt = (struct bbbt *)buf;
|
||||
+ u8 *sig = (u8*)bbt->signature + MAIN_SIGNATURE_OFFSET;
|
||||
+
|
||||
+
|
||||
+ if (memcmp(bbt->signature + MAIN_SIGNATURE_OFFSET, "BMT", 3) == 0 &&
|
||||
+ memcmp(fdm + OOB_SIGNATURE_OFFSET, "bmt", 3) == 0) {
|
||||
+ if (bbt->version == BBMT_VERSION)
|
||||
+ return true;
|
||||
+ }
|
||||
+ BBT_LOG("[BBT] BMT Version not match,upgrage preloader and uboot please! sig=%02x%02x%02x, fdm=%02x%02x%02x",
|
||||
+ sig[0], sig[1], sig[2],
|
||||
+ fdm[1], fdm[2], fdm[3]);
|
||||
+ return false;
|
||||
+}
|
||||
+
|
||||
+static u16 get_bmt_index(struct bbmt *bmt)
|
||||
+{
|
||||
+ int i = 0;
|
||||
+
|
||||
+ while (bmt[i].block != BMT_TBL_DEF_VAL) {
|
||||
+ if (bmt[i].mapped == BMT_MAPPED)
|
||||
+ return i;
|
||||
+ i++;
|
||||
+ }
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static struct bbbt *scan_bmt(u16 block)
|
||||
+{
|
||||
+ u8 fdm[4];
|
||||
+
|
||||
+ if (block < bmtd.pool_lba)
|
||||
+ return NULL;
|
||||
+
|
||||
+ if (read_bmt(block, nand_bbt_buf, fdm, sizeof(fdm)))
|
||||
+ return scan_bmt(block - 1);
|
||||
+
|
||||
+ if (is_valid_bmt(nand_bbt_buf, fdm)) {
|
||||
+ bmtd.bmt_blk_idx = get_bmt_index(bmt_tbl((struct bbbt *)nand_bbt_buf));
|
||||
+ if (bmtd.bmt_blk_idx == 0) {
|
||||
+ pr_info("[BBT] FATAL ERR: bmt block index is wrong!\n");
|
||||
+ return NULL;
|
||||
+ }
|
||||
+ pr_info("[BBT] BMT.v2 is found at 0x%x\n", block);
|
||||
+ return (struct bbbt *)nand_bbt_buf;
|
||||
+ } else
|
||||
+ return scan_bmt(block - 1);
|
||||
+}
|
||||
+
|
||||
+/* Write the Burner Bad Block Table to Nand Flash
|
||||
+ * n - write BMT to bmt_tbl[n]
|
||||
+ */
|
||||
+static u16 upload_bmt(struct bbbt *bbt, int n)
|
||||
+{
|
||||
+ u16 block;
|
||||
+
|
||||
+retry:
|
||||
+ if (n < 0 || bmt_tbl(bbt)[n].mapped == NORMAL_MAPPED) {
|
||||
+ pr_info("nand: FATAL ERR: no space to store BMT!\n");
|
||||
+ return (u16)-1;
|
||||
+ }
|
||||
+
|
||||
+ block = bmt_tbl(bbt)[n].block;
|
||||
+ BBT_LOG("n = 0x%x, block = 0x%x", n, block);
|
||||
+ if (bbt_nand_erase(block)) {
|
||||
+ bmt_tbl(bbt)[n].block = 0;
|
||||
+ /* erase failed, try the previous block: bmt_tbl[n - 1].block */
|
||||
+ n--;
|
||||
+ goto retry;
|
||||
+ }
|
||||
+
|
||||
+ /* The signature offset is fixed set to 0,
|
||||
+ * oob signature offset is fixed set to 1
|
||||
+ */
|
||||
+ memcpy(bbt->signature + MAIN_SIGNATURE_OFFSET, "BMT", 3);
|
||||
+ bbt->version = BBMT_VERSION;
|
||||
+
|
||||
+ if (write_bmt(block, (unsigned char *)bbt)) {
|
||||
+ bmt_tbl(bbt)[n].block = 0;
|
||||
+
|
||||
+ /* write failed, try the previous block in bmt_tbl[n - 1] */
|
||||
+ n--;
|
||||
+ goto retry;
|
||||
+ }
|
||||
+
|
||||
+ /* Return the current index(n) of BMT pool (bmt_tbl[n]) */
|
||||
+ return n;
|
||||
+}
|
||||
+
|
||||
+static u16 find_valid_block_in_pool(struct bbbt *bbt)
|
||||
+{
|
||||
+ int i;
|
||||
+
|
||||
+ if (bmtd.bmt_blk_idx == 0)
|
||||
+ goto error;
|
||||
+
|
||||
+ for (i = 0; i < bmtd.bmt_blk_idx; i++) {
|
||||
+ if (bmt_tbl(bbt)[i].block != 0 && bmt_tbl(bbt)[i].mapped == NO_MAPPED) {
|
||||
+ bmt_tbl(bbt)[i].mapped = NORMAL_MAPPED;
|
||||
+ return bmt_tbl(bbt)[i].block;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+error:
|
||||
+ pr_info("nand: FATAL ERR: BMT pool is run out!\n");
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+/* We met a bad block, mark it as bad and map it to a valid block in pool,
|
||||
+ * if it's a write failure, we need to write the data to mapped block
|
||||
+ */
|
||||
+static bool update_bmt(u16 block)
|
||||
+{
|
||||
+ u16 mapped_blk;
|
||||
+ struct bbbt *bbt;
|
||||
+
|
||||
+ bbt = bmtd.bbt;
|
||||
+ mapped_blk = find_valid_block_in_pool(bbt);
|
||||
+ if (mapped_blk == 0)
|
||||
+ return false;
|
||||
+
|
||||
+ /* Map new bad block to available block in pool */
|
||||
+ bbt->bb_tbl[block] = mapped_blk;
|
||||
+ bmtd.bmt_blk_idx = upload_bmt(bbt, bmtd.bmt_blk_idx);
|
||||
+
|
||||
+ return true;
|
||||
+}
|
||||
+
|
||||
+u16 get_mapping_block_index(int block)
|
||||
+{
|
||||
+ int mapping_block;
|
||||
+
|
||||
+ if (block < bmtd.pool_lba)
|
||||
+ mapping_block = bmtd.bbt->bb_tbl[block];
|
||||
+ else
|
||||
+ mapping_block = block;
|
||||
+ BBT_LOG("0x%x mapped to 0x%x", block, mapping_block);
|
||||
+
|
||||
+ return mapping_block;
|
||||
+}
|
||||
+
|
||||
+static int
|
||||
+mtk_bmt_read(struct mtd_info *mtd, loff_t from,
|
||||
+ struct mtd_oob_ops *ops)
|
||||
+{
|
||||
+ struct mtd_oob_ops cur_ops = *ops;
|
||||
+ int retry_count = 0;
|
||||
+ loff_t cur_from;
|
||||
+ int ret;
|
||||
+
|
||||
+ ops->retlen = 0;
|
||||
+ ops->oobretlen = 0;
|
||||
+
|
||||
+ while (ops->retlen < ops->len || ops->oobretlen < ops->ooblen) {
|
||||
+ u32 offset = from & (bmtd.blk_size - 1);
|
||||
+ u32 block = from >> bmtd.blk_shift;
|
||||
+ u32 cur_block;
|
||||
+
|
||||
+ cur_block = get_mapping_block_index(block);
|
||||
+ cur_from = ((loff_t)cur_block << bmtd.blk_shift) + offset;
|
||||
+
|
||||
+ cur_ops.oobretlen = 0;
|
||||
+ cur_ops.retlen = 0;
|
||||
+ cur_ops.len = min_t(u32, mtd->erasesize - offset,
|
||||
+ ops->len - ops->retlen);
|
||||
+ ret = bmtd._read_oob(mtd, cur_from, &cur_ops);
|
||||
+ if (ret < 0) {
|
||||
+ update_bmt(block);
|
||||
+ if (retry_count++ < 10)
|
||||
+ continue;
|
||||
+
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ ops->retlen += cur_ops.retlen;
|
||||
+ ops->oobretlen += cur_ops.oobretlen;
|
||||
+
|
||||
+ cur_ops.ooboffs = 0;
|
||||
+ cur_ops.datbuf += cur_ops.retlen;
|
||||
+ cur_ops.oobbuf += cur_ops.oobretlen;
|
||||
+ cur_ops.ooblen -= cur_ops.oobretlen;
|
||||
+
|
||||
+ if (!cur_ops.len)
|
||||
+ cur_ops.len = mtd->erasesize - offset;
|
||||
+
|
||||
+ from += cur_ops.len;
|
||||
+ retry_count = 0;
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int
|
||||
+mtk_bmt_write(struct mtd_info *mtd, loff_t to,
|
||||
+ struct mtd_oob_ops *ops)
|
||||
+{
|
||||
+ struct mtd_oob_ops cur_ops = *ops;
|
||||
+ int retry_count = 0;
|
||||
+ loff_t cur_to;
|
||||
+ int ret;
|
||||
+
|
||||
+ ops->retlen = 0;
|
||||
+ ops->oobretlen = 0;
|
||||
+
|
||||
+ while (ops->retlen < ops->len || ops->oobretlen < ops->ooblen) {
|
||||
+ u32 offset = to & (bmtd.blk_size - 1);
|
||||
+ u32 block = to >> bmtd.blk_shift;
|
||||
+ u32 cur_block;
|
||||
+
|
||||
+ cur_block = get_mapping_block_index(block);
|
||||
+ cur_to = ((loff_t)cur_block << bmtd.blk_shift) + offset;
|
||||
+
|
||||
+ cur_ops.oobretlen = 0;
|
||||
+ cur_ops.retlen = 0;
|
||||
+ cur_ops.len = min_t(u32, bmtd.blk_size - offset,
|
||||
+ ops->len - ops->retlen);
|
||||
+ ret = bmtd._write_oob(mtd, cur_to, &cur_ops);
|
||||
+ if (ret < 0) {
|
||||
+ update_bmt(block);
|
||||
+ if (retry_count++ < 10)
|
||||
+ continue;
|
||||
+
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ ops->retlen += cur_ops.retlen;
|
||||
+ ops->oobretlen += cur_ops.oobretlen;
|
||||
+
|
||||
+ cur_ops.ooboffs = 0;
|
||||
+ cur_ops.datbuf += cur_ops.retlen;
|
||||
+ cur_ops.oobbuf += cur_ops.oobretlen;
|
||||
+ cur_ops.ooblen -= cur_ops.oobretlen;
|
||||
+
|
||||
+ if (!cur_ops.len)
|
||||
+ cur_ops.len = mtd->erasesize - offset;
|
||||
+
|
||||
+ to += cur_ops.len;
|
||||
+ retry_count = 0;
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+
|
||||
+
|
||||
+static int
|
||||
+mtk_bmt_erase(struct nand_device *nand, const struct nand_pos *pos)
|
||||
+{
|
||||
+ struct nand_pos new_pos = *pos;
|
||||
+ int retry_count = 0;
|
||||
+ int ret;
|
||||
+
|
||||
+retry:
|
||||
+ new_pos.eraseblock = get_mapping_block_index(pos->eraseblock);
|
||||
+
|
||||
+ ret = bmtd.nand_ops->erase(nand, &new_pos);
|
||||
+ if (ret) {
|
||||
+ update_bmt(pos->eraseblock);
|
||||
+ if (retry_count++ < 10)
|
||||
+ goto retry;
|
||||
+ }
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+static bool
|
||||
+mtk_bmt_isbad(struct nand_device *nand, const struct nand_pos *pos)
|
||||
+{
|
||||
+ struct nand_pos new_pos = *pos;
|
||||
+ int retry_count = 0;
|
||||
+ bool ret;
|
||||
+
|
||||
+retry:
|
||||
+ new_pos.eraseblock = get_mapping_block_index(pos->eraseblock);
|
||||
+
|
||||
+ ret = bmtd.nand_ops->isbad(nand, &new_pos);
|
||||
+ if (ret) {
|
||||
+ update_bmt(pos->eraseblock);
|
||||
+ if (retry_count++ < 10)
|
||||
+ goto retry;
|
||||
+ }
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+static int
|
||||
+mtk_bmt_markbad(struct nand_device *nand, const struct nand_pos *pos)
|
||||
+{
|
||||
+ struct nand_pos new_pos = *pos;
|
||||
+
|
||||
+ new_pos.eraseblock = get_mapping_block_index(new_pos.eraseblock);
|
||||
+ update_bmt(pos->eraseblock);
|
||||
+
|
||||
+ return bmtd.nand_ops->markbad(nand, &new_pos);
|
||||
+}
|
||||
+
|
||||
+static void
|
||||
+mtk_bmt_replace_ops(struct mtd_info *mtd)
|
||||
+{
|
||||
+ static const struct nand_ops mtk_bmt_nand_ops = {
|
||||
+ .erase = mtk_bmt_erase,
|
||||
+ .isbad = mtk_bmt_isbad,
|
||||
+ .markbad = mtk_bmt_markbad,
|
||||
+ };
|
||||
+ struct nand_device *nand = mtd_to_nanddev(mtd);
|
||||
+
|
||||
+ bmtd.nand_ops = nand->ops;
|
||||
+ bmtd._read_oob = mtd->_read_oob;
|
||||
+ bmtd._write_oob = mtd->_write_oob;
|
||||
+
|
||||
+ mtd->_read_oob = mtk_bmt_read;
|
||||
+ mtd->_write_oob = mtk_bmt_write;
|
||||
+ nand->ops = &mtk_bmt_nand_ops;
|
||||
+}
|
||||
+
|
||||
+static int mtk_bmt_debug_mark_good(void *data, u64 val)
|
||||
+{
|
||||
+ u32 block = val >> bmtd.blk_shift;
|
||||
+
|
||||
+ bmtd.bbt->bb_tbl[block] = block;
|
||||
+ bmtd.bmt_blk_idx = upload_bmt(bmtd.bbt, bmtd.bmt_blk_idx);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int mtk_bmt_debug_mark_bad(void *data, u64 val)
|
||||
+{
|
||||
+ u32 block = val >> bmtd.blk_shift;
|
||||
+
|
||||
+ update_bmt(block);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+DEFINE_DEBUGFS_ATTRIBUTE(fops_mark_good, NULL, mtk_bmt_debug_mark_good, "%llu\n");
|
||||
+DEFINE_DEBUGFS_ATTRIBUTE(fops_mark_bad, NULL, mtk_bmt_debug_mark_bad, "%llu\n");
|
||||
+
|
||||
+static void
|
||||
+mtk_bmt_add_debugfs(void)
|
||||
+{
|
||||
+ struct dentry *dir;
|
||||
+
|
||||
+ dir = bmtd.debugfs_dir = debugfs_create_dir("mtk-bmt", NULL);
|
||||
+ if (!dir)
|
||||
+ return;
|
||||
+
|
||||
+ debugfs_create_file_unsafe("mark_good", S_IWUSR, dir, NULL, &fops_mark_good);
|
||||
+ debugfs_create_file_unsafe("mark_bad", S_IWUSR, dir, NULL, &fops_mark_bad);
|
||||
+}
|
||||
+
|
||||
+void mtk_bmt_detach(struct mtd_info *mtd)
|
||||
+{
|
||||
+ struct nand_device *nand = mtd_to_nanddev(mtd);
|
||||
+
|
||||
+ if (bmtd.mtd != mtd)
|
||||
+ return;
|
||||
+
|
||||
+ if (bmtd.debugfs_dir)
|
||||
+ debugfs_remove_recursive(bmtd.debugfs_dir);
|
||||
+ bmtd.debugfs_dir = NULL;
|
||||
+
|
||||
+ kfree(nand_bbt_buf);
|
||||
+ kfree(nand_data_buf);
|
||||
+
|
||||
+ mtd->_read_oob = bmtd._read_oob;
|
||||
+ mtd->_write_oob = bmtd._write_oob;
|
||||
+ mtd->size = bmtd.total_blks << bmtd.blk_shift;
|
||||
+ nand->ops = bmtd.nand_ops;
|
||||
+
|
||||
+ memset(&bmtd, 0, sizeof(bmtd));
|
||||
+}
|
||||
+
|
||||
+/* total_blocks - The total count of blocks that the Nand Chip has */
|
||||
+int mtk_bmt_attach(struct mtd_info *mtd)
|
||||
+{
|
||||
+ struct device_node *np;
|
||||
+ struct bbbt *bbt;
|
||||
+ u32 bufsz;
|
||||
+ u32 block;
|
||||
+ u16 total_blocks, pmt_block;
|
||||
+ int ret = 0;
|
||||
+ u32 bmt_pool_size, bmt_table_size;
|
||||
+
|
||||
+ if (bmtd.mtd)
|
||||
+ return -ENOSPC;
|
||||
+
|
||||
+ np = mtd_get_of_node(mtd);
|
||||
+ if (!np)
|
||||
+ return 0;
|
||||
+
|
||||
+ if (!of_property_read_bool(np, "mediatek,bmt-v2"))
|
||||
+ return 0;
|
||||
+
|
||||
+ if (of_property_read_u32(np, "mediatek,bmt-pool-size",
|
||||
+ &bmt_pool_size) != 0)
|
||||
+ bmt_pool_size = 80;
|
||||
+
|
||||
+ if (of_property_read_u8(np, "mediatek,bmt-oob-offset",
|
||||
+ &bmtd.oob_offset) != 0)
|
||||
+ bmtd.oob_offset = 0;
|
||||
+
|
||||
+ if (of_property_read_u32(np, "mediatek,bmt-table-size",
|
||||
+ &bmt_table_size) != 0)
|
||||
+ bmt_table_size = 0x2000U;
|
||||
+
|
||||
+ bmtd.mtd = mtd;
|
||||
+ mtk_bmt_replace_ops(mtd);
|
||||
+
|
||||
+ bmtd.table_size = bmt_table_size;
|
||||
+ bmtd.blk_size = mtd->erasesize;
|
||||
+ bmtd.blk_shift = ffs(bmtd.blk_size) - 1;
|
||||
+ bmtd.pg_size = mtd->writesize;
|
||||
+ bmtd.pg_shift = ffs(bmtd.pg_size) - 1;
|
||||
+ total_blocks = mtd->size >> bmtd.blk_shift;
|
||||
+ pmt_block = total_blocks - bmt_pool_size - 2;
|
||||
+
|
||||
+ mtd->size = pmt_block << bmtd.blk_shift;
|
||||
+
|
||||
+ /*
|
||||
+ * ---------------------------------------
|
||||
+ * | PMT(2blks) | BMT POOL(totalblks * 2%) |
|
||||
+ * ---------------------------------------
|
||||
+ * ^ ^
|
||||
+ * | |
|
||||
+ * pmt_block pmt_block + 2blocks(pool_lba)
|
||||
+ *
|
||||
+ * ATTETION!!!!!!
|
||||
+ * The blocks ahead of the boundary block are stored in bb_tbl
|
||||
+ * and blocks behind are stored in bmt_tbl
|
||||
+ */
|
||||
+
|
||||
+ bmtd.pool_lba = (u16)(pmt_block + 2);
|
||||
+ bmtd.total_blks = total_blocks;
|
||||
+ bmtd.bb_max = bmtd.total_blks * BBPOOL_RATIO / 100;
|
||||
+
|
||||
+ /* 3 buffers we need */
|
||||
+ bufsz = round_up(sizeof(struct bbbt) +
|
||||
+ bmt_table_size * sizeof(struct bbmt), bmtd.pg_size);
|
||||
+ bmtd.bmt_pgs = bufsz >> bmtd.pg_shift;
|
||||
+
|
||||
+ nand_bbt_buf = kzalloc(bufsz, GFP_KERNEL);
|
||||
+ nand_data_buf = kzalloc(bmtd.pg_size, GFP_KERNEL);
|
||||
+
|
||||
+ if (!nand_bbt_buf || !nand_data_buf) {
|
||||
+ pr_info("nand: FATAL ERR: allocate buffer failed!\n");
|
||||
+ ret = -1;
|
||||
+ goto error;
|
||||
+ }
|
||||
+
|
||||
+ memset(nand_bbt_buf, 0xff, bufsz);
|
||||
+ memset(nand_data_buf, 0xff, bmtd.pg_size);
|
||||
+
|
||||
+ BBT_LOG("bbtbuf=0x%p(0x%x) dat=0x%p(0x%x)",
|
||||
+ nand_bbt_buf, bufsz, nand_data_buf, bmtd.pg_size);
|
||||
+ BBT_LOG("pool_lba=0x%x total_blks=0x%x bb_max=0x%x",
|
||||
+ bmtd.pool_lba, bmtd.total_blks, bmtd.bb_max);
|
||||
+
|
||||
+ /* Scanning start from the first page of the last block
|
||||
+ * of whole flash
|
||||
+ */
|
||||
+ bbt = scan_bmt(bmtd.total_blks - 1);
|
||||
+ if (!bbt) {
|
||||
+ /* BMT not found */
|
||||
+ if (bmtd.total_blks > BB_TABLE_MAX + BMT_TABLE_MAX) {
|
||||
+ pr_info("nand: FATAL: Too many blocks, can not support!\n");
|
||||
+ ret = -1;
|
||||
+ goto error;
|
||||
+ }
|
||||
+
|
||||
+ bbt = (struct bbbt *)nand_bbt_buf;
|
||||
+ memset(bmt_tbl(bbt), BMT_TBL_DEF_VAL, bmtd.table_size * sizeof(struct bbmt));
|
||||
+
|
||||
+ if (scan_bad_blocks(bbt)) {
|
||||
+ ret = -1;
|
||||
+ goto error;
|
||||
+ }
|
||||
+
|
||||
+ /* BMT always in the last valid block in pool */
|
||||
+ bmtd.bmt_blk_idx = upload_bmt(bbt, bmtd.bmt_blk_idx);
|
||||
+ block = bmt_tbl(bbt)[bmtd.bmt_blk_idx].block;
|
||||
+ pr_notice("[BBT] BMT.v2 is written into PBA:0x%x\n", block);
|
||||
+
|
||||
+ if (bmtd.bmt_blk_idx == 0)
|
||||
+ pr_info("nand: Warning: no available block in BMT pool!\n");
|
||||
+ else if (bmtd.bmt_blk_idx == (u16)-1) {
|
||||
+ ret = -1;
|
||||
+ goto error;
|
||||
+ }
|
||||
+ }
|
||||
+ mtk_bmt_add_debugfs();
|
||||
+
|
||||
+ bmtd.bbt = bbt;
|
||||
+ return 0;
|
||||
+
|
||||
+error:
|
||||
+ mtk_bmt_detach(mtd);
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+
|
||||
+MODULE_LICENSE("GPL");
|
||||
+MODULE_AUTHOR("Xiangsheng Hou <xiangsheng.hou@mediatek.com>, Felix Fietkau <nbd@nbd.name>");
|
||||
+MODULE_DESCRIPTION("Bad Block mapping management v2 for MediaTek NAND Flash Driver");
|
||||
+
|
||||
--- /dev/null
|
||||
+++ b/include/linux/mtd/mtk_bmt.h
|
||||
@@ -0,0 +1,18 @@
|
||||
+#ifndef __MTK_BMT_H
|
||||
+#define __MTK_BMT_H
|
||||
+
|
||||
+#ifdef CONFIG_MTD_NAND_MTK_BMT
|
||||
+int mtk_bmt_attach(struct mtd_info *mtd);
|
||||
+void mtk_bmt_detach(struct mtd_info *mtd);
|
||||
+#else
|
||||
+static inline int mtk_bmt_attach(struct mtd_info *mtd)
|
||||
+{
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static inline void mtk_bmt_detach(struct mtd_info *mtd)
|
||||
+{
|
||||
+}
|
||||
+#endif
|
||||
+
|
||||
+#endif
|
||||
--- a/drivers/mtd/mtk-snand/mtk-snand-mtd.c
|
||||
+++ b/drivers/mtd/mtk-snand/mtk-snand-mtd.c
|
||||
@@ -16,6 +16,7 @@
|
||||
|
@ -19,7 +19,7 @@
|
||||
},
|
||||
[PORT_NPCM] = {
|
||||
.name = "Nuvoton 16550",
|
||||
@@ -2725,6 +2725,11 @@ serial8250_do_set_termios(struct uart_po
|
||||
@@ -2735,6 +2735,11 @@ serial8250_do_set_termios(struct uart_po
|
||||
unsigned long flags;
|
||||
unsigned int baud, quot, frac = 0;
|
||||
|
||||
|
@ -44,18 +44,6 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
|
||||
slot1: pcie@1,0 {
|
||||
reg = <0x0800 0 0 0 0>;
|
||||
@@ -933,6 +939,11 @@
|
||||
};
|
||||
};
|
||||
|
||||
+ hifsys: syscon@1af00000 {
|
||||
+ compatible = "mediatek,mt7622-hifsys", "syscon";
|
||||
+ reg = <0 0x1af00000 0 0x70>;
|
||||
+ };
|
||||
+
|
||||
ethsys: syscon@1b000000 {
|
||||
compatible = "mediatek,mt7622-ethsys",
|
||||
"syscon";
|
||||
--- a/drivers/pci/controller/pcie-mediatek.c
|
||||
+++ b/drivers/pci/controller/pcie-mediatek.c
|
||||
@@ -20,6 +20,7 @@
|
||||
|
@ -19,7 +19,7 @@
|
||||
},
|
||||
[PORT_NPCM] = {
|
||||
.name = "Nuvoton 16550",
|
||||
@@ -2746,6 +2746,11 @@ serial8250_do_set_termios(struct uart_po
|
||||
@@ -2764,6 +2764,11 @@ serial8250_do_set_termios(struct uart_po
|
||||
unsigned long flags;
|
||||
unsigned int baud, quot, frac = 0;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user