/****************************************************************************** * * 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 "coex.h" #include "../mac_reg.h" #include "hw.h" #include "power_saving.h" #define MAC_AX_RTK_RATE 5 #define MAC_AX_BT_MODE_0_3 0 #define MAC_AX_BT_MODE_2 2 #define MAC_AX_CSR_DELAY 0 #define MAC_AX_CSR_PRI_TO 5 #define MAC_AX_CSR_TRX_TO 4 #define MAC_AX_CSR_RATE 80 #define MAC_AX_SB_DRV_MSK 0xFFFFFF #define MAC_AX_SB_DRV_SH 0 #define MAC_AX_SB_FW_MSK 0x7F #define MAC_AX_SB_FW_SH 24 #define R_AX_LTECOEX_STATUS 0x54 #define B_AX_GNT_BT_RFC_S0_STA BIT(3) #define B_AX_GNT_WL_RFC_S0_STA BIT(2) #define B_AX_GNT_BT_RFC_S1_STA BIT(5) #define B_AX_GNT_WL_RFC_S1_STA BIT(4) #define MAC_AX_BTGS1_NOTIFY BIT(0) u32 mac_coex_init(struct mac_ax_adapter *adapter, struct mac_ax_coex *coex) { struct mac_ax_intf_ops *ops = adapter_to_intf_ops(adapter); u8 val; u16 val16; u32 ret, val32; val = MAC_REG_R8(R_AX_GPIO_MUXCFG); MAC_REG_W8(R_AX_GPIO_MUXCFG, val | B_AX_ENBT); switch (coex->direction) { case MAC_AX_COEX_INNER: val = MAC_REG_R8(R_AX_GPIO_MUXCFG + 1); val = (val & ~BIT(2)) | BIT(1); MAC_REG_W8(R_AX_GPIO_MUXCFG + 1, val); break; case MAC_AX_COEX_OUTPUT: val = MAC_REG_R8(R_AX_GPIO_MUXCFG + 1); val = val | BIT(1) | BIT(0); MAC_REG_W8(R_AX_GPIO_MUXCFG + 1, val); break; case MAC_AX_COEX_INPUT: val = MAC_REG_R8(R_AX_GPIO_MUXCFG + 1); val = val & ~(BIT(2) | BIT(1)); MAC_REG_W8(R_AX_GPIO_MUXCFG + 1, val); break; default: return MACNOITEM; } #if MAC_AX_FW_REG_OFLD if (adapter->sm.fwdl == MAC_AX_FWDL_INIT_RDY) { ret = MAC_REG_W_OFLD(R_AX_BTC_FUNC_EN, B_AX_PTA_WL_TX_EN, 1, 0); if (ret != MACSUCCESS) { PLTFM_MSG_ERR("%s: write offload fail %d", __func__, ret); return ret; } ret = MAC_REG_W_OFLD(R_AX_BT_COEX_CFG_2, B_AX_GNT_BT_BYPASS_PRIORITY, 1, 0); if (ret != MACSUCCESS) { PLTFM_MSG_ERR("%s: write offload fail %d", __func__, ret); return ret; } ret = MAC_REG_W_OFLD(R_AX_CSR_MODE, B_AX_WL_ACT_MSK | B_AX_STATIS_BT_EN | B_AX_BT_CNT_REST, 0x4003, 0); if (ret != MACSUCCESS) { PLTFM_MSG_ERR("%s: write offload fail %d", __func__, ret); return ret; } ret = MAC_REG_W_OFLD(R_AX_TRXPTCL_RESP_0, B_AX_RSP_CHK_BTCCA, 0, 0); if (ret != MACSUCCESS) { PLTFM_MSG_ERR("%s: write offload fail %d", __func__, ret); return ret; } ret = MAC_REG_W_OFLD(R_AX_CCA_CFG_0, B_AX_BTCCA_BRK_TXOP_EN | B_AX_BTCCA_EN, 1, 0); if (ret != MACSUCCESS) { PLTFM_MSG_ERR("%s: write offload fail %d", __func__, ret); return ret; } switch (coex->pta_mode) { case MAC_AX_COEX_RTK_MODE: val = MAC_REG_R8(R_AX_GPIO_MUXCFG); val = SET_CLR_WORD(val, MAC_AX_BT_MODE_0_3, B_AX_BTMODE); MAC_REG_W8(R_AX_GPIO_MUXCFG, val); ret = MAC_REG_W_OFLD(R_AX_TDMA_MODE, B_AX_RTK_BT_ENABLE, 1, 0); if (ret != MACSUCCESS) { PLTFM_MSG_ERR("%s: write offload fail %d", __func__, ret); return ret; } ret = MAC_REG_W_OFLD(R_AX_BT_COEX_CFG_5, B_AX_BT_RPT_SAMPLE_RATE_MSK << B_AX_BT_RPT_SAMPLE_RATE_SH, MAC_AX_RTK_RATE, 1); if (ret != MACSUCCESS) { PLTFM_MSG_ERR("%s: write offload fail %d", __func__, ret); return ret; } break; case MAC_AX_COEX_CSR_MODE: val = MAC_REG_R8(R_AX_GPIO_MUXCFG); val = SET_CLR_WORD(val, MAC_AX_BT_MODE_2, B_AX_BTMODE); MAC_REG_W8(R_AX_GPIO_MUXCFG, val); ret = MAC_REG_W_OFLD(R_AX_CSR_MODE, B_AX_BT_PRI_DETECT_TO_MSK << B_AX_BT_PRI_DETECT_TO_SH, MAC_AX_CSR_PRI_TO, 0); if (ret != MACSUCCESS) { PLTFM_MSG_ERR("%s: write offload fail %d", __func__, ret); return ret; } ret = MAC_REG_W_OFLD(R_AX_CSR_MODE, B_AX_BT_TRX_INIT_DETECT_MSK << B_AX_BT_TRX_INIT_DETECT_SH, MAC_AX_CSR_TRX_TO, 0); if (ret != MACSUCCESS) { PLTFM_MSG_ERR("%s: write offload fail %d", __func__, ret); return ret; } ret = MAC_REG_W_OFLD(R_AX_CSR_MODE, B_AX_BT_STAT_DELAY_MSK << B_AX_BT_STAT_DELAY_SH, MAC_AX_CSR_DELAY, 0); if (ret != MACSUCCESS) { PLTFM_MSG_ERR("%s: write offload fail %d", __func__, ret); return ret; } ret = MAC_REG_W8_OFLD(R_AX_BT_COEX_CFG_2, MAC_AX_CSR_RATE, 1); if (ret != MACSUCCESS) { PLTFM_MSG_ERR("%s: write offload fail %d", __func__, ret); return ret; } break; default: return MACNOITEM; } return ret; } #endif val = MAC_REG_R8(R_AX_BTC_FUNC_EN); MAC_REG_W8(R_AX_BTC_FUNC_EN, val | B_AX_PTA_WL_TX_EN); val = MAC_REG_R8(R_AX_BT_COEX_CFG_2 + 1); MAC_REG_W8(R_AX_BT_COEX_CFG_2 + 1, val | BIT(0)); val = MAC_REG_R8(R_AX_CSR_MODE); MAC_REG_W8(R_AX_CSR_MODE, val | B_AX_STATIS_BT_EN | B_AX_WL_ACT_MSK); val = MAC_REG_R8(R_AX_CSR_MODE + 2); MAC_REG_W8(R_AX_CSR_MODE + 2, val | BIT(0)); val = MAC_REG_R8(R_AX_TRXPTCL_RESP_0 + 3); MAC_REG_W8(R_AX_TRXPTCL_RESP_0 + 3, val & ~BIT(1)); val16 = MAC_REG_R16(R_AX_CCA_CFG_0); val16 = (val16 | B_AX_BTCCA_EN) & ~B_AX_BTCCA_BRK_TXOP_EN; MAC_REG_W16(R_AX_CCA_CFG_0, val16); ret = mac_read_lte(adapter, R_AX_LTE_SW_CFG_2, &val32); if (ret) { PLTFM_MSG_ERR("%s: Read LTE fail!\n", __func__); return ret; } val32 = val32 & B_AX_WL_RX_CTRL; ret = mac_write_lte(adapter, R_AX_LTE_SW_CFG_2, val32); if (ret) { PLTFM_MSG_ERR("%s: Write LTE fail!\n", __func__); return ret; } switch (coex->pta_mode) { case MAC_AX_COEX_RTK_MODE: val = MAC_REG_R8(R_AX_GPIO_MUXCFG); val = SET_CLR_WORD(val, MAC_AX_BT_MODE_0_3, B_AX_BTMODE); MAC_REG_W8(R_AX_GPIO_MUXCFG, val); val = MAC_REG_R8(R_AX_TDMA_MODE); MAC_REG_W8(R_AX_TDMA_MODE, val | B_AX_RTK_BT_ENABLE); val = MAC_REG_R8(R_AX_BT_COEX_CFG_5); val = SET_CLR_WORD(val, MAC_AX_RTK_RATE, B_AX_BT_RPT_SAMPLE_RATE); MAC_REG_W8(R_AX_BT_COEX_CFG_5, val); break; case MAC_AX_COEX_CSR_MODE: val = MAC_REG_R8(R_AX_GPIO_MUXCFG); val = SET_CLR_WORD(val, MAC_AX_BT_MODE_2, B_AX_BTMODE); MAC_REG_W8(R_AX_GPIO_MUXCFG, val); val16 = MAC_REG_R16(R_AX_CSR_MODE); val16 = SET_CLR_WORD(val16, MAC_AX_CSR_PRI_TO, B_AX_BT_PRI_DETECT_TO); val16 = SET_CLR_WORD(val16, MAC_AX_CSR_TRX_TO, B_AX_BT_TRX_INIT_DETECT); val16 = SET_CLR_WORD(val16, MAC_AX_CSR_DELAY, B_AX_BT_STAT_DELAY); val16 = val16 | B_AX_ENHANCED_BT; MAC_REG_W16(R_AX_CSR_MODE, val16); MAC_REG_W8(R_AX_BT_COEX_CFG_2, MAC_AX_CSR_RATE); break; default: return MACNOITEM; } return MACSUCCESS; } u32 mac_get_gnt(struct mac_ax_adapter *adapter, struct mac_ax_coex_gnt *gnt_cfg) { u32 val, ret, status; struct mac_ax_gnt *gnt; ret = mac_read_lte(adapter, R_AX_LTE_SW_CFG_1, &val); if (ret) { PLTFM_MSG_ERR("Read LTE fail!\n"); return ret; } ret = mac_read_lte(adapter, R_AX_LTECOEX_STATUS, &status); if (ret) { PLTFM_MSG_ERR("Read LTE fail!\n"); return ret; } gnt = &gnt_cfg->band0; gnt->gnt_bt_sw_en = !!(val & B_AX_GNT_BT_RFC_S0_SW_CTRL); gnt->gnt_bt = !!(status & B_AX_GNT_BT_RFC_S0_STA); gnt->gnt_wl_sw_en = !!(val & B_AX_GNT_WL_RFC_S0_SW_CTRL); gnt->gnt_wl = !!(status & B_AX_GNT_WL_RFC_S0_STA); gnt = &gnt_cfg->band1; gnt->gnt_bt_sw_en = !!(val & B_AX_GNT_BT_RFC_S1_SW_CTRL); gnt->gnt_bt = !!(status & B_AX_GNT_BT_RFC_S1_STA); gnt->gnt_wl_sw_en = !!(val & B_AX_GNT_WL_RFC_S1_SW_CTRL); gnt->gnt_wl = !!(status & B_AX_GNT_WL_RFC_S1_STA); return MACSUCCESS; } u32 mac_cfg_gnt(struct mac_ax_adapter *adapter, struct mac_ax_coex_gnt *gnt_cfg) { u32 val, ret; ret = mac_read_lte(adapter, R_AX_LTE_SW_CFG_1, &val); if (ret) { PLTFM_MSG_ERR("Read LTE fail!\n"); return ret; } val = (gnt_cfg->band0.gnt_bt ? (B_AX_GNT_BT_RFC_S0_SW_VAL | B_AX_GNT_BT_BB_S0_SW_VAL) : 0) | (gnt_cfg->band0.gnt_bt_sw_en ? (B_AX_GNT_BT_RFC_S0_SW_CTRL | B_AX_GNT_BT_BB_S0_SW_CTRL) : 0) | (gnt_cfg->band0.gnt_wl ? (B_AX_GNT_WL_RFC_S0_SW_VAL | B_AX_GNT_WL_BB_S0_SW_VAL) : 0) | (gnt_cfg->band0.gnt_wl_sw_en ? (B_AX_GNT_WL_RFC_S0_SW_CTRL | B_AX_GNT_WL_BB_S0_SW_CTRL) : 0) | (gnt_cfg->band1.gnt_bt ? (B_AX_GNT_BT_RFC_S1_SW_VAL | B_AX_GNT_BT_BB_S1_SW_VAL) : 0) | (gnt_cfg->band1.gnt_bt_sw_en ? (B_AX_GNT_BT_RFC_S1_SW_CTRL | B_AX_GNT_BT_BB_S1_SW_CTRL) : 0) | (gnt_cfg->band1.gnt_wl ? (B_AX_GNT_WL_RFC_S1_SW_VAL | B_AX_GNT_WL_BB_S1_SW_VAL) : 0) | (gnt_cfg->band1.gnt_wl_sw_en ? (B_AX_GNT_WL_RFC_S1_SW_CTRL | B_AX_GNT_WL_BB_S1_SW_CTRL) : 0); ret = mac_write_lte(adapter, R_AX_LTE_SW_CFG_1, val); if (ret) { PLTFM_MSG_ERR("Write LTE fail!\n"); return ret; } ret = mac_read_lte(adapter, R_AX_LTE_SW_CFG_2, &val); if (ret) { PLTFM_MSG_ERR("Read LTE fail!\n"); return ret; } val = val & B_AX_WL_RX_CTRL ? B_AX_WL_RX_CTRL : 0 | ((gnt_cfg->band0.gnt_bt_sw_en || gnt_cfg->band1.gnt_bt_sw_en) ? (B_AX_GNT_BT_TX_SW_CTRL | B_AX_GNT_BT_RX_SW_CTRL) : 0) | ((gnt_cfg->band0.gnt_bt || gnt_cfg->band1.gnt_bt) ? (B_AX_GNT_BT_TX_SW_VAL | B_AX_GNT_BT_RX_SW_VAL) : 0) | ((gnt_cfg->band0.gnt_wl_sw_en || gnt_cfg->band1.gnt_wl_sw_en) ? (B_AX_GNT_WL_TX_SW_CTRL | B_AX_GNT_WL_RX_SW_CTRL) : 0) | ((gnt_cfg->band0.gnt_wl || gnt_cfg->band1.gnt_wl) ? (B_AX_GNT_WL_TX_SW_VAL | B_AX_GNT_WL_RX_SW_VAL) : 0); ret = mac_write_lte(adapter, R_AX_LTE_SW_CFG_2, val); if (ret) { PLTFM_MSG_ERR("Write LTE fail!\n"); return ret; } return MACSUCCESS; } u32 mac_cfg_plt(struct mac_ax_adapter *adapter, struct mac_ax_plt *plt) { struct mac_ax_intf_ops *ops = adapter_to_intf_ops(adapter); u32 reg, ret; u16 val; ret = check_mac_en(adapter, plt->band, MAC_AX_CMAC_SEL); if (ret) return ret; reg = plt->band == 0 ? R_AX_BT_PLT : R_AX_BT_PLT_C1; val = (plt->tx & MAC_AX_PLT_LTE_RX ? B_AX_TX_PLT_GNT_LTE_RX : 0) | (plt->tx & MAC_AX_PLT_GNT_BT_TX ? B_AX_TX_PLT_GNT_BT_TX : 0) | (plt->tx & MAC_AX_PLT_GNT_BT_RX ? B_AX_TX_PLT_GNT_BT_RX : 0) | (plt->tx & MAC_AX_PLT_GNT_WL ? B_AX_TX_PLT_GNT_WL : 0) | (plt->rx & MAC_AX_PLT_LTE_RX ? B_AX_RX_PLT_GNT_LTE_RX : 0) | (plt->rx & MAC_AX_PLT_GNT_BT_TX ? B_AX_RX_PLT_GNT_BT_TX : 0) | (plt->rx & MAC_AX_PLT_GNT_BT_RX ? B_AX_RX_PLT_GNT_BT_RX : 0) | (plt->rx & MAC_AX_PLT_GNT_WL ? B_AX_RX_PLT_GNT_WL : 0) | (plt->rx || plt->tx ? B_AX_PLT_EN : 0); #if MAC_AX_FW_REG_OFLD if (adapter->sm.fwdl == MAC_AX_FWDL_INIT_RDY) { ret = MAC_REG_W_OFLD((u16)reg, B_AX_TX_PLT_GNT_LTE_RX | B_AX_TX_PLT_GNT_BT_TX | B_AX_TX_PLT_GNT_BT_RX | B_AX_TX_PLT_GNT_WL | B_AX_RX_PLT_GNT_LTE_RX | B_AX_RX_PLT_GNT_BT_TX | B_AX_RX_PLT_GNT_BT_RX | B_AX_RX_PLT_GNT_WL, val, 1); if (ret != MACSUCCESS) PLTFM_MSG_ERR("%s: write offload fail %d", __func__, ret); return ret; } #endif MAC_REG_W16(reg, val); return MACSUCCESS; } u32 mac_read_coex_reg(struct mac_ax_adapter *adapter, const u32 offset, u32 *val) { struct mac_ax_intf_ops *ops = adapter_to_intf_ops(adapter); if (offset > 0xFF) { PLTFM_MSG_ERR("[ERR]offset exceed coex reg\n"); return MACBADDR; } *val = MAC_REG_R32(R_AX_BTC_CFG + offset); return MACSUCCESS; } u32 mac_write_coex_reg(struct mac_ax_adapter *adapter, const u32 offset, const u32 val) { struct mac_ax_intf_ops *ops = adapter_to_intf_ops(adapter); if (offset > 0xFF) { PLTFM_MSG_ERR("[ERR]offset exceed coex reg\n"); return MACBADDR; } MAC_REG_W32(R_AX_BTC_CFG + offset, val); return MACSUCCESS; } void mac_cfg_sb(struct mac_ax_adapter *adapter, u32 val) { struct mac_ax_intf_ops *ops = adapter_to_intf_ops(adapter); u32 fw_sb; fw_sb = MAC_REG_R32(R_AX_SCOREBOARD); fw_sb = GET_FIELD(fw_sb, MAC_AX_SB_FW); fw_sb = fw_sb & ~MAC_AX_BTGS1_NOTIFY; if (adapter->sm.pwr == MAC_AX_PWR_OFF || _is_in_lps(adapter)) fw_sb = fw_sb | MAC_AX_NOTIFY_PWR_MAJOR; else fw_sb = fw_sb | MAC_AX_NOTIFY_TP_MAJOR; val = GET_FIELD(val, MAC_AX_SB_DRV); val = B_AX_TOGGLE | SET_WORD(val, MAC_AX_SB_DRV) | SET_WORD(fw_sb, MAC_AX_SB_FW); MAC_REG_W32(R_AX_SCOREBOARD, val); } u32 mac_cfg_ctrl_path(struct mac_ax_adapter *adapter, u32 wl) { struct mac_ax_intf_ops *ops = adapter_to_intf_ops(adapter); u8 val = MAC_REG_R8(R_AX_SYS_SDIO_CTRL + 3); val = wl ? val | BIT(2) : val & ~BIT(2); MAC_REG_W8(R_AX_SYS_SDIO_CTRL + 3, val); return MACSUCCESS; } u32 mac_get_ctrl_path(struct mac_ax_adapter *adapter, u32 *wl) { struct mac_ax_intf_ops *ops = adapter_to_intf_ops(adapter); u8 val = MAC_REG_R8(R_AX_SYS_SDIO_CTRL + 3); *wl = !!(val & BIT(2)); return MACSUCCESS; } u32 mac_get_bt_polt_cnt(struct mac_ax_adapter *adapter, struct mac_ax_bt_polt_cnt *cnt) { struct mac_ax_intf_ops *ops = adapter_to_intf_ops(adapter); u32 offset = cnt->band ? R_AX_BT_PLT_C1 : R_AX_BT_PLT; u8 val; cnt->cnt = MAC_REG_R16(offset + 2); val = MAC_REG_R8(offset + 1); MAC_REG_W8(offset + 1, val | BIT(1)); return MACSUCCESS; } u32 mac_write_coex_mask(struct mac_ax_adapter *adapter, u32 offset, u32 mask, u32 val) { u32 ret; if (offset < R_AX_BTC_CFG || offset > R_AX_LTE_RDATA) { PLTFM_MSG_ERR("[ERR]offset exceed coex reg\n"); return MACBADDR; } ret = MAC_REG_W_OFLD((u16)offset, mask, val, 1); if (ret) { PLTFM_MSG_ERR("[ERR]%s fail\n", __func__); return ret; } return MACSUCCESS; }