#define RTW89_GET_MAC_C2H_SCANOFLD_BAND(c2h) \
        le32_get_bits(*((const __le32 *)(c2h) + 5), GENMASK(25, 24))
 
+#define RTW89_GET_MAC_C2H_MCC_RCV_ACK_GROUP(c2h) \
+       le32_get_bits(*((const __le32 *)(c2h)), GENMASK(1, 0))
+#define RTW89_GET_MAC_C2H_MCC_RCV_ACK_H2C_FUNC(c2h) \
+       le32_get_bits(*((const __le32 *)(c2h)), GENMASK(15, 8))
+
+#define RTW89_GET_MAC_C2H_MCC_REQ_ACK_GROUP(c2h) \
+       le32_get_bits(*((const __le32 *)(c2h)), GENMASK(1, 0))
+#define RTW89_GET_MAC_C2H_MCC_REQ_ACK_H2C_RETURN(c2h) \
+       le32_get_bits(*((const __le32 *)(c2h)), GENMASK(7, 2))
+#define RTW89_GET_MAC_C2H_MCC_REQ_ACK_H2C_FUNC(c2h) \
+       le32_get_bits(*((const __le32 *)(c2h)), GENMASK(15, 8))
+
+struct rtw89_mac_mcc_tsf_rpt {
+       u32 macid_x;
+       u32 macid_y;
+       u32 tsf_x_low;
+       u32 tsf_x_high;
+       u32 tsf_y_low;
+       u32 tsf_y_high;
+};
+
+static_assert(sizeof(struct rtw89_mac_mcc_tsf_rpt) <= RTW89_COMPLETION_BUF_SIZE);
+
+#define RTW89_GET_MAC_C2H_MCC_TSF_RPT_MACID_X(c2h) \
+       le32_get_bits(*((const __le32 *)(c2h)), GENMASK(7, 0))
+#define RTW89_GET_MAC_C2H_MCC_TSF_RPT_MACID_Y(c2h) \
+       le32_get_bits(*((const __le32 *)(c2h)), GENMASK(15, 8))
+#define RTW89_GET_MAC_C2H_MCC_TSF_RPT_GROUP(c2h) \
+       le32_get_bits(*((const __le32 *)(c2h)), GENMASK(17, 16))
+#define RTW89_GET_MAC_C2H_MCC_TSF_RPT_TSF_LOW_X(c2h) \
+       le32_get_bits(*((const __le32 *)(c2h) + 1), GENMASK(31, 0))
+#define RTW89_GET_MAC_C2H_MCC_TSF_RPT_TSF_HIGH_X(c2h) \
+       le32_get_bits(*((const __le32 *)(c2h) + 2), GENMASK(31, 0))
+#define RTW89_GET_MAC_C2H_MCC_TSF_RPT_TSF_LOW_Y(c2h) \
+       le32_get_bits(*((const __le32 *)(c2h) + 3), GENMASK(31, 0))
+#define RTW89_GET_MAC_C2H_MCC_TSF_RPT_TSF_HIGH_Y(c2h) \
+       le32_get_bits(*((const __le32 *)(c2h) + 4), GENMASK(31, 0))
+
+#define RTW89_GET_MAC_C2H_MCC_STATUS_RPT_STATUS(c2h) \
+       le32_get_bits(*((const __le32 *)(c2h)), GENMASK(5, 0))
+#define RTW89_GET_MAC_C2H_MCC_STATUS_RPT_GROUP(c2h) \
+       le32_get_bits(*((const __le32 *)(c2h)), GENMASK(7, 6))
+#define RTW89_GET_MAC_C2H_MCC_STATUS_RPT_MACID(c2h) \
+       le32_get_bits(*((const __le32 *)(c2h)), GENMASK(15, 8))
+#define RTW89_GET_MAC_C2H_MCC_STATUS_RPT_TSF_LOW(c2h) \
+       le32_get_bits(*((const __le32 *)(c2h) + 1), GENMASK(31, 0))
+#define RTW89_GET_MAC_C2H_MCC_STATUS_RPT_TSF_HIGH(c2h) \
+       le32_get_bits(*((const __le32 *)(c2h) + 2), GENMASK(31, 0))
+
 #define RTW89_FW_HDR_SIZE 32
 #define RTW89_FW_SECTION_HDR_SIZE 16
 
 #define H2C_CL_BA_CAM                  0xc
 #define H2C_FUNC_MAC_BA_CAM            0x0
 
+/* CLASS 14 - MCC */
+#define H2C_CL_MCC                     0xe
+enum rtw89_mcc_h2c_func {
+       H2C_FUNC_ADD_MCC                = 0x0,
+       H2C_FUNC_START_MCC              = 0x1,
+       H2C_FUNC_STOP_MCC               = 0x2,
+       H2C_FUNC_DEL_MCC_GROUP          = 0x3,
+       H2C_FUNC_RESET_MCC_GROUP        = 0x4,
+       H2C_FUNC_MCC_REQ_TSF            = 0x5,
+       H2C_FUNC_MCC_MACID_BITMAP       = 0x6,
+       H2C_FUNC_MCC_SYNC               = 0x7,
+       H2C_FUNC_MCC_SET_DURATION       = 0x8,
+
+       NUM_OF_RTW89_MCC_H2C_FUNC,
+};
+
+#define RTW89_MCC_WAIT_COND(group, func) \
+       ((group) * NUM_OF_RTW89_MCC_H2C_FUNC + (func))
+
 #define H2C_CAT_OUTSRC                 0x2
 
 #define H2C_CL_OUTSRC_RA               0x1
 
 {
 }
 
+static void
+rtw89_mac_c2h_mcc_rcv_ack(struct rtw89_dev *rtwdev, struct sk_buff *c2h, u32 len)
+{
+       u8 group = RTW89_GET_MAC_C2H_MCC_RCV_ACK_GROUP(c2h->data);
+       u8 func = RTW89_GET_MAC_C2H_MCC_RCV_ACK_H2C_FUNC(c2h->data);
+
+       switch (func) {
+       case H2C_FUNC_ADD_MCC:
+       case H2C_FUNC_START_MCC:
+       case H2C_FUNC_STOP_MCC:
+       case H2C_FUNC_DEL_MCC_GROUP:
+       case H2C_FUNC_RESET_MCC_GROUP:
+       case H2C_FUNC_MCC_REQ_TSF:
+       case H2C_FUNC_MCC_MACID_BITMAP:
+       case H2C_FUNC_MCC_SYNC:
+       case H2C_FUNC_MCC_SET_DURATION:
+               break;
+       default:
+               rtw89_debug(rtwdev, RTW89_DBG_FW,
+                           "invalid MCC C2H RCV ACK: func %d\n", func);
+               return;
+       }
+
+       rtw89_debug(rtwdev, RTW89_DBG_FW,
+                   "MCC C2H RCV ACK: group %d, func %d\n", group, func);
+}
+
+static void
+rtw89_mac_c2h_mcc_req_ack(struct rtw89_dev *rtwdev, struct sk_buff *c2h, u32 len)
+{
+       u8 group = RTW89_GET_MAC_C2H_MCC_REQ_ACK_GROUP(c2h->data);
+       u8 func = RTW89_GET_MAC_C2H_MCC_REQ_ACK_H2C_FUNC(c2h->data);
+       u8 retcode = RTW89_GET_MAC_C2H_MCC_REQ_ACK_H2C_RETURN(c2h->data);
+       struct rtw89_completion_data data = {};
+       unsigned int cond;
+       bool next = false;
+
+       switch (func) {
+       case H2C_FUNC_MCC_REQ_TSF:
+               next = true;
+               break;
+       case H2C_FUNC_MCC_MACID_BITMAP:
+       case H2C_FUNC_MCC_SYNC:
+       case H2C_FUNC_MCC_SET_DURATION:
+               break;
+       case H2C_FUNC_ADD_MCC:
+       case H2C_FUNC_START_MCC:
+       case H2C_FUNC_STOP_MCC:
+       case H2C_FUNC_DEL_MCC_GROUP:
+       case H2C_FUNC_RESET_MCC_GROUP:
+       default:
+               rtw89_debug(rtwdev, RTW89_DBG_FW,
+                           "invalid MCC C2H REQ ACK: func %d\n", func);
+               return;
+       }
+
+       rtw89_debug(rtwdev, RTW89_DBG_FW,
+                   "MCC C2H REQ ACK: group %d, func %d, return code %d\n",
+                   group, func, retcode);
+
+       if (!retcode && next)
+               return;
+
+       data.err = !!retcode;
+       cond = RTW89_MCC_WAIT_COND(group, func);
+       rtw89_complete_cond(&rtwdev->mcc.wait, cond, &data);
+}
+
+static void
+rtw89_mac_c2h_mcc_tsf_rpt(struct rtw89_dev *rtwdev, struct sk_buff *c2h, u32 len)
+{
+       u8 group = RTW89_GET_MAC_C2H_MCC_TSF_RPT_GROUP(c2h->data);
+       struct rtw89_completion_data data = {};
+       struct rtw89_mac_mcc_tsf_rpt *rpt;
+       unsigned int cond;
+
+       rpt = (struct rtw89_mac_mcc_tsf_rpt *)data.buf;
+       rpt->macid_x = RTW89_GET_MAC_C2H_MCC_TSF_RPT_MACID_X(c2h->data);
+       rpt->macid_y = RTW89_GET_MAC_C2H_MCC_TSF_RPT_MACID_Y(c2h->data);
+       rpt->tsf_x_low = RTW89_GET_MAC_C2H_MCC_TSF_RPT_TSF_LOW_X(c2h->data);
+       rpt->tsf_x_high = RTW89_GET_MAC_C2H_MCC_TSF_RPT_TSF_HIGH_X(c2h->data);
+       rpt->tsf_y_low = RTW89_GET_MAC_C2H_MCC_TSF_RPT_TSF_LOW_Y(c2h->data);
+       rpt->tsf_y_high = RTW89_GET_MAC_C2H_MCC_TSF_RPT_TSF_HIGH_Y(c2h->data);
+
+       cond = RTW89_MCC_WAIT_COND(group, H2C_FUNC_MCC_REQ_TSF);
+       rtw89_complete_cond(&rtwdev->mcc.wait, cond, &data);
+}
+
+static void
+rtw89_mac_c2h_mcc_status_rpt(struct rtw89_dev *rtwdev, struct sk_buff *c2h, u32 len)
+{
+       u8 group = RTW89_GET_MAC_C2H_MCC_STATUS_RPT_GROUP(c2h->data);
+       u8 macid = RTW89_GET_MAC_C2H_MCC_STATUS_RPT_MACID(c2h->data);
+       u8 status = RTW89_GET_MAC_C2H_MCC_STATUS_RPT_STATUS(c2h->data);
+       u32 tsf_low = RTW89_GET_MAC_C2H_MCC_STATUS_RPT_TSF_LOW(c2h->data);
+       u32 tsf_high = RTW89_GET_MAC_C2H_MCC_STATUS_RPT_TSF_HIGH(c2h->data);
+       struct rtw89_completion_data data = {};
+       unsigned int cond;
+       bool rsp = true;
+       bool err;
+       u8 func;
+
+       switch (status) {
+       case RTW89_MAC_MCC_ADD_ROLE_OK:
+       case RTW89_MAC_MCC_ADD_ROLE_FAIL:
+               func = H2C_FUNC_ADD_MCC;
+               err = status == RTW89_MAC_MCC_ADD_ROLE_FAIL;
+               break;
+       case RTW89_MAC_MCC_START_GROUP_OK:
+       case RTW89_MAC_MCC_START_GROUP_FAIL:
+               func = H2C_FUNC_START_MCC;
+               err = status == RTW89_MAC_MCC_START_GROUP_FAIL;
+               break;
+       case RTW89_MAC_MCC_STOP_GROUP_OK:
+       case RTW89_MAC_MCC_STOP_GROUP_FAIL:
+               func = H2C_FUNC_STOP_MCC;
+               err = status == RTW89_MAC_MCC_STOP_GROUP_FAIL;
+               break;
+       case RTW89_MAC_MCC_DEL_GROUP_OK:
+       case RTW89_MAC_MCC_DEL_GROUP_FAIL:
+               func = H2C_FUNC_DEL_MCC_GROUP;
+               err = status == RTW89_MAC_MCC_DEL_GROUP_FAIL;
+               break;
+       case RTW89_MAC_MCC_RESET_GROUP_OK:
+       case RTW89_MAC_MCC_RESET_GROUP_FAIL:
+               func = H2C_FUNC_RESET_MCC_GROUP;
+               err = status == RTW89_MAC_MCC_RESET_GROUP_FAIL;
+               break;
+       case RTW89_MAC_MCC_SWITCH_CH_OK:
+       case RTW89_MAC_MCC_SWITCH_CH_FAIL:
+       case RTW89_MAC_MCC_TXNULL0_OK:
+       case RTW89_MAC_MCC_TXNULL0_FAIL:
+       case RTW89_MAC_MCC_TXNULL1_OK:
+       case RTW89_MAC_MCC_TXNULL1_FAIL:
+       case RTW89_MAC_MCC_SWITCH_EARLY:
+       case RTW89_MAC_MCC_TBTT:
+       case RTW89_MAC_MCC_DURATION_START:
+       case RTW89_MAC_MCC_DURATION_END:
+               rsp = false;
+               break;
+       default:
+               rtw89_debug(rtwdev, RTW89_DBG_FW,
+                           "invalid MCC C2H STS RPT: status %d\n", status);
+               return;
+       }
+
+       rtw89_debug(rtwdev, RTW89_DBG_FW,
+                   "MCC C2H STS RPT: group %d, macid %d, status %d, tsf {%d, %d}\n",
+                    group, macid, status, tsf_low, tsf_high);
+
+       if (!rsp)
+               return;
+
+       data.err = err;
+       cond = RTW89_MCC_WAIT_COND(group, func);
+       rtw89_complete_cond(&rtwdev->mcc.wait, cond, &data);
+}
+
 static
 void (* const rtw89_mac_c2h_ofld_handler[])(struct rtw89_dev *rtwdev,
                                            struct sk_buff *c2h, u32 len) = {
        [RTW89_MAC_C2H_FUNC_BCN_CNT] = rtw89_mac_c2h_bcn_cnt,
 };
 
+static
+void (* const rtw89_mac_c2h_mcc_handler[])(struct rtw89_dev *rtwdev,
+                                          struct sk_buff *c2h, u32 len) = {
+       [RTW89_MAC_C2H_FUNC_MCC_RCV_ACK] = rtw89_mac_c2h_mcc_rcv_ack,
+       [RTW89_MAC_C2H_FUNC_MCC_REQ_ACK] = rtw89_mac_c2h_mcc_req_ack,
+       [RTW89_MAC_C2H_FUNC_MCC_TSF_RPT] = rtw89_mac_c2h_mcc_tsf_rpt,
+       [RTW89_MAC_C2H_FUNC_MCC_STATUS_RPT] = rtw89_mac_c2h_mcc_status_rpt,
+};
+
 bool rtw89_mac_c2h_chk_atomic(struct rtw89_dev *rtwdev, u8 class, u8 func)
 {
        switch (class) {
                if (func < RTW89_MAC_C2H_FUNC_OFLD_MAX)
                        handler = rtw89_mac_c2h_ofld_handler[func];
                break;
+       case RTW89_MAC_C2H_CLASS_MCC:
+               if (func < NUM_OF_RTW89_MAC_C2H_FUNC_MCC)
+                       handler = rtw89_mac_c2h_mcc_handler[func];
+               break;
        case RTW89_MAC_C2H_CLASS_FWDBG:
                return;
        default:
 
        RTW89_MAC_C2H_FUNC_INFO_MAX,
 };
 
+enum rtw89_mac_c2h_mcc_func {
+       RTW89_MAC_C2H_FUNC_MCC_RCV_ACK = 0,
+       RTW89_MAC_C2H_FUNC_MCC_REQ_ACK = 1,
+       RTW89_MAC_C2H_FUNC_MCC_TSF_RPT = 2,
+       RTW89_MAC_C2H_FUNC_MCC_STATUS_RPT = 3,
+
+       NUM_OF_RTW89_MAC_C2H_FUNC_MCC,
+};
+
 enum rtw89_mac_c2h_class {
        RTW89_MAC_C2H_CLASS_INFO,
        RTW89_MAC_C2H_CLASS_OFLD,
        RTW89_MAC_C2H_CLASS_MAX,
 };
 
+enum rtw89_mac_mcc_status {
+       RTW89_MAC_MCC_ADD_ROLE_OK = 0,
+       RTW89_MAC_MCC_START_GROUP_OK = 1,
+       RTW89_MAC_MCC_STOP_GROUP_OK = 2,
+       RTW89_MAC_MCC_DEL_GROUP_OK = 3,
+       RTW89_MAC_MCC_RESET_GROUP_OK = 4,
+       RTW89_MAC_MCC_SWITCH_CH_OK = 5,
+       RTW89_MAC_MCC_TXNULL0_OK = 6,
+       RTW89_MAC_MCC_TXNULL1_OK = 7,
+
+       RTW89_MAC_MCC_SWITCH_EARLY = 10,
+       RTW89_MAC_MCC_TBTT = 11,
+       RTW89_MAC_MCC_DURATION_START = 12,
+       RTW89_MAC_MCC_DURATION_END = 13,
+
+       RTW89_MAC_MCC_ADD_ROLE_FAIL = 20,
+       RTW89_MAC_MCC_START_GROUP_FAIL = 21,
+       RTW89_MAC_MCC_STOP_GROUP_FAIL = 22,
+       RTW89_MAC_MCC_DEL_GROUP_FAIL = 23,
+       RTW89_MAC_MCC_RESET_GROUP_FAIL = 24,
+       RTW89_MAC_MCC_SWITCH_CH_FAIL = 25,
+       RTW89_MAC_MCC_TXNULL0_FAIL = 26,
+       RTW89_MAC_MCC_TXNULL1_FAIL = 27,
+};
+
 struct rtw89_mac_ax_coex {
 #define RTW89_MAC_AX_COEX_RTK_MODE 0
 #define RTW89_MAC_AX_COEX_CSR_MODE 1