root/drivers/net/wireless/realtek/rtw89/pci_be.c
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/* Copyright(c) 2023  Realtek Corporation
 */

#include <linux/pci.h>

#include "mac.h"
#include "pci.h"
#include "reg.h"

enum pcie_rxbd_mode {
        PCIE_RXBD_NORM = 0,
        PCIE_RXBD_SEP,
        PCIE_RXBD_EXT,
};

#define PL0_TMR_SCALE_ASIC 1
#define PL0_TMR_ANA_172US 0x800
#define PL0_TMR_MAC_1MS 0x27100
#define PL0_TMR_AUX_1MS 0x1E848

static void rtw89_pci_aspm_set_be(struct rtw89_dev *rtwdev, bool enable)
{
        struct rtw89_pci *rtwpci = (struct rtw89_pci *)rtwdev->priv;
        struct pci_dev *pdev = rtwpci->pdev;
        u8 value = 0;
        int ret;

        ret = pci_read_config_byte(pdev, RTW89_PCIE_ASPM_CTRL, &value);
        if (ret)
                rtw89_warn(rtwdev, "failed to read ASPM Delay\n");

        u8p_replace_bits(&value, PCIE_L1DLY_16US, RTW89_L1DLY_MASK);

        ret = pci_write_config_byte(pdev, RTW89_PCIE_ASPM_CTRL, value);
        if (ret)
                rtw89_warn(rtwdev, "failed to write ASPM Delay\n");

        if (enable)
                rtw89_write32_set(rtwdev, R_AX_PCIE_MIX_CFG_V1,
                                  B_BE_ASPM_CTRL_L1);
        else
                rtw89_write32_clr(rtwdev, R_AX_PCIE_MIX_CFG_V1,
                                  B_BE_ASPM_CTRL_L1);
}

static void rtw89_pci_l1ss_set_be(struct rtw89_dev *rtwdev, bool enable)
{
        enum rtw89_core_chip_id chip_id = rtwdev->chip->chip_id;
        struct rtw89_hal *hal = &rtwdev->hal;

        if (enable && chip_id == RTL8922D && hal->cid == RTL8922D_CID7090)
                rtw89_write32_set(rtwdev, R_BE_PCIE_PS_CTRL,
                                  B_BE_ASPM_L11_EN | B_BE_ASPM_L12_EN |
                                  B_BE_PCIPM_L11_EN | B_BE_PCIPM_L12_EN);

        if (enable)
                rtw89_write32_set(rtwdev, R_BE_PCIE_MIX_CFG,
                                  B_BE_L1SUB_ENABLE);
        else
                rtw89_write32_clr(rtwdev, R_BE_PCIE_MIX_CFG,
                                  B_BE_L1SUB_ENABLE);
}

static void rtw89_pci_clkreq_set_be(struct rtw89_dev *rtwdev, bool enable)
{
        rtw89_write32_mask(rtwdev, R_BE_PCIE_LAT_CTRL, B_BE_CLK_REQ_LAT_MASK,
                           PCIE_CLKDLY_HW_V1_0);

        if (enable)
                rtw89_write32_set(rtwdev, R_BE_L1_CLK_CTRL,
                                  B_BE_CLK_PM_EN);
        else
                rtw89_write32_clr(rtwdev, R_AX_L1_CLK_CTRL,
                                  B_BE_CLK_PM_EN);
}

static void _patch_pcie_power_wake_be(struct rtw89_dev *rtwdev, bool power_up)
{
        if (power_up)
                rtw89_write32_set(rtwdev, R_BE_HCI_OPT_CTRL, BIT_WAKE_CTRL_V1);
        else
                rtw89_write32_clr(rtwdev, R_BE_HCI_OPT_CTRL, BIT_WAKE_CTRL_V1);
}

static void _patch_pre_init_be(struct rtw89_dev *rtwdev)
{
        struct rtw89_hal *hal = &rtwdev->hal;

        if (!(rtwdev->chip->chip_id == RTL8922D && hal->cid == RTL8922D_CID7090))
                return;

        rtw89_write16_clr(rtwdev, R_RAC_DIRECT_OFFSET_BE_LANE0_G2 +
                                  RAC_ANA14 * RAC_MULT, EIEOS_L1SS_WAIT_CLKRDY);
}

static void rtw89_pci_set_io_rcy_be(struct rtw89_dev *rtwdev)
{
        const struct rtw89_pci_info *info = rtwdev->pci_info;
        u32 scale = PL0_TMR_SCALE_ASIC;
        u32 val32;

        if (info->io_rcy_en == MAC_AX_PCIE_ENABLE) {
                val32 = info->io_rcy_tmr == MAC_AX_IO_RCY_ANA_TMR_DEF ?
                        PL0_TMR_ANA_172US : info->io_rcy_tmr;
                val32 /= scale;

                rtw89_write32(rtwdev, R_BE_AON_WDT_TMR, val32);
                rtw89_write32(rtwdev, R_BE_MDIO_WDT_TMR, val32);
                rtw89_write32(rtwdev, R_BE_LA_MODE_WDT_TMR, val32);
                rtw89_write32(rtwdev, R_BE_WDT_AR_TMR, val32);
                rtw89_write32(rtwdev, R_BE_WDT_AW_TMR, val32);
                rtw89_write32(rtwdev, R_BE_WDT_W_TMR, val32);
                rtw89_write32(rtwdev, R_BE_WDT_B_TMR, val32);
                rtw89_write32(rtwdev, R_BE_WDT_R_TMR, val32);

                val32 = info->io_rcy_tmr == MAC_AX_IO_RCY_ANA_TMR_DEF ?
                        PL0_TMR_MAC_1MS : info->io_rcy_tmr;
                val32 /= scale;
                rtw89_write32(rtwdev, R_BE_WLAN_WDT_TMR, val32);
                rtw89_write32(rtwdev, R_BE_AXIDMA_WDT_TMR, val32);

                val32 = info->io_rcy_tmr == MAC_AX_IO_RCY_ANA_TMR_DEF ?
                        PL0_TMR_AUX_1MS : info->io_rcy_tmr;
                val32 /= scale;
                rtw89_write32(rtwdev, R_BE_LOCAL_WDT_TMR, val32);
        } else {
                rtw89_write32_clr(rtwdev, R_BE_WLAN_WDT, B_BE_WLAN_WDT_ENABLE);
                rtw89_write32_clr(rtwdev, R_BE_AXIDMA_WDT, B_BE_AXIDMA_WDT_ENABLE);
                rtw89_write32_clr(rtwdev, R_BE_AON_WDT, B_BE_AON_WDT_ENABLE);
                rtw89_write32_clr(rtwdev, R_BE_LOCAL_WDT, B_BE_LOCAL_WDT_ENABLE);
                rtw89_write32_clr(rtwdev, R_BE_MDIO_WDT, B_BE_MDIO_WDT_ENABLE);
                rtw89_write32_clr(rtwdev, R_BE_LA_MODE_WDT, B_BE_LA_MODE_WDT_ENABLE);
                rtw89_write32_clr(rtwdev, R_BE_WDT_AR, B_BE_WDT_AR_ENABLE);
                rtw89_write32_clr(rtwdev, R_BE_WDT_AW, B_BE_WDT_AW_ENABLE);
                rtw89_write32_clr(rtwdev, R_BE_WDT_W, B_BE_WDT_W_ENABLE);
                rtw89_write32_clr(rtwdev, R_BE_WDT_B, B_BE_WDT_B_ENABLE);
                rtw89_write32_clr(rtwdev, R_BE_WDT_R, B_BE_WDT_R_ENABLE);
        }
}

static void rtw89_pci_ctrl_wpdma_pcie_be(struct rtw89_dev *rtwdev, bool en)
{
        if (en)
                rtw89_write32_clr(rtwdev, R_BE_HAXI_DMA_STOP1, B_BE_STOP_WPDMA);
        else
                rtw89_write32_set(rtwdev, R_BE_HAXI_DMA_STOP1, B_BE_STOP_WPDMA);
}

static void rtw89_pci_ctrl_trxdma_pcie_be(struct rtw89_dev *rtwdev,
                                          enum mac_ax_pcie_func_ctrl tx_en,
                                          enum mac_ax_pcie_func_ctrl rx_en,
                                          enum mac_ax_pcie_func_ctrl io_en)
{
        u32 val;

        val = rtw89_read32(rtwdev, R_BE_HAXI_INIT_CFG1);

        if (tx_en == MAC_AX_PCIE_ENABLE)
                val |= B_BE_TXDMA_EN;
        else if (tx_en == MAC_AX_PCIE_DISABLE)
                val &= ~B_BE_TXDMA_EN;

        if (rx_en == MAC_AX_PCIE_ENABLE)
                val |= B_BE_RXDMA_EN;
        else if (rx_en == MAC_AX_PCIE_DISABLE)
                val &= ~B_BE_RXDMA_EN;

        if (io_en == MAC_AX_PCIE_ENABLE)
                val &= ~B_BE_STOP_AXI_MST;
        else if (io_en == MAC_AX_PCIE_DISABLE)
                val |= B_BE_STOP_AXI_MST;

        rtw89_write32(rtwdev, R_BE_HAXI_INIT_CFG1, val);

        if (io_en == MAC_AX_PCIE_ENABLE && rtwdev->chip->chip_id == RTL8922A)
                rtw89_write32_mask(rtwdev, R_BE_HAXI_MST_WDT_TIMEOUT_SEL_V1,
                                   B_BE_HAXI_MST_WDT_TIMEOUT_SEL_MASK, 4);
}

static void rtw89_pci_clr_idx_all_be(struct rtw89_dev *rtwdev)
{
        struct rtw89_pci *rtwpci = (struct rtw89_pci *)rtwdev->priv;
        const struct rtw89_chip_info *chip = rtwdev->chip;
        struct rtw89_pci_rx_ring *rx_ring;
        u32 val;

        if (chip->chip_id == RTL8922A)
                val = B_BE_CLR_ALL_IDX_MASK;
        else
                val = B_BE_CLR_ALL_IDX_MASK_V1;

        rtw89_write32(rtwdev, R_BE_TXBD_RWPTR_CLR1, val);

        rtw89_write32(rtwdev, R_BE_RXBD_RWPTR_CLR1_V1,
                      B_BE_CLR_RXQ0_IDX | B_BE_CLR_RPQ0_IDX);

        rx_ring = &rtwpci->rx.rings[RTW89_RXCH_RXQ];
        rtw89_write16(rtwdev, R_BE_RXQ0_RXBD_IDX_V1, rx_ring->bd_ring.len - 1);

        rx_ring = &rtwpci->rx.rings[RTW89_RXCH_RPQ];
        rtw89_write16(rtwdev, R_BE_RPQ0_RXBD_IDX_V1, rx_ring->bd_ring.len - 1);
}

static int rtw89_pci_poll_txdma_ch_idle_be(struct rtw89_dev *rtwdev)
{
        const struct rtw89_pci_info *info = rtwdev->pci_info;
        u32 dma_busy1 = info->dma_busy1.addr;
        u32 check = info->dma_busy1.mask;
        u32 val;

        return read_poll_timeout(rtw89_read32, val, (val & check) == 0,
                                 10, 1000, false, rtwdev, dma_busy1);
}

static int rtw89_pci_poll_rxdma_ch_idle_be(struct rtw89_dev *rtwdev)
{
        u32 check;
        u32 val;

        check = B_BE_RXQ0_BUSY_V1 | B_BE_RPQ0_BUSY_V1;

        return read_poll_timeout(rtw89_read32, val, (val & check) == 0,
                                 10, 1000, false, rtwdev, R_BE_HAXI_DMA_BUSY1);
}

static int rtw89_pci_poll_dma_all_idle_be(struct rtw89_dev *rtwdev)
{
        int ret;

        ret = rtw89_pci_poll_txdma_ch_idle_be(rtwdev);
        if (ret) {
                rtw89_err(rtwdev, "txdma ch busy\n");
                return ret;
        }

        ret = rtw89_pci_poll_rxdma_ch_idle_be(rtwdev);
        if (ret) {
                rtw89_err(rtwdev, "rxdma ch busy\n");
                return ret;
        }

        return 0;
}

static void rtw89_pci_mode_op_be(struct rtw89_dev *rtwdev)
{
        const struct rtw89_pci_info *info = rtwdev->pci_info;
        const struct rtw89_chip_info *chip = rtwdev->chip;
        u32 val32_init1, val32_rxapp, val32_exp;

        val32_init1 = rtw89_read32(rtwdev, R_BE_HAXI_INIT_CFG1);
        if (chip->chip_id == RTL8922A)
                val32_rxapp = rtw89_read32(rtwdev, R_BE_RX_APPEND_MODE);
        val32_exp = rtw89_read32(rtwdev, R_BE_HAXI_EXP_CTRL_V1);

        if (chip->chip_id == RTL8922A) {
                if (info->rxbd_mode == MAC_AX_RXBD_PKT) {
                        val32_init1 = u32_replace_bits(val32_init1, PCIE_RXBD_NORM,
                                                       B_BE_RXQ_RXBD_MODE_MASK);
                } else if (info->rxbd_mode == MAC_AX_RXBD_SEP) {
                        val32_init1 = u32_replace_bits(val32_init1, PCIE_RXBD_SEP,
                                                       B_BE_RXQ_RXBD_MODE_MASK);
                        val32_rxapp = u32_replace_bits(val32_rxapp, 0,
                                                       B_BE_APPEND_LEN_MASK);
                }
        }

        val32_init1 = u32_replace_bits(val32_init1, info->tx_burst,
                                       B_BE_MAX_TXDMA_MASK);
        val32_init1 = u32_replace_bits(val32_init1, info->rx_burst,
                                       B_BE_MAX_RXDMA_MASK);
        val32_exp = u32_replace_bits(val32_exp, info->multi_tag_num,
                                     B_BE_MAX_TAG_NUM_MASK);
        val32_init1 = u32_replace_bits(val32_init1, info->wd_dma_idle_intvl,
                                       B_BE_CFG_WD_PERIOD_IDLE_MASK);
        val32_init1 = u32_replace_bits(val32_init1, info->wd_dma_act_intvl,
                                       B_BE_CFG_WD_PERIOD_ACTIVE_MASK);

        rtw89_write32(rtwdev, R_BE_HAXI_INIT_CFG1, val32_init1);
        if (chip->chip_id == RTL8922A)
                rtw89_write32(rtwdev, R_BE_RX_APPEND_MODE, val32_rxapp);
        rtw89_write32(rtwdev, R_BE_HAXI_EXP_CTRL_V1, val32_exp);
}

static int rtw89_pci_rst_bdram_be(struct rtw89_dev *rtwdev)
{
        u32 val;

        rtw89_write32_set(rtwdev, R_BE_HAXI_INIT_CFG1, B_BE_SET_BDRAM_BOUND);

        return read_poll_timeout(rtw89_read32, val, !(val & B_BE_SET_BDRAM_BOUND),
                                 50, 500000, false, rtwdev, R_BE_HAXI_INIT_CFG1);
}

static void rtw89_pci_debounce_be(struct rtw89_dev *rtwdev)
{
        u32 val32;

        val32 = rtw89_read32(rtwdev, R_BE_SYS_PAGE_CLK_GATED);
        val32 = u32_replace_bits(val32, 0, B_BE_PCIE_PRST_DEBUNC_PERIOD_MASK);
        val32 |= B_BE_SYM_PRST_DEBUNC_SEL;
        rtw89_write32(rtwdev, R_BE_SYS_PAGE_CLK_GATED, val32);
}

static void rtw89_pci_ldo_low_pwr_be(struct rtw89_dev *rtwdev)
{
        enum rtw89_core_chip_id chip_id = rtwdev->chip->chip_id;
        struct rtw89_hal *hal = &rtwdev->hal;
        u32 clr;

        rtw89_write32_set(rtwdev, R_BE_SYS_PW_CTRL, B_BE_PSUS_OFF_CAPC_EN);
        rtw89_write32_set(rtwdev, R_BE_SYS_PAGE_CLK_GATED,
                          B_BE_SOP_OFFPOOBS_PC | B_BE_CPHY_AUXCLK_OP |
                          B_BE_CPHY_POWER_READY_CHK);
        rtw89_write32_clr(rtwdev, R_BE_SYS_SDIO_CTRL, B_BE_PCIE_FORCE_IBX_EN |
                                                      B_BE_PCIE_DIS_L2_RTK_PERST |
                                                      B_BE_PCIE_DIS_L2__CTRL_LDO_HCI);

        if (chip_id == RTL8922D && hal->cid == RTL8922D_CID7090)
                clr = B_BE_PCIE_DIS_L1_2_CTRL_HCILDO |
                      B_BE_PCIE_DIS_L1_2_CTRL_APHY_SUSB |
                      B_BE_PCIE_DIS_RTK_PRST_N_L1_2 |
                      B_BE_PCIE_DIS_L2_CTRL_APHY_SUSB;
        else
                clr = B_BE_PCIE_DIS_L1_2_CTRL_HCILDO;

        rtw89_write32_clr(rtwdev, R_BE_L1_2_CTRL_HCILDO, clr);
}

static void rtw89_pci_pcie_setting_be(struct rtw89_dev *rtwdev)
{
        const struct rtw89_chip_info *chip = rtwdev->chip;
        struct rtw89_hal *hal = &rtwdev->hal;

        rtw89_write32_set(rtwdev, R_BE_PCIE_FRZ_CLK, B_BE_PCIE_EN_AUX_CLK);
        rtw89_write32_clr(rtwdev, R_BE_PCIE_PS_CTRL, B_BE_CMAC_EXIT_L1_EN);

        if (chip->chip_id == RTL8922A && hal->cv == CHIP_CAV)
                return;

        rtw89_write32_set(rtwdev, R_BE_EFUSE_CTRL_2_V1, B_BE_R_SYM_AUTOLOAD_WITH_PMC_SEL);
        rtw89_write32_set(rtwdev, R_BE_PCIE_LAT_CTRL, B_BE_SYM_AUX_CLK_SEL);

        if (chip->chip_id != RTL8922D)
                return;

        rtw89_write32_set(rtwdev, R_BE_RSV_CTRL, B_BE_R_SYM_PRST_CPHY_RST);
        rtw89_write32_set(rtwdev, R_BE_SYS_PW_CTRL, B_BE_USUS_OFFCAPC_EN);
        rtw89_write32(rtwdev, R_BE_PCIE_HCI2FW_ISR, 0xFFFFFFFF);
}

static void rtw89_pci_ser_setting_be(struct rtw89_dev *rtwdev)
{
        enum rtw89_core_chip_id chip_id = rtwdev->chip->chip_id;
        struct rtw89_hal *hal = &rtwdev->hal;
        u32 val32;
        int ret;

        if (chip_id == RTL8922D)
                goto be2_chips;
        else if (chip_id != RTL8922A)
                return;

        rtw89_write32(rtwdev, R_BE_PL1_DBG_INFO, 0x0);

        switch (hal->cv) {
        case CHIP_CAV:
        case CHIP_CBV:
                rtw89_write32_clr(rtwdev, R_BE_SER_PL1_CTRL, B_BE_PL1_SER_PL1_EN);
                rtw89_write32_mask(rtwdev, R_BE_SER_PL1_CTRL,
                                   B_BE_PL1_TIMER_UNIT_MASK, PCIE_SER_TIMER_UNIT);

                val32 = rtw89_read32(rtwdev, R_BE_REG_PL1_MASK);
                val32 &= ~(B_BE_SER_PMU_IMR | B_BE_SER_L1SUB_IMR |
                           B_BE_SER_PM_MASTER_IMR | B_BE_SER_LTSSM_IMR |
                           B_BE_SER_PM_CLK_MASK | B_BE_SER_PCLKREQ_ACK_MASK);
                rtw89_write32(rtwdev, R_BE_REG_PL1_MASK, val32);
                break;
        case CHIP_CCV:
        default:
                rtw89_write32_clr(rtwdev, R_BE_SER_PL1_CTRL, B_BE_PL1_SER_PL1_EN);

                ret = read_poll_timeout_atomic(rtw89_read32, val32, !val32,
                                               1, 1000, false, rtwdev, R_BE_REG_PL1_ISR);
                if (ret)
                        rtw89_warn(rtwdev, "[ERR] PCIE SER clear poll fail\n");

                rtw89_write32_mask(rtwdev, R_BE_SER_PL1_CTRL,
                                   B_BE_PL1_TIMER_UNIT_MASK, PCIE_SER_TIMER_UNIT);
                rtw89_write32_set(rtwdev, R_BE_SER_PL1_CTRL, B_BE_PL1_SER_PL1_EN);

                val32 = rtw89_read32(rtwdev, R_BE_REG_PL1_MASK);
                val32 |= (B_BE_SER_PMU_IMR | B_BE_SER_PM_MASTER_IMR |
                          B_BE_SER_LTSSM_IMR | B_BE_SER_PM_CLK_MASK |
                          B_BE_SER_PCLKREQ_ACK_MASK);
                val32 &= ~B_BE_SER_L1SUB_IMR;
                rtw89_write32(rtwdev, R_BE_REG_PL1_MASK, val32);
                break;
        }

        return;

be2_chips:
        rtw89_write32_clr(rtwdev, R_BE_PCIE_SER_DBG, B_BE_PCIE_SER_FLUSH_RSTB);
        rtw89_write32_set(rtwdev, R_BE_PCIE_SER_DBG, B_BE_PCIE_SER_FLUSH_RSTB);

        rtw89_write16_clr(rtwdev, RAC_DIRECT_OFFESET_L0_G1 +
                                  RAC_ANA40 * RAC_MULT, PHY_ERR_IMR_DIS);
        rtw89_write16_clr(rtwdev, RAC_DIRECT_OFFESET_L0_G2 +
                                  RAC_ANA40 * RAC_MULT, PHY_ERR_IMR_DIS);

        rtw89_write16_clr(rtwdev, RAC_DIRECT_OFFESET_L0_G1 +
                                  RAC_ANA41 * RAC_MULT, PHY_ERR_FLAG_EN);
        rtw89_write16_clr(rtwdev, RAC_DIRECT_OFFESET_L0_G2 +
                                  RAC_ANA41 * RAC_MULT, PHY_ERR_FLAG_EN);
        rtw89_write16_set(rtwdev, RAC_DIRECT_OFFESET_L0_G1 +
                                  RAC_ANA41 * RAC_MULT, PHY_ERR_FLAG_EN);
        rtw89_write16_set(rtwdev, RAC_DIRECT_OFFESET_L0_G2 +
                                  RAC_ANA41 * RAC_MULT, PHY_ERR_FLAG_EN);

        val32 = rtw89_read32(rtwdev, R_BE_SER_PL1_CTRL);
        val32 &= ~B_BE_PL1_SER_PL1_EN;
        rtw89_write32(rtwdev, R_BE_SER_PL1_CTRL, val32);
        rtw89_write32(rtwdev, R_BE_REG_PL1_ISR, B_PCIE_SER_ALL_ISR);

        ret = read_poll_timeout_atomic(rtw89_read32, val32, !val32,
                                       1, 1000, false, rtwdev, R_BE_REG_PL1_ISR);
        if (ret)
                rtw89_warn(rtwdev, "[ERR] PCIE SER clear poll fail\n");

        val32 = rtw89_read32(rtwdev, R_BE_REG_PL1_MASK);
        val32 |= B_BE_SER_PMU_IMR | B_BE_SER_PM_MASTER_IMR |
                 B_BE_SER_LTSSM_IMR | B_BE_SER_PM_CLK_MASK | B_BE_SER_PCLKREQ_ACK_MASK |
                 B_BE_SER_LTSSM_UNSTABLE_MASK;
        val32 &= ~B_BE_SER_L1SUB_IMR;
        rtw89_write32(rtwdev, R_BE_REG_PL1_MASK, val32);

        rtw89_write32_mask(rtwdev, R_BE_SER_PL1_CTRL, B_BE_PL1_TIMER_UNIT_MASK,
                           PCIE_SER_TIMER_UNIT);
        rtw89_write32_set(rtwdev, R_BE_SER_PL1_CTRL, B_BE_PL1_SER_PL1_EN);

        if (hal->cid == RTL8922D_CID7090)
                rtw89_write32_set(rtwdev, R_BE_SYS_SDIO_CTRL, B_BE_SER_DETECT_EN);
}

static void rtw89_pci_ctrl_txdma_ch_be(struct rtw89_dev *rtwdev, bool enable)
{
        const struct rtw89_pci_info *info = rtwdev->pci_info;
        u32 mask_all;
        u32 val;

        mask_all = B_BE_STOP_CH0 | B_BE_STOP_CH1 | B_BE_STOP_CH2 |
                   B_BE_STOP_CH3 | B_BE_STOP_CH4 | B_BE_STOP_CH5 |
                   B_BE_STOP_CH6 | B_BE_STOP_CH7 | B_BE_STOP_CH8 |
                   B_BE_STOP_CH9 | B_BE_STOP_CH10 | B_BE_STOP_CH11;

        /* mask out unsupported channels for certains chips */
        mask_all &= info->dma_stop1.mask;

        val = rtw89_read32(rtwdev, R_BE_HAXI_DMA_STOP1);
        val |= B_BE_STOP_CH13 | B_BE_STOP_CH14;

        if (enable)
                val &= ~mask_all;
        else
                val |= mask_all;

        rtw89_write32(rtwdev, R_BE_HAXI_DMA_STOP1, val);
}

static void rtw89_pci_ctrl_txdma_fw_ch_be(struct rtw89_dev *rtwdev, bool enable)
{
        u32 val = rtw89_read32(rtwdev, R_BE_HAXI_DMA_STOP1);

        if (enable)
                val &= ~B_BE_STOP_CH12;
        else
                val |= B_BE_STOP_CH12;

        rtw89_write32(rtwdev, R_BE_HAXI_DMA_STOP1, val);
}

static int rtw89_pci_ops_mac_pre_init_be(struct rtw89_dev *rtwdev)
{
        int ret;

        rtw89_pci_set_io_rcy_be(rtwdev);
        _patch_pcie_power_wake_be(rtwdev, true);
        _patch_pre_init_be(rtwdev);
        rtw89_pci_ctrl_wpdma_pcie_be(rtwdev, false);
        rtw89_pci_ctrl_trxdma_pcie_be(rtwdev, MAC_AX_PCIE_DISABLE,
                                      MAC_AX_PCIE_DISABLE, MAC_AX_PCIE_DISABLE);
        rtw89_pci_clr_idx_all_be(rtwdev);

        ret = rtw89_pci_poll_dma_all_idle_be(rtwdev);
        if (ret) {
                rtw89_err(rtwdev, "[ERR] poll pcie dma all idle\n");
                return ret;
        }

        rtw89_pci_mode_op_be(rtwdev);
        rtw89_pci_ops_reset(rtwdev);

        ret = rtw89_pci_rst_bdram_be(rtwdev);
        if (ret) {
                rtw89_err(rtwdev, "[ERR]pcie rst bdram\n");
                return ret;
        }

        rtw89_pci_debounce_be(rtwdev);
        rtw89_pci_ldo_low_pwr_be(rtwdev);
        rtw89_pci_pcie_setting_be(rtwdev);
        rtw89_pci_ser_setting_be(rtwdev);

        rtw89_pci_ctrl_txdma_ch_be(rtwdev, false);
        rtw89_pci_ctrl_txdma_fw_ch_be(rtwdev, true);
        rtw89_pci_ctrl_trxdma_pcie_be(rtwdev, MAC_AX_PCIE_ENABLE,
                                      MAC_AX_PCIE_ENABLE, MAC_AX_PCIE_ENABLE);

        return 0;
}

static int rtw89_pci_ops_mac_pre_deinit_be(struct rtw89_dev *rtwdev)
{
        u32 val;

        _patch_pcie_power_wake_be(rtwdev, false);

        val = rtw89_read32_mask(rtwdev, R_BE_IC_PWR_STATE, B_BE_WLMAC_PWR_STE_MASK);
        if (val == 0)
                return 0;

        rtw89_pci_ctrl_trxdma_pcie_be(rtwdev, MAC_AX_PCIE_DISABLE,
                                      MAC_AX_PCIE_DISABLE, MAC_AX_PCIE_DISABLE);
        rtw89_pci_clr_idx_all_be(rtwdev);

        return 0;
}

int rtw89_pci_ltr_set_v2(struct rtw89_dev *rtwdev, bool en)
{
        u32 ctrl0, cfg0, cfg1, dec_ctrl, idle_ltcy, act_ltcy, dis_ltcy;
        u32 ltr_idle_lat_ctrl, ltr_act_lat_ctrl;

        ctrl0 = rtw89_read32(rtwdev, R_BE_LTR_CTRL_0);
        if (rtw89_pci_ltr_is_err_reg_val(ctrl0))
                return -EINVAL;
        cfg0 = rtw89_read32(rtwdev, R_BE_LTR_CFG_0);
        if (rtw89_pci_ltr_is_err_reg_val(cfg0))
                return -EINVAL;
        cfg1 = rtw89_read32(rtwdev, R_BE_LTR_CFG_1);
        if (rtw89_pci_ltr_is_err_reg_val(cfg1))
                return -EINVAL;
        dec_ctrl = rtw89_read32(rtwdev, R_BE_LTR_DECISION_CTRL_V1);
        if (rtw89_pci_ltr_is_err_reg_val(dec_ctrl))
                return -EINVAL;
        idle_ltcy = rtw89_read32(rtwdev, R_BE_LTR_LATENCY_IDX3_V1);
        if (rtw89_pci_ltr_is_err_reg_val(idle_ltcy))
                return -EINVAL;
        act_ltcy = rtw89_read32(rtwdev, R_BE_LTR_LATENCY_IDX1_V1);
        if (rtw89_pci_ltr_is_err_reg_val(act_ltcy))
                return -EINVAL;
        dis_ltcy = rtw89_read32(rtwdev, R_BE_LTR_LATENCY_IDX0_V1);
        if (rtw89_pci_ltr_is_err_reg_val(dis_ltcy))
                return -EINVAL;

        if (en) {
                dec_ctrl |= B_BE_ENABLE_LTR_CTL_DECISION | B_BE_LTR_HW_DEC_EN_V1;
                ctrl0 |= B_BE_LTR_HW_EN;
        } else {
                dec_ctrl &= ~(B_BE_ENABLE_LTR_CTL_DECISION | B_BE_LTR_HW_DEC_EN_V1 |
                              B_BE_LTR_EN_PORT_V1_MASK);
                ctrl0 &= ~B_BE_LTR_HW_EN;
        }

        dec_ctrl = u32_replace_bits(dec_ctrl, PCI_LTR_SPC_500US,
                                    B_BE_LTR_SPACE_IDX_MASK);
        cfg0 = u32_replace_bits(cfg0, PCI_LTR_IDLE_TIMER_3_2MS,
                                B_BE_LTR_IDLE_TIMER_IDX_MASK);
        cfg1 = u32_replace_bits(cfg1, 0xC0, B_BE_LTR_CMAC0_RX_USE_PG_TH_MASK);
        cfg1 = u32_replace_bits(cfg1, 0xC0, B_BE_LTR_CMAC1_RX_USE_PG_TH_MASK);
        cfg0 = u32_replace_bits(cfg0, 1, B_BE_LTR_IDX_ACTIVE_MASK);
        cfg0 = u32_replace_bits(cfg0, 3, B_BE_LTR_IDX_IDLE_MASK);
        dec_ctrl = u32_replace_bits(dec_ctrl, 0, B_BE_LTR_IDX_DISABLE_V1_MASK);

        if (rtwdev->chip->chip_id == RTL8922A) {
                ltr_idle_lat_ctrl = 0x90039003;
                ltr_act_lat_ctrl = 0x880b880b;
        } else {
                ltr_idle_lat_ctrl = 0x90019001;
                ltr_act_lat_ctrl = 0x88018801;
        }

        rtw89_write32(rtwdev, R_BE_LTR_LATENCY_IDX3_V1, ltr_idle_lat_ctrl);
        rtw89_write32(rtwdev, R_BE_LTR_LATENCY_IDX1_V1, ltr_act_lat_ctrl);
        rtw89_write32(rtwdev, R_BE_LTR_LATENCY_IDX0_V1, 0);
        rtw89_write32(rtwdev, R_BE_LTR_DECISION_CTRL_V1, dec_ctrl);
        rtw89_write32(rtwdev, R_BE_LTR_CFG_0, cfg0);
        rtw89_write32(rtwdev, R_BE_LTR_CFG_1, cfg1);
        rtw89_write32(rtwdev, R_BE_LTR_CTRL_0, ctrl0);

        return 0;
}
EXPORT_SYMBOL(rtw89_pci_ltr_set_v2);

static void rtw89_pci_configure_mit_be(struct rtw89_dev *rtwdev)
{
        u32 cnt;
        u32 val;

        rtw89_write32_mask(rtwdev, R_BE_PCIE_MIT0_TMR,
                           B_BE_PCIE_MIT0_RX_TMR_MASK, BE_MIT0_TMR_UNIT_1MS);

        val = rtw89_read32(rtwdev, R_BE_PCIE_MIT0_CNT);
        cnt = min_t(u32, U8_MAX, RTW89_PCI_RXBD_NUM_MAX / 2);
        val = u32_replace_bits(val, cnt, B_BE_PCIE_RX_MIT0_CNT_MASK);
        val = u32_replace_bits(val, 2, B_BE_PCIE_RX_MIT0_TMR_CNT_MASK);
        rtw89_write32(rtwdev, R_BE_PCIE_MIT0_CNT, val);
}

static int rtw89_pci_ops_mac_post_init_be(struct rtw89_dev *rtwdev)
{
        const struct rtw89_pci_info *info = rtwdev->pci_info;
        int ret;

        ret = info->ltr_set(rtwdev, true);
        if (ret) {
                rtw89_err(rtwdev, "pci ltr set fail\n");
                return ret;
        }

        rtw89_pci_ctrl_trxdma_pcie_be(rtwdev, MAC_AX_PCIE_IGNORE,
                                      MAC_AX_PCIE_IGNORE, MAC_AX_PCIE_ENABLE);
        rtw89_pci_ctrl_wpdma_pcie_be(rtwdev, true);
        rtw89_pci_ctrl_txdma_ch_be(rtwdev, true);
        rtw89_pci_ctrl_txdma_fw_ch_be(rtwdev, true);
        rtw89_pci_configure_mit_be(rtwdev);

        return 0;
}

static int rtw89_pci_poll_io_idle_be(struct rtw89_dev *rtwdev)
{
        u32 sts;
        int ret;

        ret = read_poll_timeout_atomic(rtw89_read32, sts,
                                       !(sts & B_BE_HAXI_MST_BUSY),
                                       10, 1000, false, rtwdev,
                                       R_BE_HAXI_DMA_BUSY1);
        if (ret) {
                rtw89_err(rtwdev, "pci dmach busy1 0x%X\n", sts);
                return ret;
        }

        return 0;
}

static int rtw89_pci_lv1rst_stop_dma_be(struct rtw89_dev *rtwdev)
{
        int ret;

        rtw89_pci_ctrl_dma_all(rtwdev, false);
        ret = rtw89_pci_poll_io_idle_be(rtwdev);
        if (!ret)
                return 0;

        rtw89_debug(rtwdev, RTW89_DBG_HCI,
                    "[PCIe] poll_io_idle fail; reset hci dma trx\n");

        rtw89_mac_ctrl_hci_dma_trx(rtwdev, false);
        rtw89_mac_ctrl_hci_dma_trx(rtwdev, true);

        return rtw89_pci_poll_io_idle_be(rtwdev);
}

static int rtw89_pci_lv1rst_start_dma_be(struct rtw89_dev *rtwdev)
{
        int ret;

        rtw89_mac_ctrl_hci_dma_trx(rtwdev, false);
        rtw89_mac_ctrl_hci_dma_trx(rtwdev, true);
        rtw89_pci_clr_idx_all(rtwdev);

        ret = rtw89_pci_rst_bdram_be(rtwdev);
        if (ret)
                return ret;

        rtw89_pci_ctrl_dma_all(rtwdev, true);
        return 0;
}

static void rtw89_pci_disable_eq_be(struct rtw89_dev *rtwdev)
{
        u32 backup_aspm, phy_offset;
        u16 oobs_val, offset_cal;
        u16 g1_oobs, g2_oobs;
        u8 gen;

        if (rtwdev->chip->chip_id != RTL8922A)
                return;

        g1_oobs = rtw89_read16_mask(rtwdev, R_RAC_DIRECT_OFFSET_BE_LANE0_G1 +
                                            RAC_ANA09 * RAC_MULT, BAC_OOBS_SEL);
        g2_oobs = rtw89_read16_mask(rtwdev, R_RAC_DIRECT_OFFSET_BE_LANE0_G2 +
                                            RAC_ANA09 * RAC_MULT, BAC_OOBS_SEL);
        if (g1_oobs && g2_oobs)
                return;

        backup_aspm = rtw89_read32(rtwdev, R_BE_PCIE_MIX_CFG);
        rtw89_write32_clr(rtwdev, R_BE_PCIE_MIX_CFG, B_BE_RTK_ASPM_CTRL_MASK);

        /* offset K */
        for (gen = 1; gen <= 2; gen++) {
                phy_offset = gen == 1 ? R_RAC_DIRECT_OFFSET_BE_LANE0_G1 :
                                        R_RAC_DIRECT_OFFSET_BE_LANE0_G2;

                rtw89_write16_clr(rtwdev, phy_offset + RAC_ANA19 * RAC_MULT,
                                  B_PCIE_BIT_RD_SEL);
        }

        offset_cal = rtw89_read16_mask(rtwdev, R_RAC_DIRECT_OFFSET_BE_LANE0_G1 +
                                               RAC_ANA1F * RAC_MULT, OFFSET_CAL_MASK);

        for (gen = 1; gen <= 2; gen++) {
                phy_offset = gen == 1 ? R_RAC_DIRECT_OFFSET_BE_LANE0_G1 :
                                        R_RAC_DIRECT_OFFSET_BE_LANE0_G2;

                rtw89_write16_mask(rtwdev, phy_offset + RAC_ANA0B * RAC_MULT,
                                   MANUAL_LVL_MASK, offset_cal);
                rtw89_write16_clr(rtwdev, phy_offset + RAC_ANA0D * RAC_MULT,
                                  OFFSET_CAL_MODE);
        }

        /* OOBS */
        for (gen = 1; gen <= 2; gen++) {
                phy_offset = gen == 1 ? R_RAC_DIRECT_OFFSET_BE_LANE0_G1 :
                                        R_RAC_DIRECT_OFFSET_BE_LANE0_G2;

                rtw89_write16_set(rtwdev, phy_offset + RAC_ANA0D * RAC_MULT,
                                  BAC_RX_TEST_EN);
                rtw89_write16_mask(rtwdev, phy_offset + RAC_ANA10 * RAC_MULT,
                                   ADDR_SEL_MASK, ADDR_SEL_VAL);
                rtw89_write16_clr(rtwdev, phy_offset + RAC_ANA10 * RAC_MULT,
                                  B_PCIE_BIT_PINOUT_DIS);
                rtw89_write16_set(rtwdev, phy_offset + RAC_ANA19 * RAC_MULT,
                                  B_PCIE_BIT_RD_SEL);
        }

        oobs_val = rtw89_read16_mask(rtwdev, R_RAC_DIRECT_OFFSET_BE_LANE0_G1 +
                                             RAC_ANA1F * RAC_MULT, OOBS_LEVEL_MASK);

        for (gen = 1; gen <= 2; gen++) {
                phy_offset = gen == 1 ? R_RAC_DIRECT_OFFSET_BE_LANE0_G1 :
                                        R_RAC_DIRECT_OFFSET_BE_LANE0_G2;

                rtw89_write16_mask(rtwdev, phy_offset + RAC_ANA03 * RAC_MULT,
                                   OOBS_SEN_MASK, oobs_val);
                rtw89_write16_set(rtwdev, phy_offset + RAC_ANA09 * RAC_MULT,
                                  BAC_OOBS_SEL);
        }

        rtw89_write32(rtwdev, R_BE_PCIE_MIX_CFG, backup_aspm);
}

static int __maybe_unused rtw89_pci_suspend_be(struct device *dev)
{
        struct ieee80211_hw *hw = dev_get_drvdata(dev);
        struct rtw89_dev *rtwdev = hw->priv;
        u32 val32;

        rtw89_write32_set(rtwdev, R_BE_RSV_CTRL, B_BE_WLOCK_1C_BIT6);
        rtw89_write32_set(rtwdev, R_BE_RSV_CTRL, B_BE_R_DIS_PRST);
        rtw89_write32_clr(rtwdev, R_BE_RSV_CTRL, B_BE_WLOCK_1C_BIT6);
        rtw89_write32_set(rtwdev, R_BE_PCIE_FRZ_CLK, B_BE_PCIE_FRZ_REG_RST);

        val32 = rtw89_read32(rtwdev, R_BE_SER_PL1_CTRL);
        if (val32 & B_BE_PL1_SER_PL1_EN) {
                val32 = u32_replace_bits(val32, PCIE_SER_WOW_TIMER_UNIT,
                                         B_BE_PL1_TIMER_UNIT_MASK);
                rtw89_write32(rtwdev, R_BE_SER_PL1_CTRL, val32);

                if (rtwdev->chip->chip_id == RTL8922A)
                        rtw89_write32_clr(rtwdev, R_BE_REG_PL1_MASK,
                                          B_BE_SER_PM_MASTER_IMR);
        }

        return 0;
}

static int __maybe_unused rtw89_pci_resume_be(struct device *dev)
{
        struct ieee80211_hw *hw = dev_get_drvdata(dev);
        struct rtw89_dev *rtwdev = hw->priv;
        u32 polling;
        u32 val32;
        u16 val16;
        int ret;

        rtw89_write32_set(rtwdev, R_BE_RSV_CTRL, B_BE_WLOCK_1C_BIT6);
        rtw89_write32_clr(rtwdev, R_BE_RSV_CTRL, B_BE_R_DIS_PRST);
        rtw89_write32_clr(rtwdev, R_BE_RSV_CTRL, B_BE_WLOCK_1C_BIT6);
        rtw89_write32_clr(rtwdev, R_BE_PCIE_FRZ_CLK, B_BE_PCIE_FRZ_REG_RST);

        val32 = rtw89_read32(rtwdev, R_BE_SER_PL1_CTRL);
        if (!(val32 & B_BE_PL1_SER_PL1_EN))
                goto clear_phy_isr;

        rtw89_write32_clr(rtwdev, R_BE_SER_PL1_CTRL, B_BE_PL1_SER_PL1_EN);
        if (rtwdev->chip->chip_id == RTL8922D)
                rtw89_write32(rtwdev, R_BE_REG_PL1_ISR, B_PCIE_SER_ALL_ISR);

        ret = read_poll_timeout_atomic(rtw89_read32, polling, !polling, 1, 1000,
                                       false, rtwdev, R_BE_REG_PL1_ISR);
        if (ret)
                rtw89_warn(rtwdev, "[ERR] PCIE SER clear polling fail\n");

        if (rtwdev->chip->chip_id == RTL8922A)
                rtw89_write32_set(rtwdev, R_BE_REG_PL1_MASK,
                                  B_BE_SER_PM_MASTER_IMR | B_BE_SER_PCLKREQ_ACK_MASK);

        val32 = rtw89_read32(rtwdev, R_BE_SER_PL1_CTRL);
        val32 = u32_replace_bits(val32, PCIE_SER_TIMER_UNIT, B_BE_PL1_TIMER_UNIT_MASK);
        val32 |= B_BE_PL1_SER_PL1_EN;
        rtw89_write32(rtwdev, R_BE_SER_PL1_CTRL, val32);

clear_phy_isr:
        if (rtwdev->chip->chip_id == RTL8922D) {
                val16 = rtw89_read16(rtwdev, RAC_DIRECT_OFFESET_L0_G2 +
                                             RAC_ANA41 * RAC_MULT);
                if (val16 & PHY_ERR_FLAG_EN) {
                        rtw89_write16_clr(rtwdev, RAC_DIRECT_OFFESET_L0_G2 +
                                                  RAC_ANA41 * RAC_MULT, PHY_ERR_FLAG_EN);
                        rtw89_write16_set(rtwdev, RAC_DIRECT_OFFESET_L0_G2 +
                                                  RAC_ANA41 * RAC_MULT, PHY_ERR_FLAG_EN);
                }

                val16 = rtw89_read16(rtwdev, RAC_DIRECT_OFFESET_L0_G1 +
                                             RAC_ANA41 * RAC_MULT);
                if (val16 & PHY_ERR_FLAG_EN) {
                        rtw89_write16_clr(rtwdev, RAC_DIRECT_OFFESET_L0_G1 +
                                                  RAC_ANA41 * RAC_MULT, PHY_ERR_FLAG_EN);
                        rtw89_write16_set(rtwdev, RAC_DIRECT_OFFESET_L0_G1 +
                                                  RAC_ANA41 * RAC_MULT, PHY_ERR_FLAG_EN);
                }

                rtw89_write32(rtwdev, R_BE_PCIE_HCI2FW_ISR, 0xFFFFFFFF);
        }

        rtw89_pci_basic_cfg(rtwdev, true);

        return 0;
}

SIMPLE_DEV_PM_OPS(rtw89_pm_ops_be, rtw89_pci_suspend_be, rtw89_pci_resume_be);
EXPORT_SYMBOL(rtw89_pm_ops_be);

const struct rtw89_pci_isr_def rtw89_pci_isr_be = {
        .isr_rdu = B_BE_RDU_CH1_INT_V1 | B_BE_RDU_CH0_INT_V1,
        .isr_halt_c2h = B_BE_HALT_C2H_INT,
        .isr_wdt_timeout = B_BE_WDT_TIMEOUT_INT,
        .isr_sps_ocp = 0,
        .isr_clear_rpq = {R_BE_PCIE_DMA_ISR, B_BE_PCIE_RX_RPQ0_ISR_V1},
        .isr_clear_rxq = {R_BE_PCIE_DMA_ISR, B_BE_PCIE_RX_RX0P2_ISR_V1},
};
EXPORT_SYMBOL(rtw89_pci_isr_be);

const struct rtw89_pci_isr_def rtw89_pci_isr_be_v1 = {
        .isr_rdu = B_BE_PCIE_RDU_CH1_INT | B_BE_PCIE_RDU_CH0_INT,
        .isr_halt_c2h = B_BE_HALT_C2H_INT,
        .isr_wdt_timeout = B_BE_WDT_TIMEOUT_INT,
        .isr_sps_ocp = B_BE_SPS_OCP_INT | B_BE_SPSANA_OCP_INT,
        .isr_clear_rpq = {R_BE_PCIE_DMA_ISR, B_BE_PCIE_RX_RPQ0_ISR_V1},
        .isr_clear_rxq = {R_BE_PCIE_DMA_ISR, B_BE_PCIE_RX_RX0P2_ISR_V1},
};
EXPORT_SYMBOL(rtw89_pci_isr_be_v1);

const struct rtw89_pci_gen_def rtw89_pci_gen_be = {
        .mac_pre_init = rtw89_pci_ops_mac_pre_init_be,
        .mac_pre_deinit = rtw89_pci_ops_mac_pre_deinit_be,
        .mac_post_init = rtw89_pci_ops_mac_post_init_be,

        .clr_idx_all = rtw89_pci_clr_idx_all_be,
        .rst_bdram = rtw89_pci_rst_bdram_be,

        .lv1rst_stop_dma = rtw89_pci_lv1rst_stop_dma_be,
        .lv1rst_start_dma = rtw89_pci_lv1rst_start_dma_be,

        .ctrl_txdma_ch = rtw89_pci_ctrl_txdma_ch_be,
        .ctrl_txdma_fw_ch = rtw89_pci_ctrl_txdma_fw_ch_be,
        .poll_txdma_ch_idle = rtw89_pci_poll_txdma_ch_idle_be,

        .aspm_set = rtw89_pci_aspm_set_be,
        .clkreq_set = rtw89_pci_clkreq_set_be,
        .l1ss_set = rtw89_pci_l1ss_set_be,

        .disable_eq = rtw89_pci_disable_eq_be,
        .power_wake = _patch_pcie_power_wake_be,
};
EXPORT_SYMBOL(rtw89_pci_gen_be);