/****************************************************************************** * * Copyright(c) 2019 Realtek Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * ******************************************************************************/ #include "dbcc.h" #include "cpuio.h" u32 dbcc_info_init(struct mac_ax_adapter *adapter) { u32 ret = MACSUCCESS; adapter->dbcc_info = (struct mac_ax_dbcc_info *)PLTFM_MALLOC(DBCC_INFO_SIZE); if (!adapter->dbcc_info) { PLTFM_MSG_ERR("dbcc info malloc fail\n"); return MACNPTR; } adapter->dbcc_info->dbcc_wmm_list = (u8 *)PLTFM_MALLOC(DBCC_WMM_LIST_SIZE); if (!adapter->dbcc_info->dbcc_wmm_list) { PLTFM_MSG_ERR("dbcc wmm list malloc fail\n"); return MACNPTR; } ret = rst_dbcc_info(adapter); return ret; } u32 dbcc_info_exit(struct mac_ax_adapter *adapter) { PLTFM_FREE(adapter->dbcc_info->dbcc_wmm_list, DBCC_WMM_LIST_SIZE); PLTFM_FREE(adapter->dbcc_info, DBCC_INFO_SIZE); return MACSUCCESS; } u32 rst_dbcc_info(struct mac_ax_adapter *adapter) { struct mac_ax_dbcc_info *dbcc_info = adapter->dbcc_info; u32 i; if (!dbcc_info) { PLTFM_MSG_ERR("no dbcc info when rst dbcc info\n"); return MACNPTR; } PLTFM_MEMSET(dbcc_info->dbcc_wmm_list, MAC_AX_DBCC_WMM_INVALID, DBCC_WMM_LIST_SIZE); for (i = 0; i < MAC_AX_DBCC_WMM_MAX; i++) { dbcc_info->dbcc_role_cnt[i] = 0; dbcc_info->dbcc_wmm_type[i] = MAC_AX_NET_TYPE_INVLAID; dbcc_info->dbcc_wmm_bp[i] = DBCC_BP_INVALID; } for (i = MAC_AX_BAND_0; i < MAC_AX_BAND_NUM; i++) { dbcc_info->bkp_flag[i] = 0; PLTFM_MEMSET(&dbcc_info->ppdu_rpt_bkp, 0, sizeof(struct mac_ax_phy_rpt_cfg)); PLTFM_MEMSET(&dbcc_info->chinfo_bkp, 0, sizeof(struct mac_ax_phy_rpt_cfg)); } return MACSUCCESS; } static u32 get_valid_dbcc_wmm(struct mac_ax_adapter *adapter, u8 *out_dbcc_wmm) { struct mac_ax_dbcc_info *dbcc_info = adapter->dbcc_info; u8 wmmidx; if (!dbcc_info) { PLTFM_MSG_ERR("no dbcc info when get valid dbcc wmm\n"); *out_dbcc_wmm = MAC_AX_DBCC_WMM_INVALID; return MACNPTR; } for (wmmidx = MAC_AX_DBCC_WMM0; wmmidx < MAC_AX_DBCC_WMM_MAX; wmmidx++) { if (!dbcc_info->dbcc_role_cnt[wmmidx]) break; } if (wmmidx == MAC_AX_DBCC_WMM_MAX) { PLTFM_MSG_ERR("no valid dbcc wmm\n"); *out_dbcc_wmm = MAC_AX_DBCC_WMM_INVALID; return MACNOITEM; } *out_dbcc_wmm = wmmidx; return MACSUCCESS; } u32 dbcc_wmm_add_macid(struct mac_ax_adapter *adapter, struct mac_ax_role_info *info) { struct mac_ax_dbcc_info *dbcc_info = adapter->dbcc_info; enum mac_ax_ss_wmm_tbl dst_link; u8 *cur_dbcc_wmm_type; u32 ret; u8 chk_emp, tar_dbcc_wmm, wmmidx, curr_bp; if (!dbcc_info) { PLTFM_MSG_ERR("no dbcc info when add macid\n"); return MACNPTR; } curr_bp = (info->port & DBCC_PORT_MASK) | (info->band == MAC_AX_BAND_1 ? DBCC_BAND_BIT : 0); tar_dbcc_wmm = MAC_AX_DBCC_WMM_INVALID; if (info->net_type == MAC_AX_NET_TYPE_AP) { for (wmmidx = MAC_AX_DBCC_WMM0; wmmidx < MAC_AX_DBCC_WMM_MAX; wmmidx++) { if (dbcc_info->dbcc_wmm_bp[wmmidx] == curr_bp && dbcc_info->dbcc_wmm_type[wmmidx] == info->net_type) break; } if (wmmidx < MAC_AX_DBCC_WMM_MAX) tar_dbcc_wmm = wmmidx; } if (tar_dbcc_wmm == MAC_AX_DBCC_WMM_INVALID) { ret = get_valid_dbcc_wmm(adapter, &tar_dbcc_wmm); if (ret != MACSUCCESS) { PLTFM_MSG_ERR("get_valid_dbcc_wmm %d\n", ret); return ret; } } cur_dbcc_wmm_type = &dbcc_info->dbcc_wmm_type[tar_dbcc_wmm]; if ((info->net_type != MAC_AX_NET_TYPE_AP && *cur_dbcc_wmm_type != MAC_AX_NET_TYPE_INVLAID) || (info->net_type == MAC_AX_NET_TYPE_AP && *cur_dbcc_wmm_type != MAC_AX_NET_TYPE_INVLAID && *cur_dbcc_wmm_type != MAC_AX_NET_TYPE_AP)) { PLTFM_MSG_ERR("dbcc wmm%d is occupied by type%d\n", tar_dbcc_wmm, *cur_dbcc_wmm_type); return MACPROCERR; } if (info->macid >= DBCC_WMM_LIST_SIZE) { PLTFM_MSG_ERR("macid%d not support in dbcc wmm add macid\n", info->macid); return MACFUNCINPUT; } if (info->band == MAC_AX_BAND_0) dst_link = info->wmm ? MAC_AX_SS_WMM_TBL_C0_WMM1 : MAC_AX_SS_WMM_TBL_C0_WMM0; else dst_link = info->wmm ? MAC_AX_SS_WMM_TBL_C1_WMM1 : MAC_AX_SS_WMM_TBL_C1_WMM0; chk_emp = (!dbcc_info->dbcc_role_cnt[tar_dbcc_wmm] || *cur_dbcc_wmm_type != MAC_AX_NET_TYPE_AP) ? 1 : 0; ret = mac_ss_wmm_map_upd(adapter, (enum mac_ax_ss_wmm)tar_dbcc_wmm, dst_link, chk_emp); if (ret != MACSUCCESS) { PLTFM_MSG_ERR("mac_ss_wmm_map_upd %d/%d/%d/%d\n", tar_dbcc_wmm, dst_link, chk_emp, ret); return ret; } *(dbcc_info->dbcc_wmm_list + info->macid) = tar_dbcc_wmm; dbcc_info->dbcc_role_cnt[tar_dbcc_wmm]++; *cur_dbcc_wmm_type = info->net_type; dbcc_info->dbcc_wmm_bp[tar_dbcc_wmm] = curr_bp; return MACSUCCESS; } u32 dbcc_wmm_rm_macid(struct mac_ax_adapter *adapter, struct mac_ax_role_info *info) { struct mac_ax_dbcc_info *dbcc_info = adapter->dbcc_info; u8 dbcc_wmm; if (!dbcc_info) { PLTFM_MSG_ERR("no dbcc info when rm macid\n"); return MACNPTR; } if (info->macid >= DBCC_WMM_LIST_SIZE) { PLTFM_MSG_ERR("macid%d not support in dbcc wmm rm macid\n", info->macid); return MACFUNCINPUT; } dbcc_wmm = *(dbcc_info->dbcc_wmm_list + info->macid); if (dbcc_wmm == MAC_AX_DBCC_WMM_INVALID) { PLTFM_MSG_ERR("macid%d dbcc wmm invalid in dbcc wmm list\n", info->macid); return MACPROCERR; } if (!dbcc_info->dbcc_role_cnt[dbcc_wmm]) { PLTFM_MSG_ERR("dbcc wmm%d rm macid%d underflow\n", dbcc_wmm, info->macid); if (dbcc_info->dbcc_wmm_type[dbcc_wmm] != MAC_AX_NET_TYPE_INVLAID) { PLTFM_MSG_ERR("dbcc wmm not invalid when no role\n"); return MACPROCERR; } return MACCMP; } *(dbcc_info->dbcc_wmm_list + info->macid) = MAC_AX_DBCC_WMM_INVALID; dbcc_info->dbcc_role_cnt[dbcc_wmm]--; if (!dbcc_info->dbcc_role_cnt[dbcc_wmm]) { dbcc_info->dbcc_wmm_type[dbcc_wmm] = MAC_AX_NET_TYPE_INVLAID; dbcc_info->dbcc_wmm_bp[dbcc_wmm] = DBCC_BP_INVALID; } return MACSUCCESS; } static u32 band1_enable(struct mac_ax_adapter *adapter, struct mac_ax_trx_info *info) { struct mac_ax_intf_ops *ops = adapter_to_intf_ops(adapter); u32 ret; ret = tx_idle_poll_band(adapter, 0, 0); if (ret != MACSUCCESS) { PLTFM_MSG_ERR("[ERR]tx idle poll %d\n", ret); return ret; } ret = dle_quota_change(adapter, info->qta_mode); if (ret != MACSUCCESS) { PLTFM_MSG_ERR("[ERR]DLE quota change %d\n", ret); return ret; } ret = cmac_func_en(adapter, MAC_AX_BAND_1, MAC_AX_FUNC_EN); if (ret != MACSUCCESS) { PLTFM_MSG_ERR("[ERR]CMAC%d func en %d\n", MAC_AX_BAND_1, ret); return ret; } ret = cmac_init(adapter, info, MAC_AX_BAND_1); if (ret != MACSUCCESS) { PLTFM_MSG_ERR("[ERR]CMAC%d init %d\n", MAC_AX_BAND_1, ret); return ret; } MAC_REG_W32(R_AX_SYS_ISO_CTRL_EXTEND, MAC_REG_R32(R_AX_SYS_ISO_CTRL_EXTEND) | B_AX_R_SYM_FEN_WLBBGLB_1 | B_AX_R_SYM_FEN_WLBBFUN_1); adapter->sm.bb1_func = MAC_AX_FUNC_ON; ret = mac_enable_imr(adapter, MAC_AX_BAND_1, MAC_AX_CMAC_SEL); if (ret != MACSUCCESS) { PLTFM_MSG_ERR("[ERR] enable CMAC1 IMR %d\n", ret); return ret; } return MACSUCCESS; } static u32 band1_disable(struct mac_ax_adapter *adapter, struct mac_ax_trx_info *info) { u32 ret; struct mac_ax_intf_ops *ops = adapter_to_intf_ops(adapter); MAC_REG_W32(R_AX_SYS_ISO_CTRL_EXTEND, MAC_REG_R32(R_AX_SYS_ISO_CTRL_EXTEND) & ~B_AX_R_SYM_FEN_WLBBFUN_1); ret = cmac_func_en(adapter, MAC_AX_BAND_1, MAC_AX_FUNC_DIS); if (ret != MACSUCCESS) { PLTFM_MSG_ERR("[ERR]CMAC%d func dis %d\n", MAC_AX_BAND_1, ret); return ret; } ret = dle_quota_change(adapter, info->qta_mode); if (ret != MACSUCCESS) { PLTFM_MSG_ERR("[ERR]DLE quota change %d\n", ret); return ret; } ret = rst_port_info(adapter, MAC_AX_BAND_1); if (ret != MACSUCCESS) { PLTFM_MSG_ERR("[ERR]Reset band 1 port info %d\n", ret); return ret; } return 0; } u32 dbcc_trx_ctrl_bkp(struct mac_ax_adapter *adapter, enum mac_ax_band band) { struct mac_ax_ops *mops = adapter_to_mac_ops(adapter); struct mac_ax_dbcc_info *dbcc_info = adapter->dbcc_info; struct mac_ax_phy_rpt_cfg *ppdu_rpt_cfg; struct mac_ax_phy_rpt_cfg *chinfo_cfg; u32 ret; if (!dbcc_info) { PLTFM_MSG_ERR("no dbcc info when bkp trx\n"); return MACNPTR; } if (dbcc_info->bkp_flag[band]) { PLTFM_MSG_ERR("band%d trx ctrl already backup\n", band); return MACPROCERR; } ppdu_rpt_cfg = &dbcc_info->ppdu_rpt_bkp[band]; ppdu_rpt_cfg->type = MAC_AX_PPDU_STATUS; ppdu_rpt_cfg->u.ppdu.band = band; ret = mops->get_phy_rpt_cfg(adapter, ppdu_rpt_cfg); if (ret != MACSUCCESS) { PLTFM_MSG_ERR("get ppdu stus rpt cfg %d\n", ret); return ret; } chinfo_cfg = &dbcc_info->chinfo_bkp[band]; chinfo_cfg->type = MAC_AX_CH_INFO; ret = mops->get_phy_rpt_cfg(adapter, chinfo_cfg); if (ret != MACSUCCESS) { PLTFM_MSG_ERR("get ch info cfg %d\n", ret); return ret; } dbcc_info->bkp_flag[band] = 1; return ret; } u32 mac_dbcc_trx_ctrl(struct mac_ax_adapter *adapter, enum mac_ax_band band, u8 pause) { struct mac_ax_intf_ops *ops = adapter_to_intf_ops(adapter); struct mac_ax_ops *mops = adapter_to_mac_ops(adapter); struct mac_ax_dbcc_info *dbcc_info = adapter->dbcc_info; struct mac_ax_sch_tx_en_cfg sch_cfg; struct mac_ax_phy_rpt_cfg phyrpt_cfg; struct mac_ax_phy_rpt_cfg *ppdu_rpt_bkp; struct mac_ax_phy_rpt_cfg *chinfo_cfg_kbp; u32 ret, reg; u16 val16; if (!dbcc_info) { PLTFM_MSG_ERR("no dbcc info when trx ctrl\n"); return MACNPTR; } if (pause) { ret = dbcc_trx_ctrl_bkp(adapter, band); if (ret != MACSUCCESS) { PLTFM_MSG_ERR("B%d dbcc trx bkp %d\n", band, ret); return ret; } } else if (!dbcc_info->bkp_flag[band]) { PLTFM_MSG_ERR("B%d dbcc trx is not backup\n", band); return MACPROCERR; } if (!pause && band == MAC_AX_BAND_1 && check_mac_en(adapter, MAC_AX_BAND_1, MAC_AX_CMAC_SEL) != MACSUCCESS) { dbcc_info->bkp_flag[band] = 0; return MACSUCCESS; } ppdu_rpt_bkp = &dbcc_info->ppdu_rpt_bkp[band]; chinfo_cfg_kbp = &dbcc_info->chinfo_bkp[band]; sch_cfg.band = (u8)band; if (pause) PLTFM_MEMSET(&sch_cfg.tx_en, 0, SCH_TX_EN_SIZE); else PLTFM_MEMSET(&sch_cfg.tx_en, 0xFF, SCH_TX_EN_SIZE); PLTFM_MEMSET(&sch_cfg.tx_en_mask, 0xFF, SCH_TX_EN_SIZE); ret = mops->set_hw_value(adapter, MAC_AX_HW_SET_SCH_TXEN_CFG, (void *)&sch_cfg); if (ret != MACSUCCESS) { PLTFM_MSG_ERR("B%d pause%d sch txen cfg %d\n", band, pause, ret); return ret; } reg = band == MAC_AX_BAND_0 ? R_AX_RCR : R_AX_RCR_C1; val16 = MAC_REG_R16(reg); if (pause) MAC_REG_W16(reg, val16 | B_AX_STOP_RX_IN); else MAC_REG_W16(reg, val16 & ~B_AX_STOP_RX_IN); phyrpt_cfg.type = MAC_AX_PPDU_STATUS; phyrpt_cfg.en = pause ? 0 : ppdu_rpt_bkp->en; phyrpt_cfg.dest = ppdu_rpt_bkp->dest; phyrpt_cfg.u.ppdu = ppdu_rpt_bkp->u.ppdu; ret = mops->cfg_phy_rpt(adapter, &phyrpt_cfg); if (ret != MACSUCCESS) { PLTFM_MSG_ERR("B%d pause%d ppdu status cfg %d\n", band, pause, ret); return ret; } phyrpt_cfg.type = MAC_AX_CH_INFO; phyrpt_cfg.en = pause ? 0 : chinfo_cfg_kbp->en; phyrpt_cfg.dest = chinfo_cfg_kbp->dest; phyrpt_cfg.u.chif = chinfo_cfg_kbp->u.chif; ret = mops->cfg_phy_rpt(adapter, &phyrpt_cfg); if (ret != MACSUCCESS) { PLTFM_MSG_ERR("B%d pause%d ch info cfg %d\n", band, pause, ret); return ret; } if (!pause) dbcc_info->bkp_flag[band] = 0; return ret; } u32 mac_dbcc_enable(struct mac_ax_adapter *adapter, struct mac_ax_trx_info *info, u8 dbcc_en) { u32 ret; if (dbcc_en) { ret = band1_enable(adapter, info); if (ret != MACSUCCESS) { PLTFM_MSG_ERR("[ERR] band1_enable %d\n", ret); return ret; } ret = mac_notify_fw_dbcc(adapter, 1); if (ret != MACSUCCESS) { PLTFM_MSG_ERR("%s: [ERR] notfify dbcc fail %d\n", __func__, ret); return ret; } } else { ret = mac_notify_fw_dbcc(adapter, 0); if (ret != MACSUCCESS) { PLTFM_MSG_ERR("%s: [ERR] notfify dbcc fail %d\n", __func__, ret); return ret; } ret = band1_disable(adapter, info); if (ret != MACSUCCESS) { PLTFM_MSG_ERR("[ERR] band1_disable %d\n", ret); return ret; } } return MACSUCCESS; } u32 mac_dbcc_move_wmm(struct mac_ax_adapter *adapter, struct mac_ax_role_info *info) { struct mac_ax_dbcc_info *dbcc_info = adapter->dbcc_info; struct deq_enq_info q_info; enum mac_ax_ss_wmm_tbl dst_link; u8 dbcc_wmm; u8 role_cnt; u8 wmm_type; u8 dbcc_en = info->band; u32 ret; if (!dbcc_info) { PLTFM_MSG_ERR("no dbcc info when move wmm\n"); return MACNPTR; } dbcc_wmm = *(dbcc_info->dbcc_wmm_list + info->macid); role_cnt = dbcc_info->dbcc_role_cnt[dbcc_wmm]; wmm_type = dbcc_info->dbcc_wmm_type[dbcc_wmm]; if (!role_cnt) { PLTFM_MSG_ERR("dbcc en%d move wmm%d role cnt 0\n", dbcc_en, dbcc_wmm); return MACCMP; } dst_link = dbcc_en ? MAC_AX_SS_WMM_TBL_C1_WMM0 : MAC_AX_SS_WMM_TBL_C0_WMM0; if (dbcc_en == MAC_AX_BAND_0) dst_link = info->wmm ? MAC_AX_SS_WMM_TBL_C0_WMM1 : MAC_AX_SS_WMM_TBL_C0_WMM0; else dst_link = info->wmm ? MAC_AX_SS_WMM_TBL_C1_WMM1 : MAC_AX_SS_WMM_TBL_C1_WMM0; ret = mac_ss_wmm_sta_move(adapter, (enum mac_ax_ss_wmm)dbcc_wmm, dst_link); if (ret == MACARDYDONE) { if (wmm_type != MAC_AX_NET_TYPE_AP) PLTFM_MSG_WARN("dbcc en%d move wmm%d is already mapped\n", dbcc_en, dbcc_wmm); return MACSUCCESS; } else if (ret != MACSUCCESS) { PLTFM_MSG_ERR("dbcc en%d move wmm%d sta %d\n", dbcc_en, dbcc_wmm, ret); return ret; } PLTFM_MEMSET(&q_info, 0, sizeof(struct deq_enq_info)); if (!dbcc_en) { q_info.src_pid = WDE_DLE_PID_C1; q_info.src_qid = WDE_DLE_QID_MG0_C1; q_info.dst_pid = WDE_DLE_PID_C0; q_info.dst_qid = WDE_DLE_QID_MG0_C0; ret = deq_enq_all(adapter, &q_info); if (ret != MACSUCCESS) { PLTFM_MSG_ERR("dbcc dis move all mgq %d\n", ret); return ret; } q_info.src_pid = WDE_DLE_PID_C1; q_info.src_qid = WDE_DLE_QID_HI_C1; q_info.dst_pid = WDE_DLE_PID_C0; q_info.dst_qid = WDE_DLE_QID_HI_C0; ret = deq_enq_all(adapter, &q_info); if (ret != MACSUCCESS) { PLTFM_MSG_ERR("dbcc dis move all hiq %d\n", ret); return ret; } } return MACSUCCESS; }