bool ethtool_convert_link_mode_to_legacy_u32(u32 *legacy_u32,
                                     const unsigned long *src);
 
+#define ETHTOOL_COALESCE_RX_USECS              BIT(0)
+#define ETHTOOL_COALESCE_RX_MAX_FRAMES         BIT(1)
+#define ETHTOOL_COALESCE_RX_USECS_IRQ          BIT(2)
+#define ETHTOOL_COALESCE_RX_MAX_FRAMES_IRQ     BIT(3)
+#define ETHTOOL_COALESCE_TX_USECS              BIT(4)
+#define ETHTOOL_COALESCE_TX_MAX_FRAMES         BIT(5)
+#define ETHTOOL_COALESCE_TX_USECS_IRQ          BIT(6)
+#define ETHTOOL_COALESCE_TX_MAX_FRAMES_IRQ     BIT(7)
+#define ETHTOOL_COALESCE_STATS_BLOCK_USECS     BIT(8)
+#define ETHTOOL_COALESCE_USE_ADAPTIVE_RX       BIT(9)
+#define ETHTOOL_COALESCE_USE_ADAPTIVE_TX       BIT(10)
+#define ETHTOOL_COALESCE_PKT_RATE_LOW          BIT(11)
+#define ETHTOOL_COALESCE_RX_USECS_LOW          BIT(12)
+#define ETHTOOL_COALESCE_RX_MAX_FRAMES_LOW     BIT(13)
+#define ETHTOOL_COALESCE_TX_USECS_LOW          BIT(14)
+#define ETHTOOL_COALESCE_TX_MAX_FRAMES_LOW     BIT(15)
+#define ETHTOOL_COALESCE_PKT_RATE_HIGH         BIT(16)
+#define ETHTOOL_COALESCE_RX_USECS_HIGH         BIT(17)
+#define ETHTOOL_COALESCE_RX_MAX_FRAMES_HIGH    BIT(18)
+#define ETHTOOL_COALESCE_TX_USECS_HIGH         BIT(19)
+#define ETHTOOL_COALESCE_TX_MAX_FRAMES_HIGH    BIT(20)
+#define ETHTOOL_COALESCE_RATE_SAMPLE_INTERVAL  BIT(21)
+
+#define ETHTOOL_COALESCE_USECS                                         \
+       (ETHTOOL_COALESCE_RX_USECS | ETHTOOL_COALESCE_TX_USECS)
+#define ETHTOOL_COALESCE_MAX_FRAMES                                    \
+       (ETHTOOL_COALESCE_RX_MAX_FRAMES | ETHTOOL_COALESCE_TX_MAX_FRAMES)
+#define ETHTOOL_COALESCE_USECS_IRQ                                     \
+       (ETHTOOL_COALESCE_RX_USECS_IRQ | ETHTOOL_COALESCE_TX_USECS_IRQ)
+#define ETHTOOL_COALESCE_MAX_FRAMES_IRQ                \
+       (ETHTOOL_COALESCE_RX_MAX_FRAMES_IRQ |   \
+        ETHTOOL_COALESCE_TX_MAX_FRAMES_IRQ)
+#define ETHTOOL_COALESCE_USE_ADAPTIVE                                  \
+       (ETHTOOL_COALESCE_USE_ADAPTIVE_RX | ETHTOOL_COALESCE_USE_ADAPTIVE_TX)
+
 /**
  * struct ethtool_ops - optional netdev operations
+ * @supported_coalesce_params: supported types of interrupt coalescing.
  * @get_drvinfo: Report driver/device information.  Should only set the
  *     @driver, @version, @fw_version and @bus_info fields.  If not
  *     implemented, the @driver and @bus_info fields will be filled in
  *     or zero.
  * @get_coalesce: Get interrupt coalescing parameters.  Returns a negative
  *     error code or zero.
- * @set_coalesce: Set interrupt coalescing parameters.  Returns a negative
- *     error code or zero.
+ * @set_coalesce: Set interrupt coalescing parameters.  Supported coalescing
+ *     types should be set in @supported_coalesce_params.
+ *     Returns a negative error code or zero.
  * @get_ringparam: Report ring sizes
  * @set_ringparam: Set ring sizes.  Returns a negative error code or zero.
  * @get_pauseparam: Report pause parameters
  * @set_per_queue_coalesce: Set interrupt coalescing parameters per queue.
  *     It must check that the given queue number is valid. If neither a RX nor
  *     a TX queue has this number, return -EINVAL. If only a RX queue or a TX
- *     queue has this number, ignore the inapplicable fields.
+ *     queue has this number, ignore the inapplicable fields. Supported
+ *     coalescing types should be set in @supported_coalesce_params.
  *     Returns a negative error code or zero.
  * @get_link_ksettings: Get various device settings including Ethernet link
  *     settings. The %cmd and %link_mode_masks_nwords fields should be
  * of the generic netdev features interface.
  */
 struct ethtool_ops {
+       u32     supported_coalesce_params;
        void    (*get_drvinfo)(struct net_device *, struct ethtool_drvinfo *);
        int     (*get_regs_len)(struct net_device *);
        void    (*get_regs)(struct net_device *, struct ethtool_regs *, void *);
 
        return 0;
 }
 
+static bool
+ethtool_set_coalesce_supported(struct net_device *dev,
+                              struct ethtool_coalesce *coalesce)
+{
+       u32 supported_params = dev->ethtool_ops->supported_coalesce_params;
+       u32 nonzero_params = 0;
+
+       if (!supported_params)
+               return true;
+
+       if (coalesce->rx_coalesce_usecs)
+               nonzero_params |= ETHTOOL_COALESCE_RX_USECS;
+       if (coalesce->rx_max_coalesced_frames)
+               nonzero_params |= ETHTOOL_COALESCE_RX_MAX_FRAMES;
+       if (coalesce->rx_coalesce_usecs_irq)
+               nonzero_params |= ETHTOOL_COALESCE_RX_USECS_IRQ;
+       if (coalesce->rx_max_coalesced_frames_irq)
+               nonzero_params |= ETHTOOL_COALESCE_RX_MAX_FRAMES_IRQ;
+       if (coalesce->tx_coalesce_usecs)
+               nonzero_params |= ETHTOOL_COALESCE_TX_USECS;
+       if (coalesce->tx_max_coalesced_frames)
+               nonzero_params |= ETHTOOL_COALESCE_TX_MAX_FRAMES;
+       if (coalesce->tx_coalesce_usecs_irq)
+               nonzero_params |= ETHTOOL_COALESCE_TX_USECS_IRQ;
+       if (coalesce->tx_max_coalesced_frames_irq)
+               nonzero_params |= ETHTOOL_COALESCE_TX_MAX_FRAMES_IRQ;
+       if (coalesce->stats_block_coalesce_usecs)
+               nonzero_params |= ETHTOOL_COALESCE_STATS_BLOCK_USECS;
+       if (coalesce->use_adaptive_rx_coalesce)
+               nonzero_params |= ETHTOOL_COALESCE_USE_ADAPTIVE_RX;
+       if (coalesce->use_adaptive_tx_coalesce)
+               nonzero_params |= ETHTOOL_COALESCE_USE_ADAPTIVE_TX;
+       if (coalesce->pkt_rate_low)
+               nonzero_params |= ETHTOOL_COALESCE_PKT_RATE_LOW;
+       if (coalesce->rx_coalesce_usecs_low)
+               nonzero_params |= ETHTOOL_COALESCE_RX_USECS_LOW;
+       if (coalesce->rx_max_coalesced_frames_low)
+               nonzero_params |= ETHTOOL_COALESCE_RX_MAX_FRAMES_LOW;
+       if (coalesce->tx_coalesce_usecs_low)
+               nonzero_params |= ETHTOOL_COALESCE_TX_USECS_LOW;
+       if (coalesce->tx_max_coalesced_frames_low)
+               nonzero_params |= ETHTOOL_COALESCE_TX_MAX_FRAMES_LOW;
+       if (coalesce->pkt_rate_high)
+               nonzero_params |= ETHTOOL_COALESCE_PKT_RATE_HIGH;
+       if (coalesce->rx_coalesce_usecs_high)
+               nonzero_params |= ETHTOOL_COALESCE_RX_USECS_HIGH;
+       if (coalesce->rx_max_coalesced_frames_high)
+               nonzero_params |= ETHTOOL_COALESCE_RX_MAX_FRAMES_HIGH;
+       if (coalesce->tx_coalesce_usecs_high)
+               nonzero_params |= ETHTOOL_COALESCE_TX_USECS_HIGH;
+       if (coalesce->tx_max_coalesced_frames_high)
+               nonzero_params |= ETHTOOL_COALESCE_TX_MAX_FRAMES_HIGH;
+       if (coalesce->rate_sample_interval)
+               nonzero_params |= ETHTOOL_COALESCE_RATE_SAMPLE_INTERVAL;
+
+       return (supported_params & nonzero_params) == nonzero_params;
+}
+
 static noinline_for_stack int ethtool_set_coalesce(struct net_device *dev,
                                                   void __user *useraddr)
 {
        if (copy_from_user(&coalesce, useraddr, sizeof(coalesce)))
                return -EFAULT;
 
+       if (!ethtool_set_coalesce_supported(dev, &coalesce))
+               return -EOPNOTSUPP;
+
        return dev->ethtool_ops->set_coalesce(dev, &coalesce);
 }
 
                        goto roll_back;
                }
 
+               if (!ethtool_set_coalesce_supported(dev, &coalesce)) {
+                       ret = -EOPNOTSUPP;
+                       goto roll_back;
+               }
+
                ret = dev->ethtool_ops->set_per_queue_coalesce(dev, bit, &coalesce);
                if (ret != 0)
                        goto roll_back;