depends on PCI && PCI_MSI
        select FSL_ENETC_MDIO
        select PHYLIB
+       select DIMLIB
        help
          This driver supports NXP ENETC gigabit ethernet controller PCIe
          physical function (PF) devices, managing ENETC Ports at a privileged
        tristate "ENETC VF driver"
        depends on PCI && PCI_MSI
        select PHYLIB
+       select DIMLIB
        help
          This driver supports NXP ENETC gigabit ethernet controller PCIe
          virtual function (VF) devices enabled by the ENETC PF driver.
 
 static int enetc_clean_rx_ring(struct enetc_bdr *rx_ring,
                               struct napi_struct *napi, int work_limit);
 
+static void enetc_rx_dim_work(struct work_struct *w)
+{
+       struct dim *dim = container_of(w, struct dim, work);
+       struct dim_cq_moder moder =
+               net_dim_get_rx_moderation(dim->mode, dim->profile_ix);
+       struct enetc_int_vector *v =
+               container_of(dim, struct enetc_int_vector, rx_dim);
+
+       v->rx_ictt = enetc_usecs_to_cycles(moder.usec);
+       dim->state = DIM_START_MEASURE;
+}
+
+static void enetc_rx_net_dim(struct enetc_int_vector *v)
+{
+       struct dim_sample dim_sample;
+
+       v->comp_cnt++;
+
+       if (!v->rx_napi_work)
+               return;
+
+       dim_update_sample(v->comp_cnt,
+                         v->rx_ring.stats.packets,
+                         v->rx_ring.stats.bytes,
+                         &dim_sample);
+       net_dim(&v->rx_dim, dim_sample);
+}
+
 static int enetc_poll(struct napi_struct *napi, int budget)
 {
        struct enetc_int_vector
        work_done = enetc_clean_rx_ring(&v->rx_ring, napi, budget);
        if (work_done == budget)
                complete = false;
+       if (work_done)
+               v->rx_napi_work = true;
 
        if (!complete)
                return budget;
 
        napi_complete_done(napi, work_done);
 
+       if (likely(v->rx_dim_en))
+               enetc_rx_net_dim(v);
+
+       v->rx_napi_work = false;
+
        /* enable interrupts */
        enetc_wr_reg(v->rbier, ENETC_RBIER_RXTIE);
 
        priv->num_rx_rings = min_t(int, cpus, si->num_rx_rings);
        priv->num_tx_rings = si->num_tx_rings;
        priv->bdr_int_num = cpus;
+       priv->ic_mode = ENETC_IC_RX_ADAPTIVE | ENETC_IC_TX_MANUAL;
+       priv->tx_ictt = ENETC_TXIC_TIMETHR;
 
        /* SI specific */
        si->cbd_ring.bd_count = ENETC_CBDR_DEFAULT_SIZE;
        int i;
 
        /* enable Tx & Rx event indication */
-       if (priv->ic_mode & ENETC_IC_RX_MANUAL) {
+       if (priv->ic_mode &
+           (ENETC_IC_RX_MANUAL | ENETC_IC_RX_ADAPTIVE)) {
                icpt = ENETC_RBICR0_SET_ICPT(ENETC_RXIC_PKTTHR);
                /* init to non-0 minimum, will be adjusted later */
                ictt = 0x1;
 
                priv->int_vector[i] = v;
 
+               /* init defaults for adaptive IC */
+               if (priv->ic_mode & ENETC_IC_RX_ADAPTIVE) {
+                       v->rx_ictt = 0x1;
+                       v->rx_dim_en = true;
+               }
+               INIT_WORK(&v->rx_dim.work, enetc_rx_dim_work);
                netif_napi_add(priv->ndev, &v->napi, enetc_poll,
                               NAPI_POLL_WEIGHT);
                v->count_tx_rings = v_tx_rings;
 fail:
        while (i--) {
                netif_napi_del(&priv->int_vector[i]->napi);
+               cancel_work_sync(&priv->int_vector[i]->rx_dim.work);
                kfree(priv->int_vector[i]);
        }
 
                struct enetc_int_vector *v = priv->int_vector[i];
 
                netif_napi_del(&v->napi);
+               cancel_work_sync(&v->rx_dim.work);
        }
 
        for (i = 0; i < priv->num_rx_rings; i++)
 
 #include <linux/ethtool.h>
 #include <linux/if_vlan.h>
 #include <linux/phy.h>
+#include <linux/dim.h>
 
 #include "enetc_hw.h"
 
        unsigned long tx_rings_map;
        int count_tx_rings;
        u32 rx_ictt;
-       struct napi_struct napi;
+       u16 comp_cnt;
+       bool rx_dim_en, rx_napi_work;
+       struct napi_struct napi ____cacheline_aligned_in_smp;
+       struct dim rx_dim ____cacheline_aligned_in_smp;
        char name[ENETC_INT_NAME_MAX];
 
        struct enetc_bdr rx_ring;
        struct enetc_bdr tx_ring[];
-};
+} ____cacheline_aligned_in_smp;
 
 struct enetc_cls_rule {
        struct ethtool_rx_flow_spec fs;
        /* activated when int coalescing time is set to a non-0 value */
        ENETC_IC_RX_MANUAL = BIT(0),
        ENETC_IC_TX_MANUAL = BIT(1),
+       /* use dynamic interrupt moderation */
+       ENETC_IC_RX_ADAPTIVE = BIT(2),
 };
 
 #define ENETC_RXIC_PKTTHR      min_t(u32, 256, ENETC_RX_RING_DEFAULT_SIZE / 2)
 #define ENETC_TXIC_PKTTHR      min_t(u32, 128, ENETC_TX_RING_DEFAULT_SIZE / 2)
+#define ENETC_TXIC_TIMETHR     enetc_usecs_to_cycles(600)
 
 struct enetc_ndev_priv {
        struct net_device *ndev;
 
        ic->tx_max_coalesced_frames = ENETC_TXIC_PKTTHR;
        ic->rx_max_coalesced_frames = ENETC_RXIC_PKTTHR;
 
+       ic->use_adaptive_rx_coalesce = priv->ic_mode & ENETC_IC_RX_ADAPTIVE;
+
        return 0;
 }
 
                return -EOPNOTSUPP;
 
        ic_mode = ENETC_IC_NONE;
+       if (ic->use_adaptive_rx_coalesce) {
+               ic_mode |= ENETC_IC_RX_ADAPTIVE;
+               rx_ictt = 0x1;
+       } else {
+               ic_mode |= rx_ictt ? ENETC_IC_RX_MANUAL : 0;
+       }
+
        ic_mode |= tx_ictt ? ENETC_IC_TX_MANUAL : 0;
-       ic_mode |= rx_ictt ? ENETC_IC_RX_MANUAL : 0;
 
        /* commit the settings */
-       changed = (ic_mode != priv->ic_mode);
+       changed = (ic_mode != priv->ic_mode) || (priv->tx_ictt != tx_ictt);
 
        priv->ic_mode = ic_mode;
        priv->tx_ictt = tx_ictt;
                struct enetc_int_vector *v = priv->int_vector[i];
 
                v->rx_ictt = rx_ictt;
+               v->rx_dim_en = !!(ic_mode & ENETC_IC_RX_ADAPTIVE);
        }
 
        if (netif_running(ndev) && changed) {
 
 static const struct ethtool_ops enetc_pf_ethtool_ops = {
        .supported_coalesce_params = ETHTOOL_COALESCE_USECS |
-                                    ETHTOOL_COALESCE_MAX_FRAMES,
+                                    ETHTOOL_COALESCE_MAX_FRAMES |
+                                    ETHTOOL_COALESCE_USE_ADAPTIVE_RX,
        .get_regs_len = enetc_get_reglen,
        .get_regs = enetc_get_regs,
        .get_sset_count = enetc_get_sset_count,
 
 static const struct ethtool_ops enetc_vf_ethtool_ops = {
        .supported_coalesce_params = ETHTOOL_COALESCE_USECS |
-                                    ETHTOOL_COALESCE_MAX_FRAMES,
+                                    ETHTOOL_COALESCE_MAX_FRAMES |
+                                    ETHTOOL_COALESCE_USE_ADAPTIVE_RX,
        .get_regs_len = enetc_get_reglen,
        .get_regs = enetc_get_regs,
        .get_sset_count = enetc_get_sset_count,