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);
+int mtk_flow_offload_cmd(struct mtk_eth *eth, struct flow_cls_offload *cls,
+                        int ppe_index);
+void mtk_flow_offload_cleanup(struct mtk_eth *eth, struct list_head *list);
 void mtk_eth_set_dma_device(struct mtk_eth *eth, struct device *dma_dev);
 
 
 
 }
 
 static int
-mtk_flow_offload_replace(struct mtk_eth *eth, struct flow_cls_offload *f)
+mtk_flow_offload_replace(struct mtk_eth *eth, struct flow_cls_offload *f,
+                        int ppe_index)
 {
        struct flow_rule *rule = flow_cls_offload_flow_rule(f);
        struct flow_action_entry *act;
        entry->cookie = f->cookie;
        memcpy(&entry->data, &foe, sizeof(entry->data));
        entry->wed_index = wed_index;
+       entry->ppe_index = ppe_index;
 
        err = mtk_foe_entry_commit(eth->ppe[entry->ppe_index], entry);
        if (err < 0)
 
 static DEFINE_MUTEX(mtk_flow_offload_mutex);
 
-static int
-mtk_eth_setup_tc_block_cb(enum tc_setup_type type, void *type_data, void *cb_priv)
+int mtk_flow_offload_cmd(struct mtk_eth *eth, struct flow_cls_offload *cls,
+                        int ppe_index)
 {
-       struct flow_cls_offload *cls = type_data;
-       struct net_device *dev = cb_priv;
-       struct mtk_mac *mac = netdev_priv(dev);
-       struct mtk_eth *eth = mac->hw;
        int err;
 
-       if (!tc_can_offload(dev))
-               return -EOPNOTSUPP;
-
-       if (type != TC_SETUP_CLSFLOWER)
-               return -EOPNOTSUPP;
-
        mutex_lock(&mtk_flow_offload_mutex);
        switch (cls->command) {
        case FLOW_CLS_REPLACE:
-               err = mtk_flow_offload_replace(eth, cls);
+               err = mtk_flow_offload_replace(eth, cls, ppe_index);
                break;
        case FLOW_CLS_DESTROY:
                err = mtk_flow_offload_destroy(eth, cls);
        return err;
 }
 
+static int
+mtk_eth_setup_tc_block_cb(enum tc_setup_type type, void *type_data, void *cb_priv)
+{
+       struct flow_cls_offload *cls = type_data;
+       struct net_device *dev = cb_priv;
+       struct mtk_mac *mac;
+       struct mtk_eth *eth;
+
+       mac = netdev_priv(dev);
+       eth = mac->hw;
+
+       if (!tc_can_offload(dev))
+               return -EOPNOTSUPP;
+
+       if (type != TC_SETUP_CLSFLOWER)
+               return -EOPNOTSUPP;
+
+       return mtk_flow_offload_cmd(eth, cls, 0);
+}
+
 static int
 mtk_eth_setup_tc_block(struct net_device *dev, struct flow_block_offload *f)
 {
 
 #include <linux/mfd/syscon.h>
 #include <linux/debugfs.h>
 #include <linux/soc/mediatek/mtk_wed.h>
+#include <net/flow_offload.h>
+#include <net/pkt_cls.h>
 #include "mtk_eth_soc.h"
 #include "mtk_wed_regs.h"
 #include "mtk_wed.h"
 static struct mtk_wed_hw *hw_list[2];
 static DEFINE_MUTEX(hw_lock);
 
+struct mtk_wed_flow_block_priv {
+       struct mtk_wed_hw *hw;
+       struct net_device *dev;
+};
+
 static void
 wed_m32(struct mtk_wed_device *dev, u32 reg, u32 mask, u32 val)
 {
        mutex_unlock(&hw_lock);
 }
 
+static int
+mtk_wed_setup_tc_block_cb(enum tc_setup_type type, void *type_data, void *cb_priv)
+{
+       struct mtk_wed_flow_block_priv *priv = cb_priv;
+       struct flow_cls_offload *cls = type_data;
+       struct mtk_wed_hw *hw = priv->hw;
+
+       if (!tc_can_offload(priv->dev))
+               return -EOPNOTSUPP;
+
+       if (type != TC_SETUP_CLSFLOWER)
+               return -EOPNOTSUPP;
+
+       return mtk_flow_offload_cmd(hw->eth, cls, hw->index);
+}
+
+static int
+mtk_wed_setup_tc_block(struct mtk_wed_hw *hw, struct net_device *dev,
+                      struct flow_block_offload *f)
+{
+       struct mtk_wed_flow_block_priv *priv;
+       static LIST_HEAD(block_cb_list);
+       struct flow_block_cb *block_cb;
+       struct mtk_eth *eth = hw->eth;
+       flow_setup_cb_t *cb;
+
+       if (!eth->soc->offload_version)
+               return -EOPNOTSUPP;
+
+       if (f->binder_type != FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS)
+               return -EOPNOTSUPP;
+
+       cb = mtk_wed_setup_tc_block_cb;
+       f->driver_block_list = &block_cb_list;
+
+       switch (f->command) {
+       case FLOW_BLOCK_BIND:
+               block_cb = flow_block_cb_lookup(f->block, cb, dev);
+               if (block_cb) {
+                       flow_block_cb_incref(block_cb);
+                       return 0;
+               }
+
+               priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+               if (!priv)
+                       return -ENOMEM;
+
+               priv->hw = hw;
+               priv->dev = dev;
+               block_cb = flow_block_cb_alloc(cb, dev, priv, NULL);
+               if (IS_ERR(block_cb)) {
+                       kfree(priv);
+                       return PTR_ERR(block_cb);
+               }
+
+               flow_block_cb_incref(block_cb);
+               flow_block_cb_add(block_cb, f);
+               list_add_tail(&block_cb->driver_list, &block_cb_list);
+               return 0;
+       case FLOW_BLOCK_UNBIND:
+               block_cb = flow_block_cb_lookup(f->block, cb, dev);
+               if (!block_cb)
+                       return -ENOENT;
+
+               if (!flow_block_cb_decref(block_cb)) {
+                       flow_block_cb_remove(block_cb, f);
+                       list_del(&block_cb->driver_list);
+                       kfree(block_cb->cb_priv);
+               }
+               return 0;
+       default:
+               return -EOPNOTSUPP;
+       }
+}
+
+static int
+mtk_wed_setup_tc(struct mtk_wed_device *wed, struct net_device *dev,
+                enum tc_setup_type type, void *type_data)
+{
+       struct mtk_wed_hw *hw = wed->hw;
+
+       if (hw->version < 2)
+               return -EOPNOTSUPP;
+
+       switch (type) {
+       case TC_SETUP_BLOCK:
+       case TC_SETUP_FT:
+               return mtk_wed_setup_tc_block(hw, dev, type_data);
+       default:
+               return -EOPNOTSUPP;
+       }
+}
+
 void mtk_wed_add_hw(struct device_node *np, struct mtk_eth *eth,
                    void __iomem *wdma, phys_addr_t wdma_phy,
                    int index)
                .irq_set_mask = mtk_wed_irq_set_mask,
                .detach = mtk_wed_detach,
                .ppe_check = mtk_wed_ppe_check,
+               .setup_tc = mtk_wed_setup_tc,
        };
        struct device_node *eth_np = eth->dev->of_node;
        struct platform_device *pdev;
 
 #include <linux/regmap.h>
 #include <linux/pci.h>
 #include <linux/skbuff.h>
+#include <linux/netdevice.h>
 
 #define MTK_WED_TX_QUEUES              2
 #define MTK_WED_RX_QUEUES              2
 
        u32 (*irq_get)(struct mtk_wed_device *dev, u32 mask);
        void (*irq_set_mask)(struct mtk_wed_device *dev, u32 mask);
+       int (*setup_tc)(struct mtk_wed_device *wed, struct net_device *dev,
+                       enum tc_setup_type type, void *type_data);
 };
 
 extern const struct mtk_wed_ops __rcu *mtk_soc_wed_ops;
        (_dev)->ops->msg_update(_dev, _id, _msg, _len)
 #define mtk_wed_device_stop(_dev) (_dev)->ops->stop(_dev)
 #define mtk_wed_device_dma_reset(_dev) (_dev)->ops->reset_dma(_dev)
+#define mtk_wed_device_setup_tc(_dev, _netdev, _type, _type_data) \
+       (_dev)->ops->setup_tc(_dev, _netdev, _type, _type_data)
 #else
 static inline bool mtk_wed_device_active(struct mtk_wed_device *dev)
 {
 #define mtk_wed_device_update_msg(_dev, _id, _msg, _len) -ENODEV
 #define mtk_wed_device_stop(_dev) do {} while (0)
 #define mtk_wed_device_dma_reset(_dev) do {} while (0)
+#define mtk_wed_device_setup_tc(_dev, _netdev, _type, _type_data) -EOPNOTSUPP
 #endif
 
 #endif