1e3037485SYan-Hsuan Chuang // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause 2e3037485SYan-Hsuan Chuang /* Copyright(c) 2018-2019 Realtek Corporation 3e3037485SYan-Hsuan Chuang */ 4e3037485SYan-Hsuan Chuang 5e3037485SYan-Hsuan Chuang #include "main.h" 6e3037485SYan-Hsuan Chuang #include "mac.h" 7e3037485SYan-Hsuan Chuang #include "reg.h" 8e3037485SYan-Hsuan Chuang #include "fw.h" 9e3037485SYan-Hsuan Chuang #include "debug.h" 10e3037485SYan-Hsuan Chuang 11e3037485SYan-Hsuan Chuang void rtw_set_channel_mac(struct rtw_dev *rtwdev, u8 channel, u8 bw, 12e3037485SYan-Hsuan Chuang u8 primary_ch_idx) 13e3037485SYan-Hsuan Chuang { 14e3037485SYan-Hsuan Chuang u8 txsc40 = 0, txsc20 = 0; 15e3037485SYan-Hsuan Chuang u32 value32; 16e3037485SYan-Hsuan Chuang u8 value8; 17e3037485SYan-Hsuan Chuang 18e3037485SYan-Hsuan Chuang txsc20 = primary_ch_idx; 1973a2d0b8SPing-Ke Shih if (bw == RTW_CHANNEL_WIDTH_80) { 2040fb04b2SPing-Ke Shih if (txsc20 == RTW_SC_20_UPPER || txsc20 == RTW_SC_20_UPMOST) 2140fb04b2SPing-Ke Shih txsc40 = RTW_SC_40_UPPER; 22e3037485SYan-Hsuan Chuang else 2340fb04b2SPing-Ke Shih txsc40 = RTW_SC_40_LOWER; 2473a2d0b8SPing-Ke Shih } 25e3037485SYan-Hsuan Chuang rtw_write8(rtwdev, REG_DATA_SC, 26e3037485SYan-Hsuan Chuang BIT_TXSC_20M(txsc20) | BIT_TXSC_40M(txsc40)); 27e3037485SYan-Hsuan Chuang 28e3037485SYan-Hsuan Chuang value32 = rtw_read32(rtwdev, REG_WMAC_TRXPTCL_CTL); 29e3037485SYan-Hsuan Chuang value32 &= ~BIT_RFMOD; 30e3037485SYan-Hsuan Chuang switch (bw) { 31e3037485SYan-Hsuan Chuang case RTW_CHANNEL_WIDTH_80: 32e3037485SYan-Hsuan Chuang value32 |= BIT_RFMOD_80M; 33e3037485SYan-Hsuan Chuang break; 34e3037485SYan-Hsuan Chuang case RTW_CHANNEL_WIDTH_40: 35e3037485SYan-Hsuan Chuang value32 |= BIT_RFMOD_40M; 36e3037485SYan-Hsuan Chuang break; 37e3037485SYan-Hsuan Chuang case RTW_CHANNEL_WIDTH_20: 38e3037485SYan-Hsuan Chuang default: 39e3037485SYan-Hsuan Chuang break; 40e3037485SYan-Hsuan Chuang } 41e3037485SYan-Hsuan Chuang rtw_write32(rtwdev, REG_WMAC_TRXPTCL_CTL, value32); 42e3037485SYan-Hsuan Chuang 435f028a9cSPing-Ke Shih if (rtw_chip_wcpu_11n(rtwdev)) 445f028a9cSPing-Ke Shih return; 455f028a9cSPing-Ke Shih 46e3037485SYan-Hsuan Chuang value32 = rtw_read32(rtwdev, REG_AFE_CTRL1) & ~(BIT_MAC_CLK_SEL); 47e3037485SYan-Hsuan Chuang value32 |= (MAC_CLK_HW_DEF_80M << BIT_SHIFT_MAC_CLK_SEL); 48e3037485SYan-Hsuan Chuang rtw_write32(rtwdev, REG_AFE_CTRL1, value32); 49e3037485SYan-Hsuan Chuang 50e3037485SYan-Hsuan Chuang rtw_write8(rtwdev, REG_USTIME_TSF, MAC_CLK_SPEED); 51e3037485SYan-Hsuan Chuang rtw_write8(rtwdev, REG_USTIME_EDCA, MAC_CLK_SPEED); 52e3037485SYan-Hsuan Chuang 53e3037485SYan-Hsuan Chuang value8 = rtw_read8(rtwdev, REG_CCK_CHECK); 54e3037485SYan-Hsuan Chuang value8 = value8 & ~BIT_CHECK_CCK_EN; 558575b534SYan-Hsuan Chuang if (IS_CH_5G_BAND(channel)) 56e3037485SYan-Hsuan Chuang value8 |= BIT_CHECK_CCK_EN; 57e3037485SYan-Hsuan Chuang rtw_write8(rtwdev, REG_CCK_CHECK, value8); 58e3037485SYan-Hsuan Chuang } 59449be866SZong-Zhe Yang EXPORT_SYMBOL(rtw_set_channel_mac); 60e3037485SYan-Hsuan Chuang 61e3037485SYan-Hsuan Chuang static int rtw_mac_pre_system_cfg(struct rtw_dev *rtwdev) 62e3037485SYan-Hsuan Chuang { 63e3037485SYan-Hsuan Chuang u32 value32; 64e3037485SYan-Hsuan Chuang u8 value8; 65e3037485SYan-Hsuan Chuang 66e3037485SYan-Hsuan Chuang rtw_write8(rtwdev, REG_RSV_CTRL, 0); 67e3037485SYan-Hsuan Chuang 684e223a5fSPing-Ke Shih if (rtw_chip_wcpu_11n(rtwdev)) { 694e223a5fSPing-Ke Shih if (rtw_read32(rtwdev, REG_SYS_CFG1) & BIT_LDO) 704e223a5fSPing-Ke Shih rtw_write8(rtwdev, REG_LDO_SWR_CTRL, LDO_SEL); 714e223a5fSPing-Ke Shih else 724e223a5fSPing-Ke Shih rtw_write8(rtwdev, REG_LDO_SWR_CTRL, SPS_SEL); 734e223a5fSPing-Ke Shih return 0; 744e223a5fSPing-Ke Shih } 754e223a5fSPing-Ke Shih 76e3037485SYan-Hsuan Chuang switch (rtw_hci_type(rtwdev)) { 77e3037485SYan-Hsuan Chuang case RTW_HCI_TYPE_PCIE: 78fc6234d7SKevin Lo rtw_write32_set(rtwdev, REG_HCI_OPT_CTRL, BIT_USB_SUS_DIS); 79e3037485SYan-Hsuan Chuang break; 80e3037485SYan-Hsuan Chuang case RTW_HCI_TYPE_USB: 81e3037485SYan-Hsuan Chuang break; 82e3037485SYan-Hsuan Chuang default: 83e3037485SYan-Hsuan Chuang return -EINVAL; 84e3037485SYan-Hsuan Chuang } 85e3037485SYan-Hsuan Chuang 86e3037485SYan-Hsuan Chuang /* config PIN Mux */ 87e3037485SYan-Hsuan Chuang value32 = rtw_read32(rtwdev, REG_PAD_CTRL1); 88e3037485SYan-Hsuan Chuang value32 |= BIT_PAPE_WLBT_SEL | BIT_LNAON_WLBT_SEL; 89e3037485SYan-Hsuan Chuang rtw_write32(rtwdev, REG_PAD_CTRL1, value32); 90e3037485SYan-Hsuan Chuang 91e3037485SYan-Hsuan Chuang value32 = rtw_read32(rtwdev, REG_LED_CFG); 92e3037485SYan-Hsuan Chuang value32 &= ~(BIT_PAPE_SEL_EN | BIT_LNAON_SEL_EN); 93e3037485SYan-Hsuan Chuang rtw_write32(rtwdev, REG_LED_CFG, value32); 94e3037485SYan-Hsuan Chuang 95e3037485SYan-Hsuan Chuang value32 = rtw_read32(rtwdev, REG_GPIO_MUXCFG); 96e3037485SYan-Hsuan Chuang value32 |= BIT_WLRFE_4_5_EN; 97e3037485SYan-Hsuan Chuang rtw_write32(rtwdev, REG_GPIO_MUXCFG, value32); 98e3037485SYan-Hsuan Chuang 99e3037485SYan-Hsuan Chuang /* disable BB/RF */ 100e3037485SYan-Hsuan Chuang value8 = rtw_read8(rtwdev, REG_SYS_FUNC_EN); 101e3037485SYan-Hsuan Chuang value8 &= ~(BIT_FEN_BB_RSTB | BIT_FEN_BB_GLB_RST); 102e3037485SYan-Hsuan Chuang rtw_write8(rtwdev, REG_SYS_FUNC_EN, value8); 103e3037485SYan-Hsuan Chuang 104e3037485SYan-Hsuan Chuang value8 = rtw_read8(rtwdev, REG_RF_CTRL); 105e3037485SYan-Hsuan Chuang value8 &= ~(BIT_RF_SDM_RSTB | BIT_RF_RSTB | BIT_RF_EN); 106e3037485SYan-Hsuan Chuang rtw_write8(rtwdev, REG_RF_CTRL, value8); 107e3037485SYan-Hsuan Chuang 108e3037485SYan-Hsuan Chuang value32 = rtw_read32(rtwdev, REG_WLRF1); 109e3037485SYan-Hsuan Chuang value32 &= ~BIT_WLRF1_BBRF_EN; 110e3037485SYan-Hsuan Chuang rtw_write32(rtwdev, REG_WLRF1, value32); 111e3037485SYan-Hsuan Chuang 112e3037485SYan-Hsuan Chuang return 0; 113e3037485SYan-Hsuan Chuang } 114e3037485SYan-Hsuan Chuang 115fd9ead38SPing-Ke Shih static bool do_pwr_poll_cmd(struct rtw_dev *rtwdev, u32 addr, u32 mask, u32 target) 116fd9ead38SPing-Ke Shih { 11786fbf264SBrian Norris u32 val; 118fd9ead38SPing-Ke Shih 119fd9ead38SPing-Ke Shih target &= mask; 120fd9ead38SPing-Ke Shih 12186fbf264SBrian Norris return read_poll_timeout_atomic(rtw_read8, val, (val & mask) == target, 12286fbf264SBrian Norris 50, 50 * RTW_PWR_POLLING_CNT, false, 12386fbf264SBrian Norris rtwdev, addr) == 0; 124fd9ead38SPing-Ke Shih } 125fd9ead38SPing-Ke Shih 126e3037485SYan-Hsuan Chuang static int rtw_pwr_cmd_polling(struct rtw_dev *rtwdev, 127d49f2c50SJoe Perches const struct rtw_pwr_seq_cmd *cmd) 128e3037485SYan-Hsuan Chuang { 129e3037485SYan-Hsuan Chuang u8 value; 130e3037485SYan-Hsuan Chuang u32 offset; 131e3037485SYan-Hsuan Chuang 132e3037485SYan-Hsuan Chuang if (cmd->base == RTW_PWR_ADDR_SDIO) 133e3037485SYan-Hsuan Chuang offset = cmd->offset | SDIO_LOCAL_OFFSET; 134e3037485SYan-Hsuan Chuang else 135e3037485SYan-Hsuan Chuang offset = cmd->offset; 136e3037485SYan-Hsuan Chuang 137fd9ead38SPing-Ke Shih if (do_pwr_poll_cmd(rtwdev, offset, cmd->mask, cmd->value)) 138e3037485SYan-Hsuan Chuang return 0; 1394e223a5fSPing-Ke Shih 140fd9ead38SPing-Ke Shih if (rtw_hci_type(rtwdev) != RTW_HCI_TYPE_PCIE) 141fd9ead38SPing-Ke Shih goto err; 142fd9ead38SPing-Ke Shih 143fd9ead38SPing-Ke Shih /* if PCIE, toggle BIT_PFM_WOWL and try again */ 144fd9ead38SPing-Ke Shih value = rtw_read8(rtwdev, REG_SYS_PW_CTRL); 145fd9ead38SPing-Ke Shih if (rtwdev->chip->id == RTW_CHIP_TYPE_8723D) 146fd9ead38SPing-Ke Shih rtw_write8(rtwdev, REG_SYS_PW_CTRL, value & ~BIT_PFM_WOWL); 147fd9ead38SPing-Ke Shih rtw_write8(rtwdev, REG_SYS_PW_CTRL, value | BIT_PFM_WOWL); 148fd9ead38SPing-Ke Shih rtw_write8(rtwdev, REG_SYS_PW_CTRL, value & ~BIT_PFM_WOWL); 149fd9ead38SPing-Ke Shih if (rtwdev->chip->id == RTW_CHIP_TYPE_8723D) 150fd9ead38SPing-Ke Shih rtw_write8(rtwdev, REG_SYS_PW_CTRL, value | BIT_PFM_WOWL); 151fd9ead38SPing-Ke Shih 152fd9ead38SPing-Ke Shih if (do_pwr_poll_cmd(rtwdev, offset, cmd->mask, cmd->value)) 153fd9ead38SPing-Ke Shih return 0; 154fd9ead38SPing-Ke Shih 155fd9ead38SPing-Ke Shih err: 156fd9ead38SPing-Ke Shih rtw_err(rtwdev, "failed to poll offset=0x%x mask=0x%x value=0x%x\n", 157fd9ead38SPing-Ke Shih offset, cmd->mask, cmd->value); 158e3037485SYan-Hsuan Chuang return -EBUSY; 159e3037485SYan-Hsuan Chuang } 160e3037485SYan-Hsuan Chuang 161e3037485SYan-Hsuan Chuang static int rtw_sub_pwr_seq_parser(struct rtw_dev *rtwdev, u8 intf_mask, 162d49f2c50SJoe Perches u8 cut_mask, 163d49f2c50SJoe Perches const struct rtw_pwr_seq_cmd *cmd) 164e3037485SYan-Hsuan Chuang { 165d49f2c50SJoe Perches const struct rtw_pwr_seq_cmd *cur_cmd; 166e3037485SYan-Hsuan Chuang u32 offset; 167e3037485SYan-Hsuan Chuang u8 value; 168e3037485SYan-Hsuan Chuang 169e3037485SYan-Hsuan Chuang for (cur_cmd = cmd; cur_cmd->cmd != RTW_PWR_CMD_END; cur_cmd++) { 170e3037485SYan-Hsuan Chuang if (!(cur_cmd->intf_mask & intf_mask) || 171e3037485SYan-Hsuan Chuang !(cur_cmd->cut_mask & cut_mask)) 172e3037485SYan-Hsuan Chuang continue; 173e3037485SYan-Hsuan Chuang 174e3037485SYan-Hsuan Chuang switch (cur_cmd->cmd) { 175e3037485SYan-Hsuan Chuang case RTW_PWR_CMD_WRITE: 176e3037485SYan-Hsuan Chuang offset = cur_cmd->offset; 177e3037485SYan-Hsuan Chuang 178e3037485SYan-Hsuan Chuang if (cur_cmd->base == RTW_PWR_ADDR_SDIO) 179e3037485SYan-Hsuan Chuang offset |= SDIO_LOCAL_OFFSET; 180e3037485SYan-Hsuan Chuang 181e3037485SYan-Hsuan Chuang value = rtw_read8(rtwdev, offset); 182e3037485SYan-Hsuan Chuang value &= ~cur_cmd->mask; 183e3037485SYan-Hsuan Chuang value |= (cur_cmd->value & cur_cmd->mask); 184e3037485SYan-Hsuan Chuang rtw_write8(rtwdev, offset, value); 185e3037485SYan-Hsuan Chuang break; 186e3037485SYan-Hsuan Chuang case RTW_PWR_CMD_POLLING: 187e3037485SYan-Hsuan Chuang if (rtw_pwr_cmd_polling(rtwdev, cur_cmd)) 188e3037485SYan-Hsuan Chuang return -EBUSY; 189e3037485SYan-Hsuan Chuang break; 190e3037485SYan-Hsuan Chuang case RTW_PWR_CMD_DELAY: 191e3037485SYan-Hsuan Chuang if (cur_cmd->value == RTW_PWR_DELAY_US) 192e3037485SYan-Hsuan Chuang udelay(cur_cmd->offset); 193e3037485SYan-Hsuan Chuang else 194e3037485SYan-Hsuan Chuang mdelay(cur_cmd->offset); 195e3037485SYan-Hsuan Chuang break; 196e3037485SYan-Hsuan Chuang case RTW_PWR_CMD_READ: 197e3037485SYan-Hsuan Chuang break; 198e3037485SYan-Hsuan Chuang default: 199e3037485SYan-Hsuan Chuang return -EINVAL; 200e3037485SYan-Hsuan Chuang } 201e3037485SYan-Hsuan Chuang } 202e3037485SYan-Hsuan Chuang 203e3037485SYan-Hsuan Chuang return 0; 204e3037485SYan-Hsuan Chuang } 205e3037485SYan-Hsuan Chuang 206e3037485SYan-Hsuan Chuang static int rtw_pwr_seq_parser(struct rtw_dev *rtwdev, 207d49f2c50SJoe Perches const struct rtw_pwr_seq_cmd **cmd_seq) 208e3037485SYan-Hsuan Chuang { 209e3037485SYan-Hsuan Chuang u8 cut_mask; 210e3037485SYan-Hsuan Chuang u8 intf_mask; 211e3037485SYan-Hsuan Chuang u8 cut; 212e3037485SYan-Hsuan Chuang u32 idx = 0; 213d49f2c50SJoe Perches const struct rtw_pwr_seq_cmd *cmd; 214e3037485SYan-Hsuan Chuang int ret; 215e3037485SYan-Hsuan Chuang 216e3037485SYan-Hsuan Chuang cut = rtwdev->hal.cut_version; 217e3037485SYan-Hsuan Chuang cut_mask = cut_version_to_mask(cut); 218e3037485SYan-Hsuan Chuang switch (rtw_hci_type(rtwdev)) { 219e3037485SYan-Hsuan Chuang case RTW_HCI_TYPE_PCIE: 22024d54855SMartin Blumenstingl intf_mask = RTW_PWR_INTF_PCI_MSK; 221e3037485SYan-Hsuan Chuang break; 222e3037485SYan-Hsuan Chuang case RTW_HCI_TYPE_USB: 22324d54855SMartin Blumenstingl intf_mask = RTW_PWR_INTF_USB_MSK; 224e3037485SYan-Hsuan Chuang break; 22596c79da2SMartin Blumenstingl case RTW_HCI_TYPE_SDIO: 22696c79da2SMartin Blumenstingl intf_mask = RTW_PWR_INTF_SDIO_MSK; 22796c79da2SMartin Blumenstingl break; 228e3037485SYan-Hsuan Chuang default: 229e3037485SYan-Hsuan Chuang return -EINVAL; 230e3037485SYan-Hsuan Chuang } 231e3037485SYan-Hsuan Chuang 232e3037485SYan-Hsuan Chuang do { 233e3037485SYan-Hsuan Chuang cmd = cmd_seq[idx]; 234e3037485SYan-Hsuan Chuang if (!cmd) 235e3037485SYan-Hsuan Chuang break; 236e3037485SYan-Hsuan Chuang 237e3037485SYan-Hsuan Chuang ret = rtw_sub_pwr_seq_parser(rtwdev, intf_mask, cut_mask, cmd); 238e3037485SYan-Hsuan Chuang if (ret) 239b7ed9fa2SMartin Blumenstingl return ret; 240e3037485SYan-Hsuan Chuang 241e3037485SYan-Hsuan Chuang idx++; 242e3037485SYan-Hsuan Chuang } while (1); 243e3037485SYan-Hsuan Chuang 244e3037485SYan-Hsuan Chuang return 0; 245e3037485SYan-Hsuan Chuang } 246e3037485SYan-Hsuan Chuang 247e3037485SYan-Hsuan Chuang static int rtw_mac_power_switch(struct rtw_dev *rtwdev, bool pwr_on) 248e3037485SYan-Hsuan Chuang { 249dcbf179cSPing-Ke Shih const struct rtw_chip_info *chip = rtwdev->chip; 250d49f2c50SJoe Perches const struct rtw_pwr_seq_cmd **pwr_seq; 251e3037485SYan-Hsuan Chuang u8 rpwm; 252e3037485SYan-Hsuan Chuang bool cur_pwr; 25315c8e267SMartin Blumenstingl int ret; 254e3037485SYan-Hsuan Chuang 2554e223a5fSPing-Ke Shih if (rtw_chip_wcpu_11ac(rtwdev)) { 256e3037485SYan-Hsuan Chuang rpwm = rtw_read8(rtwdev, rtwdev->hci.rpwm_addr); 257e3037485SYan-Hsuan Chuang 258e3037485SYan-Hsuan Chuang /* Check FW still exist or not */ 259e3037485SYan-Hsuan Chuang if (rtw_read16(rtwdev, REG_MCUFW_CTRL) == 0xC078) { 260e3037485SYan-Hsuan Chuang rpwm = (rpwm ^ BIT_RPWM_TOGGLE) & BIT_RPWM_TOGGLE; 261e3037485SYan-Hsuan Chuang rtw_write8(rtwdev, rtwdev->hci.rpwm_addr, rpwm); 262e3037485SYan-Hsuan Chuang } 2634e223a5fSPing-Ke Shih } 264e3037485SYan-Hsuan Chuang 265e3037485SYan-Hsuan Chuang if (rtw_read8(rtwdev, REG_CR) == 0xea) 266e3037485SYan-Hsuan Chuang cur_pwr = false; 267e3037485SYan-Hsuan Chuang else if (rtw_hci_type(rtwdev) == RTW_HCI_TYPE_USB && 268e3037485SYan-Hsuan Chuang (rtw_read8(rtwdev, REG_SYS_STATUS1 + 1) & BIT(0))) 269e3037485SYan-Hsuan Chuang cur_pwr = false; 270e3037485SYan-Hsuan Chuang else 271e3037485SYan-Hsuan Chuang cur_pwr = true; 272e3037485SYan-Hsuan Chuang 2734e223a5fSPing-Ke Shih if (pwr_on == cur_pwr) 274e3037485SYan-Hsuan Chuang return -EALREADY; 275e3037485SYan-Hsuan Chuang 276*6a925660SMartin Blumenstingl if (!pwr_on) 277*6a925660SMartin Blumenstingl clear_bit(RTW_FLAG_POWERON, rtwdev->flags); 278*6a925660SMartin Blumenstingl 279e3037485SYan-Hsuan Chuang pwr_seq = pwr_on ? chip->pwr_on_seq : chip->pwr_off_seq; 28015c8e267SMartin Blumenstingl ret = rtw_pwr_seq_parser(rtwdev, pwr_seq); 28115c8e267SMartin Blumenstingl if (ret) 28215c8e267SMartin Blumenstingl return ret; 283e3037485SYan-Hsuan Chuang 2844a267bc5SPing-Ke Shih if (pwr_on) 2854a267bc5SPing-Ke Shih set_bit(RTW_FLAG_POWERON, rtwdev->flags); 2864a267bc5SPing-Ke Shih 287e3037485SYan-Hsuan Chuang return 0; 288e3037485SYan-Hsuan Chuang } 289e3037485SYan-Hsuan Chuang 2904e223a5fSPing-Ke Shih static int __rtw_mac_init_system_cfg(struct rtw_dev *rtwdev) 291e3037485SYan-Hsuan Chuang { 292e3037485SYan-Hsuan Chuang u8 sys_func_en = rtwdev->chip->sys_func_en; 293e3037485SYan-Hsuan Chuang u8 value8; 294e3037485SYan-Hsuan Chuang u32 value, tmp; 295e3037485SYan-Hsuan Chuang 296e3037485SYan-Hsuan Chuang value = rtw_read32(rtwdev, REG_CPU_DMEM_CON); 297e3037485SYan-Hsuan Chuang value |= BIT_WL_PLATFORM_RST | BIT_DDMA_EN; 298e3037485SYan-Hsuan Chuang rtw_write32(rtwdev, REG_CPU_DMEM_CON, value); 299e3037485SYan-Hsuan Chuang 300a4835410SYan-Hsuan Chuang rtw_write8_set(rtwdev, REG_SYS_FUNC_EN + 1, sys_func_en); 301e3037485SYan-Hsuan Chuang value8 = (rtw_read8(rtwdev, REG_CR_EXT + 3) & 0xF0) | 0x0C; 302e3037485SYan-Hsuan Chuang rtw_write8(rtwdev, REG_CR_EXT + 3, value8); 303e3037485SYan-Hsuan Chuang 304e3037485SYan-Hsuan Chuang /* disable boot-from-flash for driver's DL FW */ 305e3037485SYan-Hsuan Chuang tmp = rtw_read32(rtwdev, REG_MCUFW_CTRL); 306e3037485SYan-Hsuan Chuang if (tmp & BIT_BOOT_FSPI_EN) { 307e3037485SYan-Hsuan Chuang rtw_write32(rtwdev, REG_MCUFW_CTRL, tmp & (~BIT_BOOT_FSPI_EN)); 308e3037485SYan-Hsuan Chuang value = rtw_read32(rtwdev, REG_GPIO_MUXCFG) & (~BIT_FSPI_EN); 309e3037485SYan-Hsuan Chuang rtw_write32(rtwdev, REG_GPIO_MUXCFG, value); 310e3037485SYan-Hsuan Chuang } 311e3037485SYan-Hsuan Chuang 312e3037485SYan-Hsuan Chuang return 0; 313e3037485SYan-Hsuan Chuang } 314e3037485SYan-Hsuan Chuang 3154e223a5fSPing-Ke Shih static int __rtw_mac_init_system_cfg_legacy(struct rtw_dev *rtwdev) 3164e223a5fSPing-Ke Shih { 3174e223a5fSPing-Ke Shih rtw_write8(rtwdev, REG_CR, 0xff); 3184e223a5fSPing-Ke Shih mdelay(2); 3194e223a5fSPing-Ke Shih rtw_write8(rtwdev, REG_HWSEQ_CTRL, 0x7f); 3204e223a5fSPing-Ke Shih mdelay(2); 3214e223a5fSPing-Ke Shih 3224e223a5fSPing-Ke Shih rtw_write8_set(rtwdev, REG_SYS_CLKR, BIT_WAKEPAD_EN); 3234e223a5fSPing-Ke Shih rtw_write16_clr(rtwdev, REG_GPIO_MUXCFG, BIT_EN_SIC); 3244e223a5fSPing-Ke Shih 3254e223a5fSPing-Ke Shih rtw_write16(rtwdev, REG_CR, 0x2ff); 3264e223a5fSPing-Ke Shih 3274e223a5fSPing-Ke Shih return 0; 3284e223a5fSPing-Ke Shih } 3294e223a5fSPing-Ke Shih 3304e223a5fSPing-Ke Shih static int rtw_mac_init_system_cfg(struct rtw_dev *rtwdev) 3314e223a5fSPing-Ke Shih { 3324e223a5fSPing-Ke Shih if (rtw_chip_wcpu_11n(rtwdev)) 3334e223a5fSPing-Ke Shih return __rtw_mac_init_system_cfg_legacy(rtwdev); 3344e223a5fSPing-Ke Shih 3354e223a5fSPing-Ke Shih return __rtw_mac_init_system_cfg(rtwdev); 3364e223a5fSPing-Ke Shih } 3374e223a5fSPing-Ke Shih 338e3037485SYan-Hsuan Chuang int rtw_mac_power_on(struct rtw_dev *rtwdev) 339e3037485SYan-Hsuan Chuang { 340e3037485SYan-Hsuan Chuang int ret = 0; 341e3037485SYan-Hsuan Chuang 342e3037485SYan-Hsuan Chuang ret = rtw_mac_pre_system_cfg(rtwdev); 343e3037485SYan-Hsuan Chuang if (ret) 344e3037485SYan-Hsuan Chuang goto err; 345e3037485SYan-Hsuan Chuang 346e3037485SYan-Hsuan Chuang ret = rtw_mac_power_switch(rtwdev, true); 347d41673b9SYan-Hsuan Chuang if (ret == -EALREADY) { 348d41673b9SYan-Hsuan Chuang rtw_mac_power_switch(rtwdev, false); 3494a267bc5SPing-Ke Shih 3504a267bc5SPing-Ke Shih ret = rtw_mac_pre_system_cfg(rtwdev); 3514a267bc5SPing-Ke Shih if (ret) 3524a267bc5SPing-Ke Shih goto err; 3534a267bc5SPing-Ke Shih 354d41673b9SYan-Hsuan Chuang ret = rtw_mac_power_switch(rtwdev, true); 355e3037485SYan-Hsuan Chuang if (ret) 356e3037485SYan-Hsuan Chuang goto err; 357d41673b9SYan-Hsuan Chuang } else if (ret) { 358d41673b9SYan-Hsuan Chuang goto err; 359d41673b9SYan-Hsuan Chuang } 360e3037485SYan-Hsuan Chuang 361e3037485SYan-Hsuan Chuang ret = rtw_mac_init_system_cfg(rtwdev); 362e3037485SYan-Hsuan Chuang if (ret) 363e3037485SYan-Hsuan Chuang goto err; 364e3037485SYan-Hsuan Chuang 365e3037485SYan-Hsuan Chuang return 0; 366e3037485SYan-Hsuan Chuang 367e3037485SYan-Hsuan Chuang err: 368e3037485SYan-Hsuan Chuang rtw_err(rtwdev, "mac power on failed"); 369e3037485SYan-Hsuan Chuang return ret; 370e3037485SYan-Hsuan Chuang } 371e3037485SYan-Hsuan Chuang 372e3037485SYan-Hsuan Chuang void rtw_mac_power_off(struct rtw_dev *rtwdev) 373e3037485SYan-Hsuan Chuang { 374e3037485SYan-Hsuan Chuang rtw_mac_power_switch(rtwdev, false); 375e3037485SYan-Hsuan Chuang } 376e3037485SYan-Hsuan Chuang 377e3037485SYan-Hsuan Chuang static bool check_firmware_size(const u8 *data, u32 size) 378e3037485SYan-Hsuan Chuang { 379cc20a713SPing-Ke Shih const struct rtw_fw_hdr *fw_hdr = (const struct rtw_fw_hdr *)data; 380e3037485SYan-Hsuan Chuang u32 dmem_size; 381e3037485SYan-Hsuan Chuang u32 imem_size; 382e3037485SYan-Hsuan Chuang u32 emem_size; 383e3037485SYan-Hsuan Chuang u32 real_size; 384e3037485SYan-Hsuan Chuang 385cc20a713SPing-Ke Shih dmem_size = le32_to_cpu(fw_hdr->dmem_size); 386cc20a713SPing-Ke Shih imem_size = le32_to_cpu(fw_hdr->imem_size); 387cc20a713SPing-Ke Shih emem_size = (fw_hdr->mem_usage & BIT(4)) ? 388cc20a713SPing-Ke Shih le32_to_cpu(fw_hdr->emem_size) : 0; 389e3037485SYan-Hsuan Chuang 390e3037485SYan-Hsuan Chuang dmem_size += FW_HDR_CHKSUM_SIZE; 391e3037485SYan-Hsuan Chuang imem_size += FW_HDR_CHKSUM_SIZE; 392e3037485SYan-Hsuan Chuang emem_size += emem_size ? FW_HDR_CHKSUM_SIZE : 0; 393e3037485SYan-Hsuan Chuang real_size = FW_HDR_SIZE + dmem_size + imem_size + emem_size; 394e3037485SYan-Hsuan Chuang if (real_size != size) 395e3037485SYan-Hsuan Chuang return false; 396e3037485SYan-Hsuan Chuang 397e3037485SYan-Hsuan Chuang return true; 398e3037485SYan-Hsuan Chuang } 399e3037485SYan-Hsuan Chuang 400e3037485SYan-Hsuan Chuang static void wlan_cpu_enable(struct rtw_dev *rtwdev, bool enable) 401e3037485SYan-Hsuan Chuang { 402e3037485SYan-Hsuan Chuang if (enable) { 403e3037485SYan-Hsuan Chuang /* cpu io interface enable */ 404e3037485SYan-Hsuan Chuang rtw_write8_set(rtwdev, REG_RSV_CTRL + 1, BIT_WLMCU_IOIF); 405e3037485SYan-Hsuan Chuang 406e3037485SYan-Hsuan Chuang /* cpu enable */ 407e3037485SYan-Hsuan Chuang rtw_write8_set(rtwdev, REG_SYS_FUNC_EN + 1, BIT_FEN_CPUEN); 408e3037485SYan-Hsuan Chuang } else { 409e3037485SYan-Hsuan Chuang /* cpu io interface disable */ 410e3037485SYan-Hsuan Chuang rtw_write8_clr(rtwdev, REG_SYS_FUNC_EN + 1, BIT_FEN_CPUEN); 411e3037485SYan-Hsuan Chuang 412e3037485SYan-Hsuan Chuang /* cpu disable */ 413e3037485SYan-Hsuan Chuang rtw_write8_clr(rtwdev, REG_RSV_CTRL + 1, BIT_WLMCU_IOIF); 414e3037485SYan-Hsuan Chuang } 415e3037485SYan-Hsuan Chuang } 416e3037485SYan-Hsuan Chuang 417e3037485SYan-Hsuan Chuang #define DLFW_RESTORE_REG_NUM 6 418e3037485SYan-Hsuan Chuang 419e3037485SYan-Hsuan Chuang static void download_firmware_reg_backup(struct rtw_dev *rtwdev, 420e3037485SYan-Hsuan Chuang struct rtw_backup_info *bckp) 421e3037485SYan-Hsuan Chuang { 422e3037485SYan-Hsuan Chuang u8 tmp; 423e3037485SYan-Hsuan Chuang u8 bckp_idx = 0; 424e3037485SYan-Hsuan Chuang 425e3037485SYan-Hsuan Chuang /* set HIQ to hi priority */ 426e3037485SYan-Hsuan Chuang bckp[bckp_idx].len = 1; 427e3037485SYan-Hsuan Chuang bckp[bckp_idx].reg = REG_TXDMA_PQ_MAP + 1; 428e3037485SYan-Hsuan Chuang bckp[bckp_idx].val = rtw_read8(rtwdev, REG_TXDMA_PQ_MAP + 1); 429e3037485SYan-Hsuan Chuang bckp_idx++; 430e3037485SYan-Hsuan Chuang tmp = RTW_DMA_MAPPING_HIGH << 6; 431e3037485SYan-Hsuan Chuang rtw_write8(rtwdev, REG_TXDMA_PQ_MAP + 1, tmp); 432e3037485SYan-Hsuan Chuang 433e3037485SYan-Hsuan Chuang /* DLFW only use HIQ, map HIQ to hi priority */ 434e3037485SYan-Hsuan Chuang bckp[bckp_idx].len = 1; 435e3037485SYan-Hsuan Chuang bckp[bckp_idx].reg = REG_CR; 436e3037485SYan-Hsuan Chuang bckp[bckp_idx].val = rtw_read8(rtwdev, REG_CR); 437e3037485SYan-Hsuan Chuang bckp_idx++; 438e3037485SYan-Hsuan Chuang bckp[bckp_idx].len = 4; 439e3037485SYan-Hsuan Chuang bckp[bckp_idx].reg = REG_H2CQ_CSR; 440e3037485SYan-Hsuan Chuang bckp[bckp_idx].val = BIT_H2CQ_FULL; 441e3037485SYan-Hsuan Chuang bckp_idx++; 442e3037485SYan-Hsuan Chuang tmp = BIT_HCI_TXDMA_EN | BIT_TXDMA_EN; 443e3037485SYan-Hsuan Chuang rtw_write8(rtwdev, REG_CR, tmp); 444e3037485SYan-Hsuan Chuang rtw_write32(rtwdev, REG_H2CQ_CSR, BIT_H2CQ_FULL); 445e3037485SYan-Hsuan Chuang 446e3037485SYan-Hsuan Chuang /* Config hi priority queue and public priority queue page number */ 447e3037485SYan-Hsuan Chuang bckp[bckp_idx].len = 2; 448e3037485SYan-Hsuan Chuang bckp[bckp_idx].reg = REG_FIFOPAGE_INFO_1; 449e3037485SYan-Hsuan Chuang bckp[bckp_idx].val = rtw_read16(rtwdev, REG_FIFOPAGE_INFO_1); 450e3037485SYan-Hsuan Chuang bckp_idx++; 451e3037485SYan-Hsuan Chuang bckp[bckp_idx].len = 4; 452e3037485SYan-Hsuan Chuang bckp[bckp_idx].reg = REG_RQPN_CTRL_2; 453e3037485SYan-Hsuan Chuang bckp[bckp_idx].val = rtw_read32(rtwdev, REG_RQPN_CTRL_2) | BIT_LD_RQPN; 454e3037485SYan-Hsuan Chuang bckp_idx++; 455e3037485SYan-Hsuan Chuang rtw_write16(rtwdev, REG_FIFOPAGE_INFO_1, 0x200); 456e3037485SYan-Hsuan Chuang rtw_write32(rtwdev, REG_RQPN_CTRL_2, bckp[bckp_idx - 1].val); 457e3037485SYan-Hsuan Chuang 458e3037485SYan-Hsuan Chuang /* Disable beacon related functions */ 459e3037485SYan-Hsuan Chuang tmp = rtw_read8(rtwdev, REG_BCN_CTRL); 460e3037485SYan-Hsuan Chuang bckp[bckp_idx].len = 1; 461e3037485SYan-Hsuan Chuang bckp[bckp_idx].reg = REG_BCN_CTRL; 462e3037485SYan-Hsuan Chuang bckp[bckp_idx].val = tmp; 463e3037485SYan-Hsuan Chuang bckp_idx++; 464e3037485SYan-Hsuan Chuang tmp = (u8)((tmp & (~BIT_EN_BCN_FUNCTION)) | BIT_DIS_TSF_UDT); 465e3037485SYan-Hsuan Chuang rtw_write8(rtwdev, REG_BCN_CTRL, tmp); 466e3037485SYan-Hsuan Chuang 467e3037485SYan-Hsuan Chuang WARN(bckp_idx != DLFW_RESTORE_REG_NUM, "wrong backup number\n"); 468e3037485SYan-Hsuan Chuang } 469e3037485SYan-Hsuan Chuang 470e3037485SYan-Hsuan Chuang static void download_firmware_reset_platform(struct rtw_dev *rtwdev) 471e3037485SYan-Hsuan Chuang { 472e3037485SYan-Hsuan Chuang rtw_write8_clr(rtwdev, REG_CPU_DMEM_CON + 2, BIT_WL_PLATFORM_RST >> 16); 473e3037485SYan-Hsuan Chuang rtw_write8_clr(rtwdev, REG_SYS_CLK_CTRL + 1, BIT_CPU_CLK_EN >> 8); 474e3037485SYan-Hsuan Chuang rtw_write8_set(rtwdev, REG_CPU_DMEM_CON + 2, BIT_WL_PLATFORM_RST >> 16); 475e3037485SYan-Hsuan Chuang rtw_write8_set(rtwdev, REG_SYS_CLK_CTRL + 1, BIT_CPU_CLK_EN >> 8); 476e3037485SYan-Hsuan Chuang } 477e3037485SYan-Hsuan Chuang 478e3037485SYan-Hsuan Chuang static void download_firmware_reg_restore(struct rtw_dev *rtwdev, 479e3037485SYan-Hsuan Chuang struct rtw_backup_info *bckp, 480e3037485SYan-Hsuan Chuang u8 bckp_num) 481e3037485SYan-Hsuan Chuang { 482e3037485SYan-Hsuan Chuang rtw_restore_reg(rtwdev, bckp, bckp_num); 483e3037485SYan-Hsuan Chuang } 484e3037485SYan-Hsuan Chuang 485e3037485SYan-Hsuan Chuang #define TX_DESC_SIZE 48 486e3037485SYan-Hsuan Chuang 487e3037485SYan-Hsuan Chuang static int send_firmware_pkt_rsvd_page(struct rtw_dev *rtwdev, u16 pg_addr, 488e3037485SYan-Hsuan Chuang const u8 *data, u32 size) 489e3037485SYan-Hsuan Chuang { 490e3037485SYan-Hsuan Chuang u8 *buf; 491e3037485SYan-Hsuan Chuang int ret; 492e3037485SYan-Hsuan Chuang 493e3037485SYan-Hsuan Chuang buf = kmemdup(data, size, GFP_KERNEL); 494e3037485SYan-Hsuan Chuang if (!buf) 495e3037485SYan-Hsuan Chuang return -ENOMEM; 496e3037485SYan-Hsuan Chuang 497e3037485SYan-Hsuan Chuang ret = rtw_fw_write_data_rsvd_page(rtwdev, pg_addr, buf, size); 498e3037485SYan-Hsuan Chuang kfree(buf); 499e3037485SYan-Hsuan Chuang return ret; 500e3037485SYan-Hsuan Chuang } 501e3037485SYan-Hsuan Chuang 502e3037485SYan-Hsuan Chuang static int 503e3037485SYan-Hsuan Chuang send_firmware_pkt(struct rtw_dev *rtwdev, u16 pg_addr, const u8 *data, u32 size) 504e3037485SYan-Hsuan Chuang { 505e3037485SYan-Hsuan Chuang int ret; 506e3037485SYan-Hsuan Chuang 507e3037485SYan-Hsuan Chuang if (rtw_hci_type(rtwdev) == RTW_HCI_TYPE_USB && 508e3037485SYan-Hsuan Chuang !((size + TX_DESC_SIZE) & (512 - 1))) 509e3037485SYan-Hsuan Chuang size += 1; 510e3037485SYan-Hsuan Chuang 511e3037485SYan-Hsuan Chuang ret = send_firmware_pkt_rsvd_page(rtwdev, pg_addr, data, size); 512e3037485SYan-Hsuan Chuang if (ret) 513e3037485SYan-Hsuan Chuang rtw_err(rtwdev, "failed to download rsvd page\n"); 514e3037485SYan-Hsuan Chuang 515e3037485SYan-Hsuan Chuang return ret; 516e3037485SYan-Hsuan Chuang } 517e3037485SYan-Hsuan Chuang 518e3037485SYan-Hsuan Chuang static int 519e3037485SYan-Hsuan Chuang iddma_enable(struct rtw_dev *rtwdev, u32 src, u32 dst, u32 ctrl) 520e3037485SYan-Hsuan Chuang { 521e3037485SYan-Hsuan Chuang rtw_write32(rtwdev, REG_DDMA_CH0SA, src); 522e3037485SYan-Hsuan Chuang rtw_write32(rtwdev, REG_DDMA_CH0DA, dst); 523e3037485SYan-Hsuan Chuang rtw_write32(rtwdev, REG_DDMA_CH0CTRL, ctrl); 524e3037485SYan-Hsuan Chuang 525e3037485SYan-Hsuan Chuang if (!check_hw_ready(rtwdev, REG_DDMA_CH0CTRL, BIT_DDMACH0_OWN, 0)) 526e3037485SYan-Hsuan Chuang return -EBUSY; 527e3037485SYan-Hsuan Chuang 528e3037485SYan-Hsuan Chuang return 0; 529e3037485SYan-Hsuan Chuang } 530e3037485SYan-Hsuan Chuang 531e3037485SYan-Hsuan Chuang static int iddma_download_firmware(struct rtw_dev *rtwdev, u32 src, u32 dst, 532e3037485SYan-Hsuan Chuang u32 len, u8 first) 533e3037485SYan-Hsuan Chuang { 534e3037485SYan-Hsuan Chuang u32 ch0_ctrl = BIT_DDMACH0_CHKSUM_EN | BIT_DDMACH0_OWN; 535e3037485SYan-Hsuan Chuang 536e3037485SYan-Hsuan Chuang if (!check_hw_ready(rtwdev, REG_DDMA_CH0CTRL, BIT_DDMACH0_OWN, 0)) 537e3037485SYan-Hsuan Chuang return -EBUSY; 538e3037485SYan-Hsuan Chuang 539e3037485SYan-Hsuan Chuang ch0_ctrl |= len & BIT_MASK_DDMACH0_DLEN; 540e3037485SYan-Hsuan Chuang if (!first) 541e3037485SYan-Hsuan Chuang ch0_ctrl |= BIT_DDMACH0_CHKSUM_CONT; 542e3037485SYan-Hsuan Chuang 543e3037485SYan-Hsuan Chuang if (iddma_enable(rtwdev, src, dst, ch0_ctrl)) 544e3037485SYan-Hsuan Chuang return -EBUSY; 545e3037485SYan-Hsuan Chuang 546e3037485SYan-Hsuan Chuang return 0; 547e3037485SYan-Hsuan Chuang } 548e3037485SYan-Hsuan Chuang 54913ce240aSZong-Zhe Yang int rtw_ddma_to_fw_fifo(struct rtw_dev *rtwdev, u32 ocp_src, u32 size) 55013ce240aSZong-Zhe Yang { 55113ce240aSZong-Zhe Yang u32 ch0_ctrl = BIT_DDMACH0_OWN | BIT_DDMACH0_DDMA_MODE; 55213ce240aSZong-Zhe Yang 55313ce240aSZong-Zhe Yang if (!check_hw_ready(rtwdev, REG_DDMA_CH0CTRL, BIT_DDMACH0_OWN, 0)) { 55413ce240aSZong-Zhe Yang rtw_dbg(rtwdev, RTW_DBG_FW, "busy to start ddma\n"); 55513ce240aSZong-Zhe Yang return -EBUSY; 55613ce240aSZong-Zhe Yang } 55713ce240aSZong-Zhe Yang 55813ce240aSZong-Zhe Yang ch0_ctrl |= size & BIT_MASK_DDMACH0_DLEN; 55913ce240aSZong-Zhe Yang 56013ce240aSZong-Zhe Yang if (iddma_enable(rtwdev, ocp_src, OCPBASE_RXBUF_FW_88XX, ch0_ctrl)) { 56113ce240aSZong-Zhe Yang rtw_dbg(rtwdev, RTW_DBG_FW, "busy to complete ddma\n"); 56213ce240aSZong-Zhe Yang return -EBUSY; 56313ce240aSZong-Zhe Yang } 56413ce240aSZong-Zhe Yang 56513ce240aSZong-Zhe Yang return 0; 56613ce240aSZong-Zhe Yang } 56713ce240aSZong-Zhe Yang 568e3037485SYan-Hsuan Chuang static bool 569e3037485SYan-Hsuan Chuang check_fw_checksum(struct rtw_dev *rtwdev, u32 addr) 570e3037485SYan-Hsuan Chuang { 571e3037485SYan-Hsuan Chuang u8 fw_ctrl; 572e3037485SYan-Hsuan Chuang 573e3037485SYan-Hsuan Chuang fw_ctrl = rtw_read8(rtwdev, REG_MCUFW_CTRL); 574e3037485SYan-Hsuan Chuang 575e3037485SYan-Hsuan Chuang if (rtw_read32(rtwdev, REG_DDMA_CH0CTRL) & BIT_DDMACH0_CHKSUM_STS) { 576e3037485SYan-Hsuan Chuang if (addr < OCPBASE_DMEM_88XX) { 577e3037485SYan-Hsuan Chuang fw_ctrl |= BIT_IMEM_DW_OK; 578e3037485SYan-Hsuan Chuang fw_ctrl &= ~BIT_IMEM_CHKSUM_OK; 579e3037485SYan-Hsuan Chuang rtw_write8(rtwdev, REG_MCUFW_CTRL, fw_ctrl); 580e3037485SYan-Hsuan Chuang } else { 581e3037485SYan-Hsuan Chuang fw_ctrl |= BIT_DMEM_DW_OK; 582e3037485SYan-Hsuan Chuang fw_ctrl &= ~BIT_DMEM_CHKSUM_OK; 583e3037485SYan-Hsuan Chuang rtw_write8(rtwdev, REG_MCUFW_CTRL, fw_ctrl); 584e3037485SYan-Hsuan Chuang } 585e3037485SYan-Hsuan Chuang 586e3037485SYan-Hsuan Chuang rtw_err(rtwdev, "invalid fw checksum\n"); 587e3037485SYan-Hsuan Chuang 588e3037485SYan-Hsuan Chuang return false; 589e3037485SYan-Hsuan Chuang } 590e3037485SYan-Hsuan Chuang 591e3037485SYan-Hsuan Chuang if (addr < OCPBASE_DMEM_88XX) { 592e3037485SYan-Hsuan Chuang fw_ctrl |= (BIT_IMEM_DW_OK | BIT_IMEM_CHKSUM_OK); 593e3037485SYan-Hsuan Chuang rtw_write8(rtwdev, REG_MCUFW_CTRL, fw_ctrl); 594e3037485SYan-Hsuan Chuang } else { 595e3037485SYan-Hsuan Chuang fw_ctrl |= (BIT_DMEM_DW_OK | BIT_DMEM_CHKSUM_OK); 596e3037485SYan-Hsuan Chuang rtw_write8(rtwdev, REG_MCUFW_CTRL, fw_ctrl); 597e3037485SYan-Hsuan Chuang } 598e3037485SYan-Hsuan Chuang 599e3037485SYan-Hsuan Chuang return true; 600e3037485SYan-Hsuan Chuang } 601e3037485SYan-Hsuan Chuang 602e3037485SYan-Hsuan Chuang static int 603e3037485SYan-Hsuan Chuang download_firmware_to_mem(struct rtw_dev *rtwdev, const u8 *data, 604e3037485SYan-Hsuan Chuang u32 src, u32 dst, u32 size) 605e3037485SYan-Hsuan Chuang { 606dcbf179cSPing-Ke Shih const struct rtw_chip_info *chip = rtwdev->chip; 607e3037485SYan-Hsuan Chuang u32 desc_size = chip->tx_pkt_desc_sz; 608e3037485SYan-Hsuan Chuang u8 first_part; 609e3037485SYan-Hsuan Chuang u32 mem_offset; 610e3037485SYan-Hsuan Chuang u32 residue_size; 611e3037485SYan-Hsuan Chuang u32 pkt_size; 612e3037485SYan-Hsuan Chuang u32 max_size = 0x1000; 613e3037485SYan-Hsuan Chuang u32 val; 614e3037485SYan-Hsuan Chuang int ret; 615e3037485SYan-Hsuan Chuang 616e3037485SYan-Hsuan Chuang mem_offset = 0; 617e3037485SYan-Hsuan Chuang first_part = 1; 618e3037485SYan-Hsuan Chuang residue_size = size; 619e3037485SYan-Hsuan Chuang 620e3037485SYan-Hsuan Chuang val = rtw_read32(rtwdev, REG_DDMA_CH0CTRL); 621e3037485SYan-Hsuan Chuang val |= BIT_DDMACH0_RESET_CHKSUM_STS; 622e3037485SYan-Hsuan Chuang rtw_write32(rtwdev, REG_DDMA_CH0CTRL, val); 623e3037485SYan-Hsuan Chuang 624e3037485SYan-Hsuan Chuang while (residue_size) { 625e3037485SYan-Hsuan Chuang if (residue_size >= max_size) 626e3037485SYan-Hsuan Chuang pkt_size = max_size; 627e3037485SYan-Hsuan Chuang else 628e3037485SYan-Hsuan Chuang pkt_size = residue_size; 629e3037485SYan-Hsuan Chuang 630e3037485SYan-Hsuan Chuang ret = send_firmware_pkt(rtwdev, (u16)(src >> 7), 631e3037485SYan-Hsuan Chuang data + mem_offset, pkt_size); 632e3037485SYan-Hsuan Chuang if (ret) 633e3037485SYan-Hsuan Chuang return ret; 634e3037485SYan-Hsuan Chuang 635e3037485SYan-Hsuan Chuang ret = iddma_download_firmware(rtwdev, OCPBASE_TXBUF_88XX + 636e3037485SYan-Hsuan Chuang src + desc_size, 637e3037485SYan-Hsuan Chuang dst + mem_offset, pkt_size, 638e3037485SYan-Hsuan Chuang first_part); 639e3037485SYan-Hsuan Chuang if (ret) 640e3037485SYan-Hsuan Chuang return ret; 641e3037485SYan-Hsuan Chuang 642e3037485SYan-Hsuan Chuang first_part = 0; 643e3037485SYan-Hsuan Chuang mem_offset += pkt_size; 644e3037485SYan-Hsuan Chuang residue_size -= pkt_size; 645e3037485SYan-Hsuan Chuang } 646e3037485SYan-Hsuan Chuang 647e3037485SYan-Hsuan Chuang if (!check_fw_checksum(rtwdev, dst)) 648e3037485SYan-Hsuan Chuang return -EINVAL; 649e3037485SYan-Hsuan Chuang 650e3037485SYan-Hsuan Chuang return 0; 651e3037485SYan-Hsuan Chuang } 652e3037485SYan-Hsuan Chuang 653e3037485SYan-Hsuan Chuang static int 654e3037485SYan-Hsuan Chuang start_download_firmware(struct rtw_dev *rtwdev, const u8 *data, u32 size) 655e3037485SYan-Hsuan Chuang { 656cc20a713SPing-Ke Shih const struct rtw_fw_hdr *fw_hdr = (const struct rtw_fw_hdr *)data; 657e3037485SYan-Hsuan Chuang const u8 *cur_fw; 658e3037485SYan-Hsuan Chuang u16 val; 659e3037485SYan-Hsuan Chuang u32 imem_size; 660e3037485SYan-Hsuan Chuang u32 dmem_size; 661e3037485SYan-Hsuan Chuang u32 emem_size; 662e3037485SYan-Hsuan Chuang u32 addr; 663e3037485SYan-Hsuan Chuang int ret; 664e3037485SYan-Hsuan Chuang 665cc20a713SPing-Ke Shih dmem_size = le32_to_cpu(fw_hdr->dmem_size); 666cc20a713SPing-Ke Shih imem_size = le32_to_cpu(fw_hdr->imem_size); 667cc20a713SPing-Ke Shih emem_size = (fw_hdr->mem_usage & BIT(4)) ? 668cc20a713SPing-Ke Shih le32_to_cpu(fw_hdr->emem_size) : 0; 669e3037485SYan-Hsuan Chuang dmem_size += FW_HDR_CHKSUM_SIZE; 670e3037485SYan-Hsuan Chuang imem_size += FW_HDR_CHKSUM_SIZE; 671e3037485SYan-Hsuan Chuang emem_size += emem_size ? FW_HDR_CHKSUM_SIZE : 0; 672e3037485SYan-Hsuan Chuang 673e3037485SYan-Hsuan Chuang val = (u16)(rtw_read16(rtwdev, REG_MCUFW_CTRL) & 0x3800); 674e3037485SYan-Hsuan Chuang val |= BIT_MCUFWDL_EN; 675e3037485SYan-Hsuan Chuang rtw_write16(rtwdev, REG_MCUFW_CTRL, val); 676e3037485SYan-Hsuan Chuang 677e3037485SYan-Hsuan Chuang cur_fw = data + FW_HDR_SIZE; 678cc20a713SPing-Ke Shih addr = le32_to_cpu(fw_hdr->dmem_addr); 679e3037485SYan-Hsuan Chuang addr &= ~BIT(31); 680e3037485SYan-Hsuan Chuang ret = download_firmware_to_mem(rtwdev, cur_fw, 0, addr, dmem_size); 681e3037485SYan-Hsuan Chuang if (ret) 682e3037485SYan-Hsuan Chuang return ret; 683e3037485SYan-Hsuan Chuang 684e3037485SYan-Hsuan Chuang cur_fw = data + FW_HDR_SIZE + dmem_size; 685cc20a713SPing-Ke Shih addr = le32_to_cpu(fw_hdr->imem_addr); 686e3037485SYan-Hsuan Chuang addr &= ~BIT(31); 687e3037485SYan-Hsuan Chuang ret = download_firmware_to_mem(rtwdev, cur_fw, 0, addr, imem_size); 688e3037485SYan-Hsuan Chuang if (ret) 689e3037485SYan-Hsuan Chuang return ret; 690e3037485SYan-Hsuan Chuang 691e3037485SYan-Hsuan Chuang if (emem_size) { 692e3037485SYan-Hsuan Chuang cur_fw = data + FW_HDR_SIZE + dmem_size + imem_size; 693cc20a713SPing-Ke Shih addr = le32_to_cpu(fw_hdr->emem_addr); 694e3037485SYan-Hsuan Chuang addr &= ~BIT(31); 695e3037485SYan-Hsuan Chuang ret = download_firmware_to_mem(rtwdev, cur_fw, 0, addr, 696e3037485SYan-Hsuan Chuang emem_size); 697e3037485SYan-Hsuan Chuang if (ret) 698e3037485SYan-Hsuan Chuang return ret; 699e3037485SYan-Hsuan Chuang } 700e3037485SYan-Hsuan Chuang 701e3037485SYan-Hsuan Chuang return 0; 702e3037485SYan-Hsuan Chuang } 703e3037485SYan-Hsuan Chuang 704e3037485SYan-Hsuan Chuang static int download_firmware_validate(struct rtw_dev *rtwdev) 705e3037485SYan-Hsuan Chuang { 706e3037485SYan-Hsuan Chuang u32 fw_key; 707e3037485SYan-Hsuan Chuang 708e3037485SYan-Hsuan Chuang if (!check_hw_ready(rtwdev, REG_MCUFW_CTRL, FW_READY_MASK, FW_READY)) { 709e3037485SYan-Hsuan Chuang fw_key = rtw_read32(rtwdev, REG_FW_DBG7) & FW_KEY_MASK; 710e3037485SYan-Hsuan Chuang if (fw_key == ILLEGAL_KEY_GROUP) 711e3037485SYan-Hsuan Chuang rtw_err(rtwdev, "invalid fw key\n"); 712e3037485SYan-Hsuan Chuang return -EINVAL; 713e3037485SYan-Hsuan Chuang } 714e3037485SYan-Hsuan Chuang 715e3037485SYan-Hsuan Chuang return 0; 716e3037485SYan-Hsuan Chuang } 717e3037485SYan-Hsuan Chuang 718e3037485SYan-Hsuan Chuang static void download_firmware_end_flow(struct rtw_dev *rtwdev) 719e3037485SYan-Hsuan Chuang { 720e3037485SYan-Hsuan Chuang u16 fw_ctrl; 721e3037485SYan-Hsuan Chuang 722e3037485SYan-Hsuan Chuang rtw_write32(rtwdev, REG_TXDMA_STATUS, BTI_PAGE_OVF); 723e3037485SYan-Hsuan Chuang 724e3037485SYan-Hsuan Chuang /* Check IMEM & DMEM checksum is OK or not */ 725e3037485SYan-Hsuan Chuang fw_ctrl = rtw_read16(rtwdev, REG_MCUFW_CTRL); 726e3037485SYan-Hsuan Chuang if ((fw_ctrl & BIT_CHECK_SUM_OK) != BIT_CHECK_SUM_OK) 727e3037485SYan-Hsuan Chuang return; 728e3037485SYan-Hsuan Chuang 729e3037485SYan-Hsuan Chuang fw_ctrl = (fw_ctrl | BIT_FW_DW_RDY) & ~BIT_MCUFWDL_EN; 730e3037485SYan-Hsuan Chuang rtw_write16(rtwdev, REG_MCUFW_CTRL, fw_ctrl); 731e3037485SYan-Hsuan Chuang } 732e3037485SYan-Hsuan Chuang 7333d8bf508SYan-Hsuan Chuang static int __rtw_download_firmware(struct rtw_dev *rtwdev, 7343d8bf508SYan-Hsuan Chuang struct rtw_fw_state *fw) 735e3037485SYan-Hsuan Chuang { 736e3037485SYan-Hsuan Chuang struct rtw_backup_info bckp[DLFW_RESTORE_REG_NUM]; 737e3037485SYan-Hsuan Chuang const u8 *data = fw->firmware->data; 738e3037485SYan-Hsuan Chuang u32 size = fw->firmware->size; 739e3037485SYan-Hsuan Chuang u32 ltecoex_bckp; 740e3037485SYan-Hsuan Chuang int ret; 741e3037485SYan-Hsuan Chuang 742e3037485SYan-Hsuan Chuang if (!check_firmware_size(data, size)) 743e3037485SYan-Hsuan Chuang return -EINVAL; 744e3037485SYan-Hsuan Chuang 745e3037485SYan-Hsuan Chuang if (!ltecoex_read_reg(rtwdev, 0x38, <ecoex_bckp)) 746e3037485SYan-Hsuan Chuang return -EBUSY; 747e3037485SYan-Hsuan Chuang 748e3037485SYan-Hsuan Chuang wlan_cpu_enable(rtwdev, false); 749e3037485SYan-Hsuan Chuang 750e3037485SYan-Hsuan Chuang download_firmware_reg_backup(rtwdev, bckp); 751e3037485SYan-Hsuan Chuang download_firmware_reset_platform(rtwdev); 752e3037485SYan-Hsuan Chuang 753e3037485SYan-Hsuan Chuang ret = start_download_firmware(rtwdev, data, size); 754e3037485SYan-Hsuan Chuang if (ret) 755e3037485SYan-Hsuan Chuang goto dlfw_fail; 756e3037485SYan-Hsuan Chuang 757e3037485SYan-Hsuan Chuang download_firmware_reg_restore(rtwdev, bckp, DLFW_RESTORE_REG_NUM); 758e3037485SYan-Hsuan Chuang 759e3037485SYan-Hsuan Chuang download_firmware_end_flow(rtwdev); 760e3037485SYan-Hsuan Chuang 761e3037485SYan-Hsuan Chuang wlan_cpu_enable(rtwdev, true); 762e3037485SYan-Hsuan Chuang 763e3037485SYan-Hsuan Chuang if (!ltecoex_reg_write(rtwdev, 0x38, ltecoex_bckp)) 764e3037485SYan-Hsuan Chuang return -EBUSY; 765e3037485SYan-Hsuan Chuang 766e3037485SYan-Hsuan Chuang ret = download_firmware_validate(rtwdev); 767e3037485SYan-Hsuan Chuang if (ret) 768e3037485SYan-Hsuan Chuang goto dlfw_fail; 769e3037485SYan-Hsuan Chuang 770e3037485SYan-Hsuan Chuang /* reset desc and index */ 771e3037485SYan-Hsuan Chuang rtw_hci_setup(rtwdev); 772e3037485SYan-Hsuan Chuang 773e3037485SYan-Hsuan Chuang rtwdev->h2c.last_box_num = 0; 774e3037485SYan-Hsuan Chuang rtwdev->h2c.seq = 0; 775e3037485SYan-Hsuan Chuang 7763c519605SYan-Hsuan Chuang set_bit(RTW_FLAG_FW_RUNNING, rtwdev->flags); 777e3037485SYan-Hsuan Chuang 778e3037485SYan-Hsuan Chuang return 0; 779e3037485SYan-Hsuan Chuang 780e3037485SYan-Hsuan Chuang dlfw_fail: 781e3037485SYan-Hsuan Chuang /* Disable FWDL_EN */ 782e3037485SYan-Hsuan Chuang rtw_write8_clr(rtwdev, REG_MCUFW_CTRL, BIT_MCUFWDL_EN); 783e3037485SYan-Hsuan Chuang rtw_write8_set(rtwdev, REG_SYS_FUNC_EN + 1, BIT_FEN_CPUEN); 784e3037485SYan-Hsuan Chuang 785e3037485SYan-Hsuan Chuang return ret; 786e3037485SYan-Hsuan Chuang } 787e3037485SYan-Hsuan Chuang 78815d2fcc6SPing-Ke Shih static void en_download_firmware_legacy(struct rtw_dev *rtwdev, bool en) 78915d2fcc6SPing-Ke Shih { 79015d2fcc6SPing-Ke Shih int try; 79115d2fcc6SPing-Ke Shih 79215d2fcc6SPing-Ke Shih if (en) { 79315d2fcc6SPing-Ke Shih wlan_cpu_enable(rtwdev, false); 79415d2fcc6SPing-Ke Shih wlan_cpu_enable(rtwdev, true); 79515d2fcc6SPing-Ke Shih 79615d2fcc6SPing-Ke Shih rtw_write8_set(rtwdev, REG_MCUFW_CTRL, BIT_MCUFWDL_EN); 79715d2fcc6SPing-Ke Shih 79815d2fcc6SPing-Ke Shih for (try = 0; try < 10; try++) { 79915d2fcc6SPing-Ke Shih if (rtw_read8(rtwdev, REG_MCUFW_CTRL) & BIT_MCUFWDL_EN) 80015d2fcc6SPing-Ke Shih goto fwdl_ready; 80115d2fcc6SPing-Ke Shih rtw_write8_set(rtwdev, REG_MCUFW_CTRL, BIT_MCUFWDL_EN); 80215d2fcc6SPing-Ke Shih msleep(20); 80315d2fcc6SPing-Ke Shih } 80415d2fcc6SPing-Ke Shih rtw_err(rtwdev, "failed to check fw download ready\n"); 80515d2fcc6SPing-Ke Shih fwdl_ready: 80615d2fcc6SPing-Ke Shih rtw_write32_clr(rtwdev, REG_MCUFW_CTRL, BIT_ROM_DLEN); 80715d2fcc6SPing-Ke Shih } else { 80815d2fcc6SPing-Ke Shih rtw_write8_clr(rtwdev, REG_MCUFW_CTRL, BIT_MCUFWDL_EN); 80915d2fcc6SPing-Ke Shih } 81015d2fcc6SPing-Ke Shih } 81115d2fcc6SPing-Ke Shih 81215d2fcc6SPing-Ke Shih static void 81315d2fcc6SPing-Ke Shih write_firmware_page(struct rtw_dev *rtwdev, u32 page, const u8 *data, u32 size) 81415d2fcc6SPing-Ke Shih { 81515d2fcc6SPing-Ke Shih u32 val32; 81615d2fcc6SPing-Ke Shih u32 block_nr; 81715d2fcc6SPing-Ke Shih u32 remain_size; 81815d2fcc6SPing-Ke Shih u32 write_addr = FW_START_ADDR_LEGACY; 81915d2fcc6SPing-Ke Shih const __le32 *ptr = (const __le32 *)data; 82015d2fcc6SPing-Ke Shih u32 block; 82115d2fcc6SPing-Ke Shih __le32 remain_data = 0; 82215d2fcc6SPing-Ke Shih 82315d2fcc6SPing-Ke Shih block_nr = size >> DLFW_BLK_SIZE_SHIFT_LEGACY; 82415d2fcc6SPing-Ke Shih remain_size = size & (DLFW_BLK_SIZE_LEGACY - 1); 82515d2fcc6SPing-Ke Shih 82615d2fcc6SPing-Ke Shih val32 = rtw_read32(rtwdev, REG_MCUFW_CTRL); 82715d2fcc6SPing-Ke Shih val32 &= ~BIT_ROM_PGE; 82815d2fcc6SPing-Ke Shih val32 |= (page << BIT_SHIFT_ROM_PGE) & BIT_ROM_PGE; 82915d2fcc6SPing-Ke Shih rtw_write32(rtwdev, REG_MCUFW_CTRL, val32); 83015d2fcc6SPing-Ke Shih 83115d2fcc6SPing-Ke Shih for (block = 0; block < block_nr; block++) { 83215d2fcc6SPing-Ke Shih rtw_write32(rtwdev, write_addr, le32_to_cpu(*ptr)); 83315d2fcc6SPing-Ke Shih 83415d2fcc6SPing-Ke Shih write_addr += DLFW_BLK_SIZE_LEGACY; 83515d2fcc6SPing-Ke Shih ptr++; 83615d2fcc6SPing-Ke Shih } 83715d2fcc6SPing-Ke Shih 83815d2fcc6SPing-Ke Shih if (remain_size) { 83915d2fcc6SPing-Ke Shih memcpy(&remain_data, ptr, remain_size); 84015d2fcc6SPing-Ke Shih rtw_write32(rtwdev, write_addr, le32_to_cpu(remain_data)); 84115d2fcc6SPing-Ke Shih } 84215d2fcc6SPing-Ke Shih } 84315d2fcc6SPing-Ke Shih 84415d2fcc6SPing-Ke Shih static int 84515d2fcc6SPing-Ke Shih download_firmware_legacy(struct rtw_dev *rtwdev, const u8 *data, u32 size) 84615d2fcc6SPing-Ke Shih { 84715d2fcc6SPing-Ke Shih u32 page; 84815d2fcc6SPing-Ke Shih u32 total_page; 84915d2fcc6SPing-Ke Shih u32 last_page_size; 85015d2fcc6SPing-Ke Shih 85115d2fcc6SPing-Ke Shih data += sizeof(struct rtw_fw_hdr_legacy); 85215d2fcc6SPing-Ke Shih size -= sizeof(struct rtw_fw_hdr_legacy); 85315d2fcc6SPing-Ke Shih 85415d2fcc6SPing-Ke Shih total_page = size >> DLFW_PAGE_SIZE_SHIFT_LEGACY; 85515d2fcc6SPing-Ke Shih last_page_size = size & (DLFW_PAGE_SIZE_LEGACY - 1); 85615d2fcc6SPing-Ke Shih 85715d2fcc6SPing-Ke Shih rtw_write8_set(rtwdev, REG_MCUFW_CTRL, BIT_FWDL_CHK_RPT); 85815d2fcc6SPing-Ke Shih 85915d2fcc6SPing-Ke Shih for (page = 0; page < total_page; page++) { 86015d2fcc6SPing-Ke Shih write_firmware_page(rtwdev, page, data, DLFW_PAGE_SIZE_LEGACY); 86115d2fcc6SPing-Ke Shih data += DLFW_PAGE_SIZE_LEGACY; 86215d2fcc6SPing-Ke Shih } 86315d2fcc6SPing-Ke Shih if (last_page_size) 86415d2fcc6SPing-Ke Shih write_firmware_page(rtwdev, page, data, last_page_size); 86515d2fcc6SPing-Ke Shih 86615d2fcc6SPing-Ke Shih if (!check_hw_ready(rtwdev, REG_MCUFW_CTRL, BIT_FWDL_CHK_RPT, 1)) { 867a6336094SColin Ian King rtw_err(rtwdev, "failed to check download firmware report\n"); 86815d2fcc6SPing-Ke Shih return -EINVAL; 86915d2fcc6SPing-Ke Shih } 87015d2fcc6SPing-Ke Shih 87115d2fcc6SPing-Ke Shih return 0; 87215d2fcc6SPing-Ke Shih } 87315d2fcc6SPing-Ke Shih 87415d2fcc6SPing-Ke Shih static int download_firmware_validate_legacy(struct rtw_dev *rtwdev) 87515d2fcc6SPing-Ke Shih { 87615d2fcc6SPing-Ke Shih u32 val32; 87715d2fcc6SPing-Ke Shih int try; 87815d2fcc6SPing-Ke Shih 87915d2fcc6SPing-Ke Shih val32 = rtw_read32(rtwdev, REG_MCUFW_CTRL); 88015d2fcc6SPing-Ke Shih val32 |= BIT_MCUFWDL_RDY; 88115d2fcc6SPing-Ke Shih val32 &= ~BIT_WINTINI_RDY; 88215d2fcc6SPing-Ke Shih rtw_write32(rtwdev, REG_MCUFW_CTRL, val32); 88315d2fcc6SPing-Ke Shih 88415d2fcc6SPing-Ke Shih wlan_cpu_enable(rtwdev, false); 88515d2fcc6SPing-Ke Shih wlan_cpu_enable(rtwdev, true); 88615d2fcc6SPing-Ke Shih 88715d2fcc6SPing-Ke Shih for (try = 0; try < 10; try++) { 88815d2fcc6SPing-Ke Shih val32 = rtw_read32(rtwdev, REG_MCUFW_CTRL); 88915d2fcc6SPing-Ke Shih if ((val32 & FW_READY_LEGACY) == FW_READY_LEGACY) 89015d2fcc6SPing-Ke Shih return 0; 89115d2fcc6SPing-Ke Shih msleep(20); 89215d2fcc6SPing-Ke Shih } 89315d2fcc6SPing-Ke Shih 894a6336094SColin Ian King rtw_err(rtwdev, "failed to validate firmware\n"); 89515d2fcc6SPing-Ke Shih return -EINVAL; 89615d2fcc6SPing-Ke Shih } 89715d2fcc6SPing-Ke Shih 8983d8bf508SYan-Hsuan Chuang static int __rtw_download_firmware_legacy(struct rtw_dev *rtwdev, 8993d8bf508SYan-Hsuan Chuang struct rtw_fw_state *fw) 90015d2fcc6SPing-Ke Shih { 90115d2fcc6SPing-Ke Shih int ret = 0; 90215d2fcc6SPing-Ke Shih 90315d2fcc6SPing-Ke Shih en_download_firmware_legacy(rtwdev, true); 90415d2fcc6SPing-Ke Shih ret = download_firmware_legacy(rtwdev, fw->firmware->data, fw->firmware->size); 90515d2fcc6SPing-Ke Shih en_download_firmware_legacy(rtwdev, false); 90615d2fcc6SPing-Ke Shih if (ret) 90715d2fcc6SPing-Ke Shih goto out; 90815d2fcc6SPing-Ke Shih 90915d2fcc6SPing-Ke Shih ret = download_firmware_validate_legacy(rtwdev); 91015d2fcc6SPing-Ke Shih if (ret) 91115d2fcc6SPing-Ke Shih goto out; 91215d2fcc6SPing-Ke Shih 91315d2fcc6SPing-Ke Shih /* reset desc and index */ 91415d2fcc6SPing-Ke Shih rtw_hci_setup(rtwdev); 91515d2fcc6SPing-Ke Shih 91615d2fcc6SPing-Ke Shih rtwdev->h2c.last_box_num = 0; 91715d2fcc6SPing-Ke Shih rtwdev->h2c.seq = 0; 91815d2fcc6SPing-Ke Shih 91915d2fcc6SPing-Ke Shih set_bit(RTW_FLAG_FW_RUNNING, rtwdev->flags); 92015d2fcc6SPing-Ke Shih 92115d2fcc6SPing-Ke Shih out: 92215d2fcc6SPing-Ke Shih return ret; 92315d2fcc6SPing-Ke Shih } 92415d2fcc6SPing-Ke Shih 9257c57d3dcSPing-Ke Shih static 9267c57d3dcSPing-Ke Shih int _rtw_download_firmware(struct rtw_dev *rtwdev, struct rtw_fw_state *fw) 92715d2fcc6SPing-Ke Shih { 92815d2fcc6SPing-Ke Shih if (rtw_chip_wcpu_11n(rtwdev)) 92915d2fcc6SPing-Ke Shih return __rtw_download_firmware_legacy(rtwdev, fw); 93015d2fcc6SPing-Ke Shih 93115d2fcc6SPing-Ke Shih return __rtw_download_firmware(rtwdev, fw); 93215d2fcc6SPing-Ke Shih } 93315d2fcc6SPing-Ke Shih 9347c57d3dcSPing-Ke Shih int rtw_download_firmware(struct rtw_dev *rtwdev, struct rtw_fw_state *fw) 9357c57d3dcSPing-Ke Shih { 9367c57d3dcSPing-Ke Shih int ret; 9377c57d3dcSPing-Ke Shih 9387c57d3dcSPing-Ke Shih ret = _rtw_download_firmware(rtwdev, fw); 9397c57d3dcSPing-Ke Shih if (ret) 9407c57d3dcSPing-Ke Shih return ret; 9417c57d3dcSPing-Ke Shih 9427c57d3dcSPing-Ke Shih if (rtw_hci_type(rtwdev) == RTW_HCI_TYPE_PCIE && 9437c57d3dcSPing-Ke Shih rtwdev->chip->id == RTW_CHIP_TYPE_8821C) 9447c57d3dcSPing-Ke Shih rtw_fw_set_recover_bt_device(rtwdev); 9457c57d3dcSPing-Ke Shih 9467c57d3dcSPing-Ke Shih return 0; 9477c57d3dcSPing-Ke Shih } 9487c57d3dcSPing-Ke Shih 9491131ad7fSYan-Hsuan Chuang static u32 get_priority_queues(struct rtw_dev *rtwdev, u32 queues) 9501131ad7fSYan-Hsuan Chuang { 951d49f2c50SJoe Perches const struct rtw_rqpn *rqpn = rtwdev->fifo.rqpn; 9521131ad7fSYan-Hsuan Chuang u32 prio_queues = 0; 9531131ad7fSYan-Hsuan Chuang 9541131ad7fSYan-Hsuan Chuang if (queues & BIT(IEEE80211_AC_VO)) 9551131ad7fSYan-Hsuan Chuang prio_queues |= BIT(rqpn->dma_map_vo); 9561131ad7fSYan-Hsuan Chuang if (queues & BIT(IEEE80211_AC_VI)) 9571131ad7fSYan-Hsuan Chuang prio_queues |= BIT(rqpn->dma_map_vi); 9581131ad7fSYan-Hsuan Chuang if (queues & BIT(IEEE80211_AC_BE)) 9591131ad7fSYan-Hsuan Chuang prio_queues |= BIT(rqpn->dma_map_be); 9601131ad7fSYan-Hsuan Chuang if (queues & BIT(IEEE80211_AC_BK)) 9611131ad7fSYan-Hsuan Chuang prio_queues |= BIT(rqpn->dma_map_bk); 9621131ad7fSYan-Hsuan Chuang 9631131ad7fSYan-Hsuan Chuang return prio_queues; 9641131ad7fSYan-Hsuan Chuang } 9651131ad7fSYan-Hsuan Chuang 9661131ad7fSYan-Hsuan Chuang static void __rtw_mac_flush_prio_queue(struct rtw_dev *rtwdev, 9671131ad7fSYan-Hsuan Chuang u32 prio_queue, bool drop) 9681131ad7fSYan-Hsuan Chuang { 969dcbf179cSPing-Ke Shih const struct rtw_chip_info *chip = rtwdev->chip; 9707d754f97SPing-Ke Shih const struct rtw_prioq_addr *addr; 9717d754f97SPing-Ke Shih bool wsize; 9721131ad7fSYan-Hsuan Chuang u16 avail_page, rsvd_page; 9731131ad7fSYan-Hsuan Chuang int i; 9741131ad7fSYan-Hsuan Chuang 9757d754f97SPing-Ke Shih if (prio_queue >= RTW_DMA_MAPPING_MAX) 9761131ad7fSYan-Hsuan Chuang return; 9777d754f97SPing-Ke Shih 9787d754f97SPing-Ke Shih addr = &chip->prioq_addrs->prio[prio_queue]; 9797d754f97SPing-Ke Shih wsize = chip->prioq_addrs->wsize; 9801131ad7fSYan-Hsuan Chuang 9811131ad7fSYan-Hsuan Chuang /* check if all of the reserved pages are available for 100 msecs */ 9821131ad7fSYan-Hsuan Chuang for (i = 0; i < 5; i++) { 9837d754f97SPing-Ke Shih rsvd_page = wsize ? rtw_read16(rtwdev, addr->rsvd) : 9847d754f97SPing-Ke Shih rtw_read8(rtwdev, addr->rsvd); 9857d754f97SPing-Ke Shih avail_page = wsize ? rtw_read16(rtwdev, addr->avail) : 9867d754f97SPing-Ke Shih rtw_read8(rtwdev, addr->avail); 9871131ad7fSYan-Hsuan Chuang if (rsvd_page == avail_page) 9881131ad7fSYan-Hsuan Chuang return; 9891131ad7fSYan-Hsuan Chuang 9901131ad7fSYan-Hsuan Chuang msleep(20); 9911131ad7fSYan-Hsuan Chuang } 9921131ad7fSYan-Hsuan Chuang 9931131ad7fSYan-Hsuan Chuang /* priority queue is still not empty, throw a warning, 9941131ad7fSYan-Hsuan Chuang * 9951131ad7fSYan-Hsuan Chuang * Note that if we want to flush the tx queue when having a lot of 9961131ad7fSYan-Hsuan Chuang * traffic (ex, 100Mbps up), some of the packets could be dropped. 9971131ad7fSYan-Hsuan Chuang * And it requires like ~2secs to flush the full priority queue. 9981131ad7fSYan-Hsuan Chuang */ 9991131ad7fSYan-Hsuan Chuang if (!drop) 10001131ad7fSYan-Hsuan Chuang rtw_warn(rtwdev, "timed out to flush queue %d\n", prio_queue); 10011131ad7fSYan-Hsuan Chuang } 10021131ad7fSYan-Hsuan Chuang 10031131ad7fSYan-Hsuan Chuang static void rtw_mac_flush_prio_queues(struct rtw_dev *rtwdev, 10041131ad7fSYan-Hsuan Chuang u32 prio_queues, bool drop) 10051131ad7fSYan-Hsuan Chuang { 10061131ad7fSYan-Hsuan Chuang u32 q; 10071131ad7fSYan-Hsuan Chuang 10081131ad7fSYan-Hsuan Chuang for (q = 0; q < RTW_DMA_MAPPING_MAX; q++) 10091131ad7fSYan-Hsuan Chuang if (prio_queues & BIT(q)) 10101131ad7fSYan-Hsuan Chuang __rtw_mac_flush_prio_queue(rtwdev, q, drop); 10111131ad7fSYan-Hsuan Chuang } 10121131ad7fSYan-Hsuan Chuang 10131131ad7fSYan-Hsuan Chuang void rtw_mac_flush_queues(struct rtw_dev *rtwdev, u32 queues, bool drop) 10141131ad7fSYan-Hsuan Chuang { 10151131ad7fSYan-Hsuan Chuang u32 prio_queues = 0; 10161131ad7fSYan-Hsuan Chuang 10171131ad7fSYan-Hsuan Chuang /* If all of the hardware queues are requested to flush, 10181131ad7fSYan-Hsuan Chuang * or the priority queues are not mapped yet, 10191131ad7fSYan-Hsuan Chuang * flush all of the priority queues 10201131ad7fSYan-Hsuan Chuang */ 10211131ad7fSYan-Hsuan Chuang if (queues == BIT(rtwdev->hw->queues) - 1 || !rtwdev->fifo.rqpn) 10221131ad7fSYan-Hsuan Chuang prio_queues = BIT(RTW_DMA_MAPPING_MAX) - 1; 10231131ad7fSYan-Hsuan Chuang else 10241131ad7fSYan-Hsuan Chuang prio_queues = get_priority_queues(rtwdev, queues); 10251131ad7fSYan-Hsuan Chuang 10261131ad7fSYan-Hsuan Chuang rtw_mac_flush_prio_queues(rtwdev, prio_queues, drop); 10271131ad7fSYan-Hsuan Chuang } 10281131ad7fSYan-Hsuan Chuang 1029e3037485SYan-Hsuan Chuang static int txdma_queue_mapping(struct rtw_dev *rtwdev) 1030e3037485SYan-Hsuan Chuang { 1031dcbf179cSPing-Ke Shih const struct rtw_chip_info *chip = rtwdev->chip; 1032d49f2c50SJoe Perches const struct rtw_rqpn *rqpn = NULL; 1033e3037485SYan-Hsuan Chuang u16 txdma_pq_map = 0; 1034e3037485SYan-Hsuan Chuang 1035e3037485SYan-Hsuan Chuang switch (rtw_hci_type(rtwdev)) { 1036e3037485SYan-Hsuan Chuang case RTW_HCI_TYPE_PCIE: 1037e3037485SYan-Hsuan Chuang rqpn = &chip->rqpn_table[1]; 1038e3037485SYan-Hsuan Chuang break; 1039e3037485SYan-Hsuan Chuang case RTW_HCI_TYPE_USB: 1040e3037485SYan-Hsuan Chuang if (rtwdev->hci.bulkout_num == 2) 1041e3037485SYan-Hsuan Chuang rqpn = &chip->rqpn_table[2]; 1042e3037485SYan-Hsuan Chuang else if (rtwdev->hci.bulkout_num == 3) 1043e3037485SYan-Hsuan Chuang rqpn = &chip->rqpn_table[3]; 1044e3037485SYan-Hsuan Chuang else if (rtwdev->hci.bulkout_num == 4) 1045e3037485SYan-Hsuan Chuang rqpn = &chip->rqpn_table[4]; 1046e3037485SYan-Hsuan Chuang else 1047e3037485SYan-Hsuan Chuang return -EINVAL; 1048e3037485SYan-Hsuan Chuang break; 10498599ea40SMartin Blumenstingl case RTW_HCI_TYPE_SDIO: 10508599ea40SMartin Blumenstingl rqpn = &chip->rqpn_table[0]; 10518599ea40SMartin Blumenstingl break; 1052e3037485SYan-Hsuan Chuang default: 1053e3037485SYan-Hsuan Chuang return -EINVAL; 1054e3037485SYan-Hsuan Chuang } 1055e3037485SYan-Hsuan Chuang 10561131ad7fSYan-Hsuan Chuang rtwdev->fifo.rqpn = rqpn; 1057e3037485SYan-Hsuan Chuang txdma_pq_map |= BIT_TXDMA_HIQ_MAP(rqpn->dma_map_hi); 1058e3037485SYan-Hsuan Chuang txdma_pq_map |= BIT_TXDMA_MGQ_MAP(rqpn->dma_map_mg); 1059e3037485SYan-Hsuan Chuang txdma_pq_map |= BIT_TXDMA_BKQ_MAP(rqpn->dma_map_bk); 1060e3037485SYan-Hsuan Chuang txdma_pq_map |= BIT_TXDMA_BEQ_MAP(rqpn->dma_map_be); 1061e3037485SYan-Hsuan Chuang txdma_pq_map |= BIT_TXDMA_VIQ_MAP(rqpn->dma_map_vi); 1062e3037485SYan-Hsuan Chuang txdma_pq_map |= BIT_TXDMA_VOQ_MAP(rqpn->dma_map_vo); 1063e3037485SYan-Hsuan Chuang rtw_write16(rtwdev, REG_TXDMA_PQ_MAP, txdma_pq_map); 1064e3037485SYan-Hsuan Chuang 1065e3037485SYan-Hsuan Chuang rtw_write8(rtwdev, REG_CR, 0); 1066e3037485SYan-Hsuan Chuang rtw_write8(rtwdev, REG_CR, MAC_TRX_ENABLE); 10677907b52dSPing-Ke Shih if (rtw_chip_wcpu_11ac(rtwdev)) 1068e3037485SYan-Hsuan Chuang rtw_write32(rtwdev, REG_H2CQ_CSR, BIT_H2CQ_FULL); 1069e3037485SYan-Hsuan Chuang 1070a82dfd33SSascha Hauer if (rtw_hci_type(rtwdev) == RTW_HCI_TYPE_USB) 1071a82dfd33SSascha Hauer rtw_write8_set(rtwdev, REG_TXDMA_PQ_MAP, BIT_RXDMA_ARBBW_EN); 1072a82dfd33SSascha Hauer 1073e3037485SYan-Hsuan Chuang return 0; 1074e3037485SYan-Hsuan Chuang } 1075e3037485SYan-Hsuan Chuang 1076e3037485SYan-Hsuan Chuang static int set_trx_fifo_info(struct rtw_dev *rtwdev) 1077e3037485SYan-Hsuan Chuang { 1078dcbf179cSPing-Ke Shih const struct rtw_chip_info *chip = rtwdev->chip; 1079e3037485SYan-Hsuan Chuang struct rtw_fifo_conf *fifo = &rtwdev->fifo; 1080e3037485SYan-Hsuan Chuang u16 cur_pg_addr; 1081e3037485SYan-Hsuan Chuang u8 csi_buf_pg_num = chip->csi_buf_pg_num; 1082e3037485SYan-Hsuan Chuang 1083e3037485SYan-Hsuan Chuang /* config rsvd page num */ 1084e3037485SYan-Hsuan Chuang fifo->rsvd_drv_pg_num = 8; 1085e3037485SYan-Hsuan Chuang fifo->txff_pg_num = chip->txff_size >> 7; 1086d91277deSPing-Ke Shih if (rtw_chip_wcpu_11n(rtwdev)) 1087d91277deSPing-Ke Shih fifo->rsvd_pg_num = fifo->rsvd_drv_pg_num; 1088d91277deSPing-Ke Shih else 1089e3037485SYan-Hsuan Chuang fifo->rsvd_pg_num = fifo->rsvd_drv_pg_num + 1090e3037485SYan-Hsuan Chuang RSVD_PG_H2C_EXTRAINFO_NUM + 1091e3037485SYan-Hsuan Chuang RSVD_PG_H2C_STATICINFO_NUM + 1092e3037485SYan-Hsuan Chuang RSVD_PG_H2CQ_NUM + 1093e3037485SYan-Hsuan Chuang RSVD_PG_CPU_INSTRUCTION_NUM + 1094e3037485SYan-Hsuan Chuang RSVD_PG_FW_TXBUF_NUM + 1095e3037485SYan-Hsuan Chuang csi_buf_pg_num; 1096e3037485SYan-Hsuan Chuang 1097e3037485SYan-Hsuan Chuang if (fifo->rsvd_pg_num > fifo->txff_pg_num) 1098e3037485SYan-Hsuan Chuang return -ENOMEM; 1099e3037485SYan-Hsuan Chuang 1100e3037485SYan-Hsuan Chuang fifo->acq_pg_num = fifo->txff_pg_num - fifo->rsvd_pg_num; 1101e3037485SYan-Hsuan Chuang fifo->rsvd_boundary = fifo->txff_pg_num - fifo->rsvd_pg_num; 1102e3037485SYan-Hsuan Chuang 1103e3037485SYan-Hsuan Chuang cur_pg_addr = fifo->txff_pg_num; 1104d91277deSPing-Ke Shih if (rtw_chip_wcpu_11ac(rtwdev)) { 1105e3037485SYan-Hsuan Chuang cur_pg_addr -= csi_buf_pg_num; 1106e3037485SYan-Hsuan Chuang fifo->rsvd_csibuf_addr = cur_pg_addr; 1107e3037485SYan-Hsuan Chuang cur_pg_addr -= RSVD_PG_FW_TXBUF_NUM; 1108e3037485SYan-Hsuan Chuang fifo->rsvd_fw_txbuf_addr = cur_pg_addr; 1109e3037485SYan-Hsuan Chuang cur_pg_addr -= RSVD_PG_CPU_INSTRUCTION_NUM; 1110e3037485SYan-Hsuan Chuang fifo->rsvd_cpu_instr_addr = cur_pg_addr; 1111e3037485SYan-Hsuan Chuang cur_pg_addr -= RSVD_PG_H2CQ_NUM; 1112e3037485SYan-Hsuan Chuang fifo->rsvd_h2cq_addr = cur_pg_addr; 1113e3037485SYan-Hsuan Chuang cur_pg_addr -= RSVD_PG_H2C_STATICINFO_NUM; 1114e3037485SYan-Hsuan Chuang fifo->rsvd_h2c_sta_info_addr = cur_pg_addr; 1115e3037485SYan-Hsuan Chuang cur_pg_addr -= RSVD_PG_H2C_EXTRAINFO_NUM; 1116e3037485SYan-Hsuan Chuang fifo->rsvd_h2c_info_addr = cur_pg_addr; 1117d91277deSPing-Ke Shih } 1118e3037485SYan-Hsuan Chuang cur_pg_addr -= fifo->rsvd_drv_pg_num; 1119e3037485SYan-Hsuan Chuang fifo->rsvd_drv_addr = cur_pg_addr; 1120e3037485SYan-Hsuan Chuang 1121e3037485SYan-Hsuan Chuang if (fifo->rsvd_boundary != fifo->rsvd_drv_addr) { 1122e3037485SYan-Hsuan Chuang rtw_err(rtwdev, "wrong rsvd driver address\n"); 1123e3037485SYan-Hsuan Chuang return -EINVAL; 1124e3037485SYan-Hsuan Chuang } 1125e3037485SYan-Hsuan Chuang 1126e3037485SYan-Hsuan Chuang return 0; 1127e3037485SYan-Hsuan Chuang } 1128e3037485SYan-Hsuan Chuang 1129d91277deSPing-Ke Shih static int __priority_queue_cfg(struct rtw_dev *rtwdev, 1130d91277deSPing-Ke Shih const struct rtw_page_table *pg_tbl, 1131d91277deSPing-Ke Shih u16 pubq_num) 1132d91277deSPing-Ke Shih { 1133dcbf179cSPing-Ke Shih const struct rtw_chip_info *chip = rtwdev->chip; 1134d91277deSPing-Ke Shih struct rtw_fifo_conf *fifo = &rtwdev->fifo; 1135d91277deSPing-Ke Shih 1136d91277deSPing-Ke Shih rtw_write16(rtwdev, REG_FIFOPAGE_INFO_1, pg_tbl->hq_num); 1137d91277deSPing-Ke Shih rtw_write16(rtwdev, REG_FIFOPAGE_INFO_2, pg_tbl->lq_num); 1138d91277deSPing-Ke Shih rtw_write16(rtwdev, REG_FIFOPAGE_INFO_3, pg_tbl->nq_num); 1139d91277deSPing-Ke Shih rtw_write16(rtwdev, REG_FIFOPAGE_INFO_4, pg_tbl->exq_num); 1140d91277deSPing-Ke Shih rtw_write16(rtwdev, REG_FIFOPAGE_INFO_5, pubq_num); 1141d91277deSPing-Ke Shih rtw_write32_set(rtwdev, REG_RQPN_CTRL_2, BIT_LD_RQPN); 1142d91277deSPing-Ke Shih 1143d91277deSPing-Ke Shih rtw_write16(rtwdev, REG_FIFOPAGE_CTRL_2, fifo->rsvd_boundary); 1144d91277deSPing-Ke Shih rtw_write8_set(rtwdev, REG_FWHW_TXQ_CTRL + 2, BIT_EN_WR_FREE_TAIL >> 16); 1145d91277deSPing-Ke Shih 1146d91277deSPing-Ke Shih rtw_write16(rtwdev, REG_BCNQ_BDNY_V1, fifo->rsvd_boundary); 1147d91277deSPing-Ke Shih rtw_write16(rtwdev, REG_FIFOPAGE_CTRL_2 + 2, fifo->rsvd_boundary); 1148d91277deSPing-Ke Shih rtw_write16(rtwdev, REG_BCNQ1_BDNY_V1, fifo->rsvd_boundary); 1149d91277deSPing-Ke Shih rtw_write32(rtwdev, REG_RXFF_BNDY, chip->rxff_size - C2H_PKT_BUF - 1); 1150d91277deSPing-Ke Shih rtw_write8_set(rtwdev, REG_AUTO_LLT_V1, BIT_AUTO_INIT_LLT_V1); 1151d91277deSPing-Ke Shih 1152d91277deSPing-Ke Shih if (!check_hw_ready(rtwdev, REG_AUTO_LLT_V1, BIT_AUTO_INIT_LLT_V1, 0)) 1153d91277deSPing-Ke Shih return -EBUSY; 1154d91277deSPing-Ke Shih 1155d91277deSPing-Ke Shih rtw_write8(rtwdev, REG_CR + 3, 0); 1156d91277deSPing-Ke Shih 1157d91277deSPing-Ke Shih return 0; 1158d91277deSPing-Ke Shih } 1159d91277deSPing-Ke Shih 1160d91277deSPing-Ke Shih static int __priority_queue_cfg_legacy(struct rtw_dev *rtwdev, 1161d91277deSPing-Ke Shih const struct rtw_page_table *pg_tbl, 1162d91277deSPing-Ke Shih u16 pubq_num) 1163d91277deSPing-Ke Shih { 1164dcbf179cSPing-Ke Shih const struct rtw_chip_info *chip = rtwdev->chip; 1165d91277deSPing-Ke Shih struct rtw_fifo_conf *fifo = &rtwdev->fifo; 1166d91277deSPing-Ke Shih u32 val32; 1167d91277deSPing-Ke Shih 1168d91277deSPing-Ke Shih val32 = BIT_RQPN_NE(pg_tbl->nq_num, pg_tbl->exq_num); 1169d91277deSPing-Ke Shih rtw_write32(rtwdev, REG_RQPN_NPQ, val32); 1170d91277deSPing-Ke Shih val32 = BIT_RQPN_HLP(pg_tbl->hq_num, pg_tbl->lq_num, pubq_num); 1171d91277deSPing-Ke Shih rtw_write32(rtwdev, REG_RQPN, val32); 1172d91277deSPing-Ke Shih 1173d91277deSPing-Ke Shih rtw_write8(rtwdev, REG_TRXFF_BNDY, fifo->rsvd_boundary); 1174d91277deSPing-Ke Shih rtw_write16(rtwdev, REG_TRXFF_BNDY + 2, chip->rxff_size - REPORT_BUF - 1); 1175d91277deSPing-Ke Shih rtw_write8(rtwdev, REG_DWBCN0_CTRL + 1, fifo->rsvd_boundary); 1176d91277deSPing-Ke Shih rtw_write8(rtwdev, REG_BCNQ_BDNY, fifo->rsvd_boundary); 1177d91277deSPing-Ke Shih rtw_write8(rtwdev, REG_MGQ_BDNY, fifo->rsvd_boundary); 1178d91277deSPing-Ke Shih rtw_write8(rtwdev, REG_WMAC_LBK_BF_HD, fifo->rsvd_boundary); 1179d91277deSPing-Ke Shih 1180d91277deSPing-Ke Shih rtw_write32_set(rtwdev, REG_AUTO_LLT, BIT_AUTO_INIT_LLT); 1181d91277deSPing-Ke Shih 1182d91277deSPing-Ke Shih if (!check_hw_ready(rtwdev, REG_AUTO_LLT, BIT_AUTO_INIT_LLT, 0)) 1183d91277deSPing-Ke Shih return -EBUSY; 1184d91277deSPing-Ke Shih 1185d91277deSPing-Ke Shih return 0; 1186d91277deSPing-Ke Shih } 1187d91277deSPing-Ke Shih 1188e3037485SYan-Hsuan Chuang static int priority_queue_cfg(struct rtw_dev *rtwdev) 1189e3037485SYan-Hsuan Chuang { 1190dcbf179cSPing-Ke Shih const struct rtw_chip_info *chip = rtwdev->chip; 1191e3037485SYan-Hsuan Chuang struct rtw_fifo_conf *fifo = &rtwdev->fifo; 1192d49f2c50SJoe Perches const struct rtw_page_table *pg_tbl = NULL; 1193e3037485SYan-Hsuan Chuang u16 pubq_num; 1194e3037485SYan-Hsuan Chuang int ret; 1195e3037485SYan-Hsuan Chuang 1196e3037485SYan-Hsuan Chuang ret = set_trx_fifo_info(rtwdev); 1197e3037485SYan-Hsuan Chuang if (ret) 1198e3037485SYan-Hsuan Chuang return ret; 1199e3037485SYan-Hsuan Chuang 1200e3037485SYan-Hsuan Chuang switch (rtw_hci_type(rtwdev)) { 1201e3037485SYan-Hsuan Chuang case RTW_HCI_TYPE_PCIE: 1202e3037485SYan-Hsuan Chuang pg_tbl = &chip->page_table[1]; 1203e3037485SYan-Hsuan Chuang break; 1204e3037485SYan-Hsuan Chuang case RTW_HCI_TYPE_USB: 1205e3037485SYan-Hsuan Chuang if (rtwdev->hci.bulkout_num == 2) 1206e3037485SYan-Hsuan Chuang pg_tbl = &chip->page_table[2]; 1207e3037485SYan-Hsuan Chuang else if (rtwdev->hci.bulkout_num == 3) 1208e3037485SYan-Hsuan Chuang pg_tbl = &chip->page_table[3]; 1209e3037485SYan-Hsuan Chuang else if (rtwdev->hci.bulkout_num == 4) 1210e3037485SYan-Hsuan Chuang pg_tbl = &chip->page_table[4]; 1211e3037485SYan-Hsuan Chuang else 1212e3037485SYan-Hsuan Chuang return -EINVAL; 1213e3037485SYan-Hsuan Chuang break; 12148599ea40SMartin Blumenstingl case RTW_HCI_TYPE_SDIO: 12158599ea40SMartin Blumenstingl pg_tbl = &chip->page_table[0]; 12168599ea40SMartin Blumenstingl break; 1217e3037485SYan-Hsuan Chuang default: 1218e3037485SYan-Hsuan Chuang return -EINVAL; 1219e3037485SYan-Hsuan Chuang } 1220e3037485SYan-Hsuan Chuang 1221e3037485SYan-Hsuan Chuang pubq_num = fifo->acq_pg_num - pg_tbl->hq_num - pg_tbl->lq_num - 1222e3037485SYan-Hsuan Chuang pg_tbl->nq_num - pg_tbl->exq_num - pg_tbl->gapq_num; 1223d91277deSPing-Ke Shih if (rtw_chip_wcpu_11n(rtwdev)) 1224d91277deSPing-Ke Shih return __priority_queue_cfg_legacy(rtwdev, pg_tbl, pubq_num); 1225d91277deSPing-Ke Shih else 1226d91277deSPing-Ke Shih return __priority_queue_cfg(rtwdev, pg_tbl, pubq_num); 1227e3037485SYan-Hsuan Chuang } 1228e3037485SYan-Hsuan Chuang 1229e3037485SYan-Hsuan Chuang static int init_h2c(struct rtw_dev *rtwdev) 1230e3037485SYan-Hsuan Chuang { 1231e3037485SYan-Hsuan Chuang struct rtw_fifo_conf *fifo = &rtwdev->fifo; 1232e3037485SYan-Hsuan Chuang u8 value8; 1233e3037485SYan-Hsuan Chuang u32 value32; 1234e3037485SYan-Hsuan Chuang u32 h2cq_addr; 1235e3037485SYan-Hsuan Chuang u32 h2cq_size; 1236e3037485SYan-Hsuan Chuang u32 h2cq_free; 1237e3037485SYan-Hsuan Chuang u32 wp, rp; 1238e3037485SYan-Hsuan Chuang 12397907b52dSPing-Ke Shih if (rtw_chip_wcpu_11n(rtwdev)) 12407907b52dSPing-Ke Shih return 0; 12417907b52dSPing-Ke Shih 1242e3037485SYan-Hsuan Chuang h2cq_addr = fifo->rsvd_h2cq_addr << TX_PAGE_SIZE_SHIFT; 1243e3037485SYan-Hsuan Chuang h2cq_size = RSVD_PG_H2CQ_NUM << TX_PAGE_SIZE_SHIFT; 1244e3037485SYan-Hsuan Chuang 1245e3037485SYan-Hsuan Chuang value32 = rtw_read32(rtwdev, REG_H2C_HEAD); 1246e3037485SYan-Hsuan Chuang value32 = (value32 & 0xFFFC0000) | h2cq_addr; 1247e3037485SYan-Hsuan Chuang rtw_write32(rtwdev, REG_H2C_HEAD, value32); 1248e3037485SYan-Hsuan Chuang 1249e3037485SYan-Hsuan Chuang value32 = rtw_read32(rtwdev, REG_H2C_READ_ADDR); 1250e3037485SYan-Hsuan Chuang value32 = (value32 & 0xFFFC0000) | h2cq_addr; 1251e3037485SYan-Hsuan Chuang rtw_write32(rtwdev, REG_H2C_READ_ADDR, value32); 1252e3037485SYan-Hsuan Chuang 1253e3037485SYan-Hsuan Chuang value32 = rtw_read32(rtwdev, REG_H2C_TAIL); 1254e3037485SYan-Hsuan Chuang value32 &= 0xFFFC0000; 1255e3037485SYan-Hsuan Chuang value32 |= (h2cq_addr + h2cq_size); 1256e3037485SYan-Hsuan Chuang rtw_write32(rtwdev, REG_H2C_TAIL, value32); 1257e3037485SYan-Hsuan Chuang 1258e3037485SYan-Hsuan Chuang value8 = rtw_read8(rtwdev, REG_H2C_INFO); 1259e3037485SYan-Hsuan Chuang value8 = (u8)((value8 & 0xFC) | 0x01); 1260e3037485SYan-Hsuan Chuang rtw_write8(rtwdev, REG_H2C_INFO, value8); 1261e3037485SYan-Hsuan Chuang 1262e3037485SYan-Hsuan Chuang value8 = rtw_read8(rtwdev, REG_H2C_INFO); 1263e3037485SYan-Hsuan Chuang value8 = (u8)((value8 & 0xFB) | 0x04); 1264e3037485SYan-Hsuan Chuang rtw_write8(rtwdev, REG_H2C_INFO, value8); 1265e3037485SYan-Hsuan Chuang 1266e3037485SYan-Hsuan Chuang value8 = rtw_read8(rtwdev, REG_TXDMA_OFFSET_CHK + 1); 1267e3037485SYan-Hsuan Chuang value8 = (u8)((value8 & 0x7f) | 0x80); 1268e3037485SYan-Hsuan Chuang rtw_write8(rtwdev, REG_TXDMA_OFFSET_CHK + 1, value8); 1269e3037485SYan-Hsuan Chuang 1270e3037485SYan-Hsuan Chuang wp = rtw_read32(rtwdev, REG_H2C_PKT_WRITEADDR) & 0x3FFFF; 1271e3037485SYan-Hsuan Chuang rp = rtw_read32(rtwdev, REG_H2C_PKT_READADDR) & 0x3FFFF; 1272e3037485SYan-Hsuan Chuang h2cq_free = wp >= rp ? h2cq_size - (wp - rp) : rp - wp; 1273e3037485SYan-Hsuan Chuang 1274e3037485SYan-Hsuan Chuang if (h2cq_size != h2cq_free) { 1275e3037485SYan-Hsuan Chuang rtw_err(rtwdev, "H2C queue mismatch\n"); 1276e3037485SYan-Hsuan Chuang return -EINVAL; 1277e3037485SYan-Hsuan Chuang } 1278e3037485SYan-Hsuan Chuang 1279e3037485SYan-Hsuan Chuang return 0; 1280e3037485SYan-Hsuan Chuang } 1281e3037485SYan-Hsuan Chuang 1282e3037485SYan-Hsuan Chuang static int rtw_init_trx_cfg(struct rtw_dev *rtwdev) 1283e3037485SYan-Hsuan Chuang { 1284e3037485SYan-Hsuan Chuang int ret; 1285e3037485SYan-Hsuan Chuang 1286e3037485SYan-Hsuan Chuang ret = txdma_queue_mapping(rtwdev); 1287e3037485SYan-Hsuan Chuang if (ret) 1288e3037485SYan-Hsuan Chuang return ret; 1289e3037485SYan-Hsuan Chuang 1290e3037485SYan-Hsuan Chuang ret = priority_queue_cfg(rtwdev); 1291e3037485SYan-Hsuan Chuang if (ret) 1292e3037485SYan-Hsuan Chuang return ret; 1293e3037485SYan-Hsuan Chuang 1294e3037485SYan-Hsuan Chuang ret = init_h2c(rtwdev); 1295e3037485SYan-Hsuan Chuang if (ret) 1296e3037485SYan-Hsuan Chuang return ret; 1297e3037485SYan-Hsuan Chuang 1298e3037485SYan-Hsuan Chuang return 0; 1299e3037485SYan-Hsuan Chuang } 1300e3037485SYan-Hsuan Chuang 1301e3037485SYan-Hsuan Chuang static int rtw_drv_info_cfg(struct rtw_dev *rtwdev) 1302e3037485SYan-Hsuan Chuang { 1303e3037485SYan-Hsuan Chuang u8 value8; 1304e3037485SYan-Hsuan Chuang 1305e3037485SYan-Hsuan Chuang rtw_write8(rtwdev, REG_RX_DRVINFO_SZ, PHY_STATUS_SIZE); 1306d91277deSPing-Ke Shih if (rtw_chip_wcpu_11ac(rtwdev)) { 1307e3037485SYan-Hsuan Chuang value8 = rtw_read8(rtwdev, REG_TRXFF_BNDY + 1); 1308e3037485SYan-Hsuan Chuang value8 &= 0xF0; 1309e3037485SYan-Hsuan Chuang /* For rxdesc len = 0 issue */ 1310e3037485SYan-Hsuan Chuang value8 |= 0xF; 1311e3037485SYan-Hsuan Chuang rtw_write8(rtwdev, REG_TRXFF_BNDY + 1, value8); 1312d91277deSPing-Ke Shih } 1313e3037485SYan-Hsuan Chuang rtw_write32_set(rtwdev, REG_RCR, BIT_APP_PHYSTS); 1314e3037485SYan-Hsuan Chuang rtw_write32_clr(rtwdev, REG_WMAC_OPTION_FUNCTION + 4, BIT(8) | BIT(9)); 1315e3037485SYan-Hsuan Chuang 1316e3037485SYan-Hsuan Chuang return 0; 1317e3037485SYan-Hsuan Chuang } 1318e3037485SYan-Hsuan Chuang 1319e3037485SYan-Hsuan Chuang int rtw_mac_init(struct rtw_dev *rtwdev) 1320e3037485SYan-Hsuan Chuang { 1321dcbf179cSPing-Ke Shih const struct rtw_chip_info *chip = rtwdev->chip; 1322e3037485SYan-Hsuan Chuang int ret; 1323e3037485SYan-Hsuan Chuang 1324e3037485SYan-Hsuan Chuang ret = rtw_init_trx_cfg(rtwdev); 1325e3037485SYan-Hsuan Chuang if (ret) 1326e3037485SYan-Hsuan Chuang return ret; 1327e3037485SYan-Hsuan Chuang 1328e3037485SYan-Hsuan Chuang ret = chip->ops->mac_init(rtwdev); 1329e3037485SYan-Hsuan Chuang if (ret) 1330e3037485SYan-Hsuan Chuang return ret; 1331e3037485SYan-Hsuan Chuang 1332e3037485SYan-Hsuan Chuang ret = rtw_drv_info_cfg(rtwdev); 1333e3037485SYan-Hsuan Chuang if (ret) 1334e3037485SYan-Hsuan Chuang return ret; 1335e3037485SYan-Hsuan Chuang 133678622104SYan-Hsuan Chuang rtw_hci_interface_cfg(rtwdev); 133778622104SYan-Hsuan Chuang 1338e3037485SYan-Hsuan Chuang return 0; 1339e3037485SYan-Hsuan Chuang } 1340