lan966x_ptp.o lan966x_fdma.o lan966x_lag.o \
                        lan966x_tc.o lan966x_mqprio.o lan966x_taprio.o \
                        lan966x_tbf.o lan966x_cbs.o lan966x_ets.o \
-                       lan966x_tc_matchall.o lan966x_police.o lan966x_mirror.o
+                       lan966x_tc_matchall.o lan966x_police.o lan966x_mirror.o \
+                       lan966x_xdp.o
 
 static int lan966x_fdma_rx_check_frame(struct lan966x_rx *rx, u64 *src_port)
 {
        struct lan966x *lan966x = rx->lan966x;
+       struct lan966x_port *port;
        struct lan966x_db *db;
        struct page *page;
 
        if (WARN_ON(*src_port >= lan966x->num_phys_ports))
                return FDMA_ERROR;
 
-       return FDMA_PASS;
+       port = lan966x->ports[*src_port];
+       if (!lan966x_xdp_port_present(port))
+               return FDMA_PASS;
+
+       return lan966x_xdp_run(port, page, FDMA_DCB_STATUS_BLOCKL(db->status));
 }
 
 static struct sk_buff *lan966x_fdma_rx_get_frame(struct lan966x_rx *rx,
                        lan966x_fdma_rx_free_page(rx);
                        lan966x_fdma_rx_advance_dcb(rx);
                        goto allocate_new;
+               case FDMA_DROP:
+                       lan966x_fdma_rx_free_page(rx);
+                       lan966x_fdma_rx_advance_dcb(rx);
+                       continue;
                }
 
                skb = lan966x_fdma_rx_get_frame(rx, src_port);
 
        .ndo_get_port_parent_id         = lan966x_port_get_parent_id,
        .ndo_eth_ioctl                  = lan966x_port_ioctl,
        .ndo_setup_tc                   = lan966x_tc_setup,
+       .ndo_bpf                        = lan966x_xdp,
 };
 
 bool lan966x_netdevice_check(const struct net_device *dev)
                if (port->dev)
                        unregister_netdev(port->dev);
 
+               lan966x_xdp_port_deinit(port);
                if (lan966x->fdma && lan966x->fdma_ndev == port->dev)
                        lan966x_fdma_netdev_deinit(lan966x, port->dev);
 
                lan966x->ports[p]->serdes = serdes;
 
                lan966x_port_init(lan966x->ports[p]);
+               err = lan966x_xdp_port_init(lan966x->ports[p]);
+               if (err)
+                       goto cleanup_ports;
        }
 
        lan966x_mdb_init(lan966x);
 
 /* FDMA return action codes for checking if the frame is valid
  * FDMA_PASS, frame is valid and can be used
  * FDMA_ERROR, something went wrong, stop getting more frames
+ * FDMA_DROP, frame is dropped, but continue to get more frames
  */
 enum lan966x_fdma_action {
        FDMA_PASS = 0,
        FDMA_ERROR,
+       FDMA_DROP,
 };
 
 struct lan966x_port;
        enum netdev_lag_hash hash_type;
 
        struct lan966x_port_tc tc;
+
+       struct bpf_prog *xdp_prog;
+       struct xdp_rxq_info xdp_rxq;
 };
 
 extern const struct phylink_mac_ops lan966x_phylink_mac_ops;
                               struct flow_stats *stats,
                               bool ingress);
 
+int lan966x_xdp_port_init(struct lan966x_port *port);
+void lan966x_xdp_port_deinit(struct lan966x_port *port);
+int lan966x_xdp(struct net_device *dev, struct netdev_bpf *xdp);
+int lan966x_xdp_run(struct lan966x_port *port,
+                   struct page *page,
+                   u32 data_len);
+static inline bool lan966x_xdp_port_present(struct lan966x_port *port)
+{
+       return !!port->xdp_prog;
+}
+
 static inline void __iomem *lan_addr(void __iomem *base[],
                                     int id, int tinst, int tcnt,
                                     int gbase, int ginst,
 
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0+
+
+#include <linux/bpf.h>
+#include <linux/bpf_trace.h>
+#include <linux/filter.h>
+
+#include "lan966x_main.h"
+
+static int lan966x_xdp_setup(struct net_device *dev, struct netdev_bpf *xdp)
+{
+       struct lan966x_port *port = netdev_priv(dev);
+       struct lan966x *lan966x = port->lan966x;
+       struct bpf_prog *old_prog;
+
+       if (!lan966x->fdma) {
+               NL_SET_ERR_MSG_MOD(xdp->extack,
+                                  "Allow to set xdp only when using fdma");
+               return -EOPNOTSUPP;
+       }
+
+       old_prog = xchg(&port->xdp_prog, xdp->prog);
+       if (old_prog)
+               bpf_prog_put(old_prog);
+
+       return 0;
+}
+
+int lan966x_xdp(struct net_device *dev, struct netdev_bpf *xdp)
+{
+       switch (xdp->command) {
+       case XDP_SETUP_PROG:
+               return lan966x_xdp_setup(dev, xdp);
+       default:
+               return -EINVAL;
+       }
+}
+
+int lan966x_xdp_run(struct lan966x_port *port, struct page *page, u32 data_len)
+{
+       struct bpf_prog *xdp_prog = port->xdp_prog;
+       struct lan966x *lan966x = port->lan966x;
+       struct xdp_buff xdp;
+       u32 act;
+
+       xdp_init_buff(&xdp, PAGE_SIZE << lan966x->rx.page_order,
+                     &port->xdp_rxq);
+       xdp_prepare_buff(&xdp, page_address(page), IFH_LEN_BYTES,
+                        data_len - IFH_LEN_BYTES, false);
+       act = bpf_prog_run_xdp(xdp_prog, &xdp);
+       switch (act) {
+       case XDP_PASS:
+               return FDMA_PASS;
+       default:
+               bpf_warn_invalid_xdp_action(port->dev, xdp_prog, act);
+               fallthrough;
+       case XDP_ABORTED:
+               trace_xdp_exception(port->dev, xdp_prog, act);
+               fallthrough;
+       case XDP_DROP:
+               return FDMA_DROP;
+       }
+}
+
+int lan966x_xdp_port_init(struct lan966x_port *port)
+{
+       struct lan966x *lan966x = port->lan966x;
+
+       return xdp_rxq_info_reg(&port->xdp_rxq, port->dev, 0,
+                               lan966x->napi.napi_id);
+}
+
+void lan966x_xdp_port_deinit(struct lan966x_port *port)
+{
+       if (xdp_rxq_info_is_reg(&port->xdp_rxq))
+               xdp_rxq_info_unreg(&port->xdp_rxq);
+}