}
 #endif
 
+static int mwifiex_get_coalesce_pkt_type(u8 *byte_seq)
+{
+       const u8 ipv4_mc_mac[] = {0x33, 0x33};
+       const u8 ipv6_mc_mac[] = {0x01, 0x00, 0x5e};
+       const u8 bc_mac[] = {0xff, 0xff, 0xff, 0xff};
+
+       if ((byte_seq[0] & 0x01) &&
+           (byte_seq[MWIFIEX_COALESCE_MAX_BYTESEQ] == 1))
+               return PACKET_TYPE_UNICAST;
+       else if (!memcmp(byte_seq, bc_mac, 4))
+               return PACKET_TYPE_BROADCAST;
+       else if ((!memcmp(byte_seq, ipv4_mc_mac, 2) &&
+                 byte_seq[MWIFIEX_COALESCE_MAX_BYTESEQ] == 2) ||
+                (!memcmp(byte_seq, ipv6_mc_mac, 3) &&
+                 byte_seq[MWIFIEX_COALESCE_MAX_BYTESEQ] == 3))
+               return PACKET_TYPE_MULTICAST;
+
+       return 0;
+}
+
+static int
+mwifiex_fill_coalesce_rule_info(struct mwifiex_private *priv,
+                               struct cfg80211_coalesce_rules *crule,
+                               struct mwifiex_coalesce_rule *mrule)
+{
+       u8 byte_seq[MWIFIEX_COALESCE_MAX_BYTESEQ + 1];
+       struct filt_field_param *param;
+       int i;
+
+       mrule->max_coalescing_delay = crule->delay;
+
+       param = mrule->params;
+
+       for (i = 0; i < crule->n_patterns; i++) {
+               memset(byte_seq, 0, sizeof(byte_seq));
+               if (!mwifiex_is_pattern_supported(&crule->patterns[i],
+                                                 byte_seq,
+                                               MWIFIEX_COALESCE_MAX_BYTESEQ)) {
+                       dev_err(priv->adapter->dev, "Pattern not supported\n");
+                       return -EOPNOTSUPP;
+               }
+
+               if (!crule->patterns[i].pkt_offset) {
+                       u8 pkt_type;
+
+                       pkt_type = mwifiex_get_coalesce_pkt_type(byte_seq);
+                       if (pkt_type && mrule->pkt_type) {
+                               dev_err(priv->adapter->dev,
+                                       "Multiple packet types not allowed\n");
+                               return -EOPNOTSUPP;
+                       } else if (pkt_type) {
+                               mrule->pkt_type = pkt_type;
+                               continue;
+                       }
+               }
+
+               if (crule->condition == NL80211_COALESCE_CONDITION_MATCH)
+                       param->operation = RECV_FILTER_MATCH_TYPE_EQ;
+               else
+                       param->operation = RECV_FILTER_MATCH_TYPE_NE;
+
+               param->operand_len = byte_seq[MWIFIEX_COALESCE_MAX_BYTESEQ];
+               memcpy(param->operand_byte_stream, byte_seq,
+                      param->operand_len);
+               param->offset = crule->patterns[i].pkt_offset;
+               param++;
+
+               mrule->num_of_fields++;
+       }
+
+       if (!mrule->pkt_type) {
+               dev_err(priv->adapter->dev,
+                       "Packet type can not be determined\n");
+               return -EOPNOTSUPP;
+       }
+
+       return 0;
+}
+
+static int mwifiex_cfg80211_set_coalesce(struct wiphy *wiphy,
+                                        struct cfg80211_coalesce *coalesce)
+{
+       struct mwifiex_adapter *adapter = mwifiex_cfg80211_get_adapter(wiphy);
+       int i, ret;
+       struct mwifiex_ds_coalesce_cfg coalesce_cfg;
+       struct mwifiex_private *priv =
+                       mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA);
+
+       memset(&coalesce_cfg, 0, sizeof(coalesce_cfg));
+       if (!coalesce) {
+               dev_dbg(adapter->dev,
+                       "Disable coalesce and reset all previous rules\n");
+               return mwifiex_send_cmd_sync(priv, HostCmd_CMD_COALESCE_CFG,
+                                            HostCmd_ACT_GEN_SET, 0,
+                                            &coalesce_cfg);
+       }
+
+       coalesce_cfg.num_of_rules = coalesce->n_rules;
+       for (i = 0; i < coalesce->n_rules; i++) {
+               ret = mwifiex_fill_coalesce_rule_info(priv, &coalesce->rules[i],
+                                                     &coalesce_cfg.rule[i]);
+               if (ret) {
+                       dev_err(priv->adapter->dev,
+                               "Recheck the patterns provided for rule %d\n",
+                               i + 1);
+                       return ret;
+               }
+       }
+
+       return mwifiex_send_cmd_sync(priv, HostCmd_CMD_COALESCE_CFG,
+                                    HostCmd_ACT_GEN_SET, 0, &coalesce_cfg);
+}
+
 /* station cfg80211 operations */
 static struct cfg80211_ops mwifiex_cfg80211_ops = {
        .add_virtual_intf = mwifiex_add_virtual_intf,
        .suspend = mwifiex_cfg80211_suspend,
        .resume = mwifiex_cfg80211_resume,
        .set_wakeup = mwifiex_cfg80211_set_wakeup,
+       .set_coalesce = mwifiex_cfg80211_set_coalesce,
 #endif
 };
 
        return false;
 }
 
+static const struct wiphy_coalesce_support mwifiex_coalesce_support = {
+       .n_rules = MWIFIEX_COALESCE_MAX_RULES,
+       .max_delay = MWIFIEX_MAX_COALESCING_DELAY,
+       .n_patterns = MWIFIEX_COALESCE_MAX_FILTERS,
+       .pattern_min_len = 1,
+       .pattern_max_len = MWIFIEX_MAX_PATTERN_LEN,
+       .max_pkt_offset = MWIFIEX_MAX_OFFSET_LEN,
+};
+
 /*
  * This function registers the device with CFG802.11 subsystem.
  *
        wiphy->wowlan = &mwifiex_wowlan_support;
 #endif
 
+       wiphy->coalesce = &mwifiex_coalesce_support;
+
        wiphy->probe_resp_offload = NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS |
                                    NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2 |
                                    NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P;
 
 #define TLV_TYPE_UAP_PS_AO_TIMER    (PROPRIETARY_TLV_BASE_ID + 123)
 #define TLV_TYPE_PWK_CIPHER         (PROPRIETARY_TLV_BASE_ID + 145)
 #define TLV_TYPE_GWK_CIPHER         (PROPRIETARY_TLV_BASE_ID + 146)
+#define TLV_TYPE_COALESCE_RULE      (PROPRIETARY_TLV_BASE_ID + 154)
 
 #define MWIFIEX_TX_DATA_BUF_SIZE_2K        2048
 
 #define HostCmd_CMD_CAU_REG_ACCESS                    0x00ed
 #define HostCmd_CMD_SET_BSS_MODE                      0x00f7
 #define HostCmd_CMD_PCIE_DESC_DETAILS                 0x00fa
+#define HostCmd_CMD_COALESCE_CFG                      0x010a
 #define HostCmd_CMD_MGMT_FRAME_REG                    0x010c
 #define HostCmd_CMD_REMAIN_ON_CHAN                    0x010d
 #define HostCmd_CMD_11AC_CFG                         0x0112
        __le16 data_len;
 } __packed;
 
+struct coalesce_filt_field_param {
+       u8 operation;
+       u8 operand_len;
+       __le16 offset;
+       u8 operand_byte_stream[4];
+};
+
+struct coalesce_receive_filt_rule {
+       struct mwifiex_ie_types_header header;
+       u8 num_of_fields;
+       u8 pkt_type;
+       __le16 max_coalescing_delay;
+       struct coalesce_filt_field_param params[0];
+} __packed;
+
+struct host_cmd_ds_coalesce_cfg {
+       __le16 action;
+       __le16 num_of_rules;
+       struct coalesce_receive_filt_rule rule[0];
+} __packed;
+
 struct host_cmd_ds_command {
        __le16 command;
        __le16 size;
                struct host_cmd_ds_sta_deauth sta_deauth;
                struct host_cmd_11ac_vht_cfg vht_cfg;
                struct host_cmd_ds_802_11_cfg_data cfg_data;
+               struct host_cmd_ds_coalesce_cfg coalesce_cfg;
        } params;
 } __packed;
 
 
        MWIFIEX_FUNC_SHUTDOWN,
 };
 
+enum COALESCE_OPERATION {
+       RECV_FILTER_MATCH_TYPE_EQ = 0x80,
+       RECV_FILTER_MATCH_TYPE_NE,
+};
+
+enum COALESCE_PACKET_TYPE {
+       PACKET_TYPE_UNICAST = 1,
+       PACKET_TYPE_MULTICAST = 2,
+       PACKET_TYPE_BROADCAST = 3
+};
+
+#define MWIFIEX_COALESCE_MAX_RULES     8
+#define MWIFIEX_COALESCE_MAX_BYTESEQ   4       /* non-adjustable */
+#define MWIFIEX_COALESCE_MAX_FILTERS   4
+#define MWIFIEX_MAX_COALESCING_DELAY   100     /* in msecs */
+
+struct filt_field_param {
+       u8 operation;
+       u8 operand_len;
+       u16 offset;
+       u8 operand_byte_stream[MWIFIEX_COALESCE_MAX_BYTESEQ];
+};
+
+struct mwifiex_coalesce_rule {
+       u16 max_coalescing_delay;
+       u8 num_of_fields;
+       u8 pkt_type;
+       struct filt_field_param params[MWIFIEX_COALESCE_MAX_FILTERS];
+};
+
+struct mwifiex_ds_coalesce_cfg {
+       u16 num_of_rules;
+       struct mwifiex_coalesce_rule rule[MWIFIEX_COALESCE_MAX_RULES];
+};
+
 #endif /* !_MWIFIEX_IOCTL_H_ */
 
        return 0;
 }
 
+static int
+mwifiex_cmd_coalesce_cfg(struct mwifiex_private *priv,
+                        struct host_cmd_ds_command *cmd,
+                        u16 cmd_action, void *data_buf)
+{
+       struct host_cmd_ds_coalesce_cfg *coalesce_cfg =
+                                               &cmd->params.coalesce_cfg;
+       struct mwifiex_ds_coalesce_cfg *cfg = data_buf;
+       struct coalesce_filt_field_param *param;
+       u16 cnt, idx, length;
+       struct coalesce_receive_filt_rule *rule;
+
+       cmd->command = cpu_to_le16(HostCmd_CMD_COALESCE_CFG);
+       cmd->size = cpu_to_le16(S_DS_GEN);
+
+       coalesce_cfg->action = cpu_to_le16(cmd_action);
+       coalesce_cfg->num_of_rules = cpu_to_le16(cfg->num_of_rules);
+       rule = coalesce_cfg->rule;
+
+       for (cnt = 0; cnt < cfg->num_of_rules; cnt++) {
+               rule->header.type = cpu_to_le16(TLV_TYPE_COALESCE_RULE);
+               rule->max_coalescing_delay =
+                       cpu_to_le16(cfg->rule[cnt].max_coalescing_delay);
+               rule->pkt_type = cfg->rule[cnt].pkt_type;
+               rule->num_of_fields = cfg->rule[cnt].num_of_fields;
+
+               length = 0;
+
+               param = rule->params;
+               for (idx = 0; idx < cfg->rule[cnt].num_of_fields; idx++) {
+                       param->operation = cfg->rule[cnt].params[idx].operation;
+                       param->operand_len =
+                                       cfg->rule[cnt].params[idx].operand_len;
+                       param->offset =
+                               cpu_to_le16(cfg->rule[cnt].params[idx].offset);
+                       memcpy(param->operand_byte_stream,
+                              cfg->rule[cnt].params[idx].operand_byte_stream,
+                              param->operand_len);
+
+                       length += sizeof(struct coalesce_filt_field_param);
+
+                       param++;
+               }
+
+               /* Total rule length is sizeof max_coalescing_delay(u16),
+                * num_of_fields(u8), pkt_type(u8) and total length of the all
+                * params
+                */
+               rule->header.len = cpu_to_le16(length + sizeof(u16) +
+                                              sizeof(u8) + sizeof(u8));
+
+               /* Add the rule length to the command size*/
+               le16_add_cpu(&cmd->size, le16_to_cpu(rule->header.len) +
+                            sizeof(struct mwifiex_ie_types_header));
+
+               rule = (void *)((u8 *)rule->params + length);
+       }
+
+       /* Add sizeof action, num_of_rules to total command length */
+       le16_add_cpu(&cmd->size, sizeof(u16) + sizeof(u16));
+
+       return 0;
+}
+
 /*
  * This function prepares the commands before sending them to the firmware.
  *
        case HostCmd_CMD_MEF_CFG:
                ret = mwifiex_cmd_mef_cfg(priv, cmd_ptr, data_buf);
                break;
+       case HostCmd_CMD_COALESCE_CFG:
+               ret = mwifiex_cmd_coalesce_cfg(priv, cmd_ptr, cmd_action,
+                                              data_buf);
+               break;
        default:
                dev_err(priv->adapter->dev,
                        "PREP_CMD: unknown cmd- %#x\n", cmd_no);