| // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause |
| /* Copyright 2024 Fiona Klute |
| * |
| * Based on code originally in rtw8723d.[ch], |
| * Copyright(c) 2018-2019 Realtek Corporation |
| */ |
| |
| #include "main.h" |
| #include "debug.h" |
| #include "phy.h" |
| #include "reg.h" |
| #include "tx.h" |
| #include "rtw8723x.h" |
| |
| static const struct rtw_hw_reg rtw8723x_txagc[] = { |
| [DESC_RATE1M] = { .addr = 0xe08, .mask = 0x0000ff00 }, |
| [DESC_RATE2M] = { .addr = 0x86c, .mask = 0x0000ff00 }, |
| [DESC_RATE5_5M] = { .addr = 0x86c, .mask = 0x00ff0000 }, |
| [DESC_RATE11M] = { .addr = 0x86c, .mask = 0xff000000 }, |
| [DESC_RATE6M] = { .addr = 0xe00, .mask = 0x000000ff }, |
| [DESC_RATE9M] = { .addr = 0xe00, .mask = 0x0000ff00 }, |
| [DESC_RATE12M] = { .addr = 0xe00, .mask = 0x00ff0000 }, |
| [DESC_RATE18M] = { .addr = 0xe00, .mask = 0xff000000 }, |
| [DESC_RATE24M] = { .addr = 0xe04, .mask = 0x000000ff }, |
| [DESC_RATE36M] = { .addr = 0xe04, .mask = 0x0000ff00 }, |
| [DESC_RATE48M] = { .addr = 0xe04, .mask = 0x00ff0000 }, |
| [DESC_RATE54M] = { .addr = 0xe04, .mask = 0xff000000 }, |
| [DESC_RATEMCS0] = { .addr = 0xe10, .mask = 0x000000ff }, |
| [DESC_RATEMCS1] = { .addr = 0xe10, .mask = 0x0000ff00 }, |
| [DESC_RATEMCS2] = { .addr = 0xe10, .mask = 0x00ff0000 }, |
| [DESC_RATEMCS3] = { .addr = 0xe10, .mask = 0xff000000 }, |
| [DESC_RATEMCS4] = { .addr = 0xe14, .mask = 0x000000ff }, |
| [DESC_RATEMCS5] = { .addr = 0xe14, .mask = 0x0000ff00 }, |
| [DESC_RATEMCS6] = { .addr = 0xe14, .mask = 0x00ff0000 }, |
| [DESC_RATEMCS7] = { .addr = 0xe14, .mask = 0xff000000 }, |
| }; |
| |
| static void __rtw8723x_lck(struct rtw_dev *rtwdev) |
| { |
| u32 lc_cal; |
| u8 val_ctx, rf_val; |
| int ret; |
| |
| val_ctx = rtw_read8(rtwdev, REG_CTX); |
| if ((val_ctx & BIT_MASK_CTX_TYPE) != 0) |
| rtw_write8(rtwdev, REG_CTX, val_ctx & ~BIT_MASK_CTX_TYPE); |
| else |
| rtw_write8(rtwdev, REG_TXPAUSE, 0xFF); |
| lc_cal = rtw_read_rf(rtwdev, RF_PATH_A, RF_CFGCH, RFREG_MASK); |
| |
| rtw_write_rf(rtwdev, RF_PATH_A, RF_CFGCH, RFREG_MASK, lc_cal | BIT_LCK); |
| |
| ret = read_poll_timeout(rtw_read_rf, rf_val, rf_val != 0x1, |
| 10000, 1000000, false, |
| rtwdev, RF_PATH_A, RF_CFGCH, BIT_LCK); |
| if (ret) |
| rtw_warn(rtwdev, "failed to poll LCK status bit\n"); |
| |
| rtw_write_rf(rtwdev, RF_PATH_A, RF_CFGCH, RFREG_MASK, lc_cal); |
| if ((val_ctx & BIT_MASK_CTX_TYPE) != 0) |
| rtw_write8(rtwdev, REG_CTX, val_ctx); |
| else |
| rtw_write8(rtwdev, REG_TXPAUSE, 0x00); |
| } |
| |
| #define DBG_EFUSE_VAL(rtwdev, map, name) \ |
| rtw_dbg(rtwdev, RTW_DBG_EFUSE, # name "=0x%02x\n", \ |
| (map)->name) |
| #define DBG_EFUSE_2BYTE(rtwdev, map, name) \ |
| rtw_dbg(rtwdev, RTW_DBG_EFUSE, # name "=0x%02x%02x\n", \ |
| (map)->name[0], (map)->name[1]) |
| |
| static void rtw8723xe_efuse_debug(struct rtw_dev *rtwdev, |
| struct rtw8723x_efuse *map) |
| { |
| rtw_dbg(rtwdev, RTW_DBG_EFUSE, "mac_addr=%pM\n", map->e.mac_addr); |
| DBG_EFUSE_2BYTE(rtwdev, map, e.vendor_id); |
| DBG_EFUSE_2BYTE(rtwdev, map, e.device_id); |
| DBG_EFUSE_2BYTE(rtwdev, map, e.sub_vendor_id); |
| DBG_EFUSE_2BYTE(rtwdev, map, e.sub_device_id); |
| } |
| |
| static void rtw8723xu_efuse_debug(struct rtw_dev *rtwdev, |
| struct rtw8723x_efuse *map) |
| { |
| DBG_EFUSE_2BYTE(rtwdev, map, u.vendor_id); |
| DBG_EFUSE_2BYTE(rtwdev, map, u.product_id); |
| DBG_EFUSE_VAL(rtwdev, map, u.usb_option); |
| rtw_dbg(rtwdev, RTW_DBG_EFUSE, "mac_addr=%pM\n", map->u.mac_addr); |
| } |
| |
| static void rtw8723xs_efuse_debug(struct rtw_dev *rtwdev, |
| struct rtw8723x_efuse *map) |
| { |
| rtw_dbg(rtwdev, RTW_DBG_EFUSE, "mac_addr=%pM\n", map->s.mac_addr); |
| } |
| |
| static void __rtw8723x_debug_txpwr_limit(struct rtw_dev *rtwdev, |
| struct rtw_txpwr_idx *table, |
| int tx_path_count) |
| { |
| if (!rtw_dbg_is_enabled(rtwdev, RTW_DBG_EFUSE)) |
| return; |
| |
| rtw_dbg(rtwdev, RTW_DBG_EFUSE, |
| "Power index table (2.4G):\n"); |
| /* CCK base */ |
| rtw_dbg(rtwdev, RTW_DBG_EFUSE, "CCK base\n"); |
| rtw_dbg(rtwdev, RTW_DBG_EFUSE, "RF G0 G1 G2 G3 G4 G5\n"); |
| for (int i = 0; i < tx_path_count; i++) |
| rtw_dbg(rtwdev, RTW_DBG_EFUSE, |
| "[%c]: %3u %3u %3u %3u %3u %3u\n", |
| 'A' + i, |
| table[i].pwr_idx_2g.cck_base[0], |
| table[i].pwr_idx_2g.cck_base[1], |
| table[i].pwr_idx_2g.cck_base[2], |
| table[i].pwr_idx_2g.cck_base[3], |
| table[i].pwr_idx_2g.cck_base[4], |
| table[i].pwr_idx_2g.cck_base[5]); |
| /* CCK diff */ |
| rtw_dbg(rtwdev, RTW_DBG_EFUSE, "CCK diff\n"); |
| rtw_dbg(rtwdev, RTW_DBG_EFUSE, "RF 1S 2S 3S 4S\n"); |
| for (int i = 0; i < tx_path_count; i++) |
| rtw_dbg(rtwdev, RTW_DBG_EFUSE, |
| "[%c]: %2d %2d %2d %2d\n", |
| 'A' + i, 0 /* no diff for 1S */, |
| table[i].pwr_idx_2g.ht_2s_diff.cck, |
| table[i].pwr_idx_2g.ht_3s_diff.cck, |
| table[i].pwr_idx_2g.ht_4s_diff.cck); |
| /* BW40-1S base */ |
| rtw_dbg(rtwdev, RTW_DBG_EFUSE, "BW40-1S base\n"); |
| rtw_dbg(rtwdev, RTW_DBG_EFUSE, "RF G0 G1 G2 G3 G4\n"); |
| for (int i = 0; i < tx_path_count; i++) |
| rtw_dbg(rtwdev, RTW_DBG_EFUSE, |
| "[%c]: %3u %3u %3u %3u %3u\n", |
| 'A' + i, |
| table[i].pwr_idx_2g.bw40_base[0], |
| table[i].pwr_idx_2g.bw40_base[1], |
| table[i].pwr_idx_2g.bw40_base[2], |
| table[i].pwr_idx_2g.bw40_base[3], |
| table[i].pwr_idx_2g.bw40_base[4]); |
| /* OFDM diff */ |
| rtw_dbg(rtwdev, RTW_DBG_EFUSE, "OFDM diff\n"); |
| rtw_dbg(rtwdev, RTW_DBG_EFUSE, "RF 1S 2S 3S 4S\n"); |
| for (int i = 0; i < tx_path_count; i++) |
| rtw_dbg(rtwdev, RTW_DBG_EFUSE, |
| "[%c]: %2d %2d %2d %2d\n", |
| 'A' + i, |
| table[i].pwr_idx_2g.ht_1s_diff.ofdm, |
| table[i].pwr_idx_2g.ht_2s_diff.ofdm, |
| table[i].pwr_idx_2g.ht_3s_diff.ofdm, |
| table[i].pwr_idx_2g.ht_4s_diff.ofdm); |
| /* BW20 diff */ |
| rtw_dbg(rtwdev, RTW_DBG_EFUSE, "BW20 diff\n"); |
| rtw_dbg(rtwdev, RTW_DBG_EFUSE, "RF 1S 2S 3S 4S\n"); |
| for (int i = 0; i < tx_path_count; i++) |
| rtw_dbg(rtwdev, RTW_DBG_EFUSE, |
| "[%c]: %2d %2d %2d %2d\n", |
| 'A' + i, |
| table[i].pwr_idx_2g.ht_1s_diff.bw20, |
| table[i].pwr_idx_2g.ht_2s_diff.bw20, |
| table[i].pwr_idx_2g.ht_3s_diff.bw20, |
| table[i].pwr_idx_2g.ht_4s_diff.bw20); |
| /* BW40 diff */ |
| rtw_dbg(rtwdev, RTW_DBG_EFUSE, "BW40 diff\n"); |
| rtw_dbg(rtwdev, RTW_DBG_EFUSE, "RF 1S 2S 3S 4S\n"); |
| for (int i = 0; i < tx_path_count; i++) |
| rtw_dbg(rtwdev, RTW_DBG_EFUSE, |
| "[%c]: %2d %2d %2d %2d\n", |
| 'A' + i, 0 /* no diff for 1S */, |
| table[i].pwr_idx_2g.ht_2s_diff.bw40, |
| table[i].pwr_idx_2g.ht_3s_diff.bw40, |
| table[i].pwr_idx_2g.ht_4s_diff.bw40); |
| } |
| |
| static void efuse_debug_dump(struct rtw_dev *rtwdev, |
| struct rtw8723x_efuse *map) |
| { |
| if (!rtw_dbg_is_enabled(rtwdev, RTW_DBG_EFUSE)) |
| return; |
| |
| rtw_dbg(rtwdev, RTW_DBG_EFUSE, "EFUSE raw logical map:\n"); |
| print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, 16, 1, |
| (u8 *)map, sizeof(struct rtw8723x_efuse), false); |
| rtw_dbg(rtwdev, RTW_DBG_EFUSE, "Parsed rtw8723x EFUSE data:\n"); |
| DBG_EFUSE_VAL(rtwdev, map, rtl_id); |
| DBG_EFUSE_VAL(rtwdev, map, afe); |
| rtw8723x_debug_txpwr_limit(rtwdev, map->txpwr_idx_table, 4); |
| DBG_EFUSE_VAL(rtwdev, map, channel_plan); |
| DBG_EFUSE_VAL(rtwdev, map, xtal_k); |
| DBG_EFUSE_VAL(rtwdev, map, thermal_meter); |
| DBG_EFUSE_VAL(rtwdev, map, iqk_lck); |
| DBG_EFUSE_VAL(rtwdev, map, pa_type); |
| DBG_EFUSE_2BYTE(rtwdev, map, lna_type_2g); |
| DBG_EFUSE_2BYTE(rtwdev, map, lna_type_5g); |
| DBG_EFUSE_VAL(rtwdev, map, rf_board_option); |
| DBG_EFUSE_VAL(rtwdev, map, rf_feature_option); |
| DBG_EFUSE_VAL(rtwdev, map, rf_bt_setting); |
| DBG_EFUSE_VAL(rtwdev, map, eeprom_version); |
| DBG_EFUSE_VAL(rtwdev, map, eeprom_customer_id); |
| DBG_EFUSE_VAL(rtwdev, map, tx_bb_swing_setting_2g); |
| DBG_EFUSE_VAL(rtwdev, map, tx_pwr_calibrate_rate); |
| DBG_EFUSE_VAL(rtwdev, map, rf_antenna_option); |
| DBG_EFUSE_VAL(rtwdev, map, rfe_option); |
| DBG_EFUSE_2BYTE(rtwdev, map, country_code); |
| |
| switch (rtw_hci_type(rtwdev)) { |
| case RTW_HCI_TYPE_PCIE: |
| rtw8723xe_efuse_debug(rtwdev, map); |
| break; |
| case RTW_HCI_TYPE_USB: |
| rtw8723xu_efuse_debug(rtwdev, map); |
| break; |
| case RTW_HCI_TYPE_SDIO: |
| rtw8723xs_efuse_debug(rtwdev, map); |
| break; |
| default: |
| /* unsupported now */ |
| break; |
| } |
| } |
| |
| static void rtw8723xe_efuse_parsing(struct rtw_efuse *efuse, |
| struct rtw8723x_efuse *map) |
| { |
| ether_addr_copy(efuse->addr, map->e.mac_addr); |
| } |
| |
| static void rtw8723xu_efuse_parsing(struct rtw_efuse *efuse, |
| struct rtw8723x_efuse *map) |
| { |
| ether_addr_copy(efuse->addr, map->u.mac_addr); |
| } |
| |
| static void rtw8723xs_efuse_parsing(struct rtw_efuse *efuse, |
| struct rtw8723x_efuse *map) |
| { |
| ether_addr_copy(efuse->addr, map->s.mac_addr); |
| } |
| |
| static int __rtw8723x_read_efuse(struct rtw_dev *rtwdev, u8 *log_map) |
| { |
| struct rtw_efuse *efuse = &rtwdev->efuse; |
| struct rtw8723x_efuse *map; |
| int i; |
| |
| map = (struct rtw8723x_efuse *)log_map; |
| efuse_debug_dump(rtwdev, map); |
| |
| efuse->rfe_option = 0; |
| efuse->rf_board_option = map->rf_board_option; |
| efuse->crystal_cap = map->xtal_k; |
| efuse->pa_type_2g = map->pa_type; |
| efuse->lna_type_2g = map->lna_type_2g[0]; |
| efuse->channel_plan = map->channel_plan; |
| efuse->country_code[0] = map->country_code[0]; |
| efuse->country_code[1] = map->country_code[1]; |
| efuse->bt_setting = map->rf_bt_setting; |
| efuse->regd = map->rf_board_option & 0x7; |
| efuse->thermal_meter[0] = map->thermal_meter; |
| efuse->thermal_meter_k = map->thermal_meter; |
| efuse->afe = map->afe; |
| |
| for (i = 0; i < 4; i++) |
| efuse->txpwr_idx_table[i] = map->txpwr_idx_table[i]; |
| |
| switch (rtw_hci_type(rtwdev)) { |
| case RTW_HCI_TYPE_PCIE: |
| rtw8723xe_efuse_parsing(efuse, map); |
| break; |
| case RTW_HCI_TYPE_USB: |
| rtw8723xu_efuse_parsing(efuse, map); |
| break; |
| case RTW_HCI_TYPE_SDIO: |
| rtw8723xs_efuse_parsing(efuse, map); |
| break; |
| default: |
| /* unsupported now */ |
| return -EOPNOTSUPP; |
| } |
| |
| return 0; |
| } |
| |
| #define BIT_CFENDFORM BIT(9) |
| #define BIT_WMAC_TCR_ERR0 BIT(12) |
| #define BIT_WMAC_TCR_ERR1 BIT(13) |
| #define BIT_TCR_CFG (BIT_CFENDFORM | BIT_WMAC_TCR_ERR0 | \ |
| BIT_WMAC_TCR_ERR1) |
| #define WLAN_RX_FILTER0 0xFFFF |
| #define WLAN_RX_FILTER1 0x400 |
| #define WLAN_RX_FILTER2 0xFFFF |
| #define WLAN_RCR_CFG 0x700060CE |
| |
| static int __rtw8723x_mac_init(struct rtw_dev *rtwdev) |
| { |
| rtw_write8(rtwdev, REG_FWHW_TXQ_CTRL + 1, WLAN_TXQ_RPT_EN); |
| rtw_write32(rtwdev, REG_TCR, BIT_TCR_CFG); |
| |
| rtw_write16(rtwdev, REG_RXFLTMAP0, WLAN_RX_FILTER0); |
| rtw_write16(rtwdev, REG_RXFLTMAP1, WLAN_RX_FILTER1); |
| rtw_write16(rtwdev, REG_RXFLTMAP2, WLAN_RX_FILTER2); |
| rtw_write32(rtwdev, REG_RCR, WLAN_RCR_CFG); |
| |
| rtw_write32(rtwdev, REG_INT_MIG, 0); |
| rtw_write32(rtwdev, REG_MCUTST_1, 0x0); |
| |
| rtw_write8(rtwdev, REG_MISC_CTRL, BIT_DIS_SECOND_CCA); |
| rtw_write8(rtwdev, REG_2ND_CCA_CTRL, 0); |
| |
| return 0; |
| } |
| |
| static void __rtw8723x_cfg_ldo25(struct rtw_dev *rtwdev, bool enable) |
| { |
| u8 ldo_pwr; |
| |
| ldo_pwr = rtw_read8(rtwdev, REG_LDO_EFUSE_CTRL + 3); |
| if (enable) { |
| ldo_pwr &= ~BIT_MASK_LDO25_VOLTAGE; |
| ldo_pwr |= (BIT_LDO25_VOLTAGE_V25 << 4) | BIT_LDO25_EN; |
| } else { |
| ldo_pwr &= ~BIT_LDO25_EN; |
| } |
| rtw_write8(rtwdev, REG_LDO_EFUSE_CTRL + 3, ldo_pwr); |
| } |
| |
| static void |
| rtw8723x_set_tx_power_index_by_rate(struct rtw_dev *rtwdev, u8 path, u8 rs) |
| { |
| struct rtw_hal *hal = &rtwdev->hal; |
| const struct rtw_hw_reg *txagc; |
| u8 rate, pwr_index; |
| int j; |
| |
| for (j = 0; j < rtw_rate_size[rs]; j++) { |
| rate = rtw_rate_section[rs][j]; |
| pwr_index = hal->tx_pwr_tbl[path][rate]; |
| |
| if (rate >= ARRAY_SIZE(rtw8723x_txagc)) { |
| rtw_warn(rtwdev, "rate 0x%x isn't supported\n", rate); |
| continue; |
| } |
| txagc = &rtw8723x_txagc[rate]; |
| if (!txagc->addr) { |
| rtw_warn(rtwdev, "rate 0x%x isn't defined\n", rate); |
| continue; |
| } |
| |
| rtw_write32_mask(rtwdev, txagc->addr, txagc->mask, pwr_index); |
| } |
| } |
| |
| static void __rtw8723x_set_tx_power_index(struct rtw_dev *rtwdev) |
| { |
| struct rtw_hal *hal = &rtwdev->hal; |
| int rs, path; |
| |
| for (path = 0; path < hal->rf_path_num; path++) { |
| for (rs = 0; rs <= RTW_RATE_SECTION_HT_1S; rs++) |
| rtw8723x_set_tx_power_index_by_rate(rtwdev, path, rs); |
| } |
| } |
| |
| static void __rtw8723x_efuse_grant(struct rtw_dev *rtwdev, bool on) |
| { |
| if (on) { |
| rtw_write8(rtwdev, REG_EFUSE_ACCESS, EFUSE_ACCESS_ON); |
| |
| rtw_write16_set(rtwdev, REG_SYS_FUNC_EN, BIT_FEN_ELDR); |
| rtw_write16_set(rtwdev, REG_SYS_CLKR, BIT_LOADER_CLK_EN | BIT_ANA8M); |
| } else { |
| rtw_write8(rtwdev, REG_EFUSE_ACCESS, EFUSE_ACCESS_OFF); |
| } |
| } |
| |
| static void __rtw8723x_false_alarm_statistics(struct rtw_dev *rtwdev) |
| { |
| struct rtw_dm_info *dm_info = &rtwdev->dm_info; |
| u32 cck_fa_cnt; |
| u32 ofdm_fa_cnt; |
| u32 crc32_cnt; |
| u32 val32; |
| |
| /* hold counter */ |
| rtw_write32_mask(rtwdev, REG_OFDM_FA_HOLDC_11N, BIT_MASK_OFDM_FA_KEEP, 1); |
| rtw_write32_mask(rtwdev, REG_OFDM_FA_RSTD_11N, BIT_MASK_OFDM_FA_KEEP1, 1); |
| rtw_write32_mask(rtwdev, REG_CCK_FA_RST_11N, BIT_MASK_CCK_CNT_KEEP, 1); |
| rtw_write32_mask(rtwdev, REG_CCK_FA_RST_11N, BIT_MASK_CCK_FA_KEEP, 1); |
| |
| cck_fa_cnt = rtw_read32_mask(rtwdev, REG_CCK_FA_LSB_11N, MASKBYTE0); |
| cck_fa_cnt += rtw_read32_mask(rtwdev, REG_CCK_FA_MSB_11N, MASKBYTE3) << 8; |
| |
| val32 = rtw_read32(rtwdev, REG_OFDM_FA_TYPE1_11N); |
| ofdm_fa_cnt = u32_get_bits(val32, BIT_MASK_OFDM_FF_CNT); |
| ofdm_fa_cnt += u32_get_bits(val32, BIT_MASK_OFDM_SF_CNT); |
| val32 = rtw_read32(rtwdev, REG_OFDM_FA_TYPE2_11N); |
| dm_info->ofdm_cca_cnt = u32_get_bits(val32, BIT_MASK_OFDM_CCA_CNT); |
| ofdm_fa_cnt += u32_get_bits(val32, BIT_MASK_OFDM_PF_CNT); |
| val32 = rtw_read32(rtwdev, REG_OFDM_FA_TYPE3_11N); |
| ofdm_fa_cnt += u32_get_bits(val32, BIT_MASK_OFDM_RI_CNT); |
| ofdm_fa_cnt += u32_get_bits(val32, BIT_MASK_OFDM_CRC_CNT); |
| val32 = rtw_read32(rtwdev, REG_OFDM_FA_TYPE4_11N); |
| ofdm_fa_cnt += u32_get_bits(val32, BIT_MASK_OFDM_MNS_CNT); |
| |
| dm_info->cck_fa_cnt = cck_fa_cnt; |
| dm_info->ofdm_fa_cnt = ofdm_fa_cnt; |
| dm_info->total_fa_cnt = cck_fa_cnt + ofdm_fa_cnt; |
| |
| dm_info->cck_err_cnt = rtw_read32(rtwdev, REG_IGI_C_11N); |
| dm_info->cck_ok_cnt = rtw_read32(rtwdev, REG_IGI_D_11N); |
| crc32_cnt = rtw_read32(rtwdev, REG_OFDM_CRC32_CNT_11N); |
| dm_info->ofdm_err_cnt = u32_get_bits(crc32_cnt, BIT_MASK_OFDM_LCRC_ERR); |
| dm_info->ofdm_ok_cnt = u32_get_bits(crc32_cnt, BIT_MASK_OFDM_LCRC_OK); |
| crc32_cnt = rtw_read32(rtwdev, REG_HT_CRC32_CNT_11N); |
| dm_info->ht_err_cnt = u32_get_bits(crc32_cnt, BIT_MASK_HT_CRC_ERR); |
| dm_info->ht_ok_cnt = u32_get_bits(crc32_cnt, BIT_MASK_HT_CRC_OK); |
| dm_info->vht_err_cnt = 0; |
| dm_info->vht_ok_cnt = 0; |
| |
| val32 = rtw_read32(rtwdev, REG_CCK_CCA_CNT_11N); |
| dm_info->cck_cca_cnt = (u32_get_bits(val32, BIT_MASK_CCK_FA_MSB) << 8) | |
| u32_get_bits(val32, BIT_MASK_CCK_FA_LSB); |
| dm_info->total_cca_cnt = dm_info->cck_cca_cnt + dm_info->ofdm_cca_cnt; |
| |
| /* reset counter */ |
| rtw_write32_mask(rtwdev, REG_OFDM_FA_RSTC_11N, BIT_MASK_OFDM_FA_RST, 1); |
| rtw_write32_mask(rtwdev, REG_OFDM_FA_RSTC_11N, BIT_MASK_OFDM_FA_RST, 0); |
| rtw_write32_mask(rtwdev, REG_OFDM_FA_RSTD_11N, BIT_MASK_OFDM_FA_RST1, 1); |
| rtw_write32_mask(rtwdev, REG_OFDM_FA_RSTD_11N, BIT_MASK_OFDM_FA_RST1, 0); |
| rtw_write32_mask(rtwdev, REG_OFDM_FA_HOLDC_11N, BIT_MASK_OFDM_FA_KEEP, 0); |
| rtw_write32_mask(rtwdev, REG_OFDM_FA_RSTD_11N, BIT_MASK_OFDM_FA_KEEP1, 0); |
| rtw_write32_mask(rtwdev, REG_CCK_FA_RST_11N, BIT_MASK_CCK_CNT_KPEN, 0); |
| rtw_write32_mask(rtwdev, REG_CCK_FA_RST_11N, BIT_MASK_CCK_CNT_KPEN, 2); |
| rtw_write32_mask(rtwdev, REG_CCK_FA_RST_11N, BIT_MASK_CCK_FA_KPEN, 0); |
| rtw_write32_mask(rtwdev, REG_CCK_FA_RST_11N, BIT_MASK_CCK_FA_KPEN, 2); |
| rtw_write32_mask(rtwdev, REG_PAGE_F_RST_11N, BIT_MASK_F_RST_ALL, 1); |
| rtw_write32_mask(rtwdev, REG_PAGE_F_RST_11N, BIT_MASK_F_RST_ALL, 0); |
| } |
| |
| /* IQK (IQ calibration) */ |
| |
| static |
| void __rtw8723x_iqk_backup_regs(struct rtw_dev *rtwdev, |
| struct rtw8723x_iqk_backup_regs *backup) |
| { |
| int i; |
| |
| for (i = 0; i < RTW8723X_IQK_ADDA_REG_NUM; i++) |
| backup->adda[i] = rtw_read32(rtwdev, |
| rtw8723x_common.iqk_adda_regs[i]); |
| |
| for (i = 0; i < RTW8723X_IQK_MAC8_REG_NUM; i++) |
| backup->mac8[i] = rtw_read8(rtwdev, |
| rtw8723x_common.iqk_mac8_regs[i]); |
| for (i = 0; i < RTW8723X_IQK_MAC32_REG_NUM; i++) |
| backup->mac32[i] = rtw_read32(rtwdev, |
| rtw8723x_common.iqk_mac32_regs[i]); |
| |
| for (i = 0; i < RTW8723X_IQK_BB_REG_NUM; i++) |
| backup->bb[i] = rtw_read32(rtwdev, |
| rtw8723x_common.iqk_bb_regs[i]); |
| |
| backup->igia = rtw_read32_mask(rtwdev, REG_OFDM0_XAAGC1, MASKBYTE0); |
| backup->igib = rtw_read32_mask(rtwdev, REG_OFDM0_XBAGC1, MASKBYTE0); |
| |
| backup->bb_sel_btg = rtw_read32(rtwdev, REG_BB_SEL_BTG); |
| } |
| |
| static |
| void __rtw8723x_iqk_restore_regs(struct rtw_dev *rtwdev, |
| const struct rtw8723x_iqk_backup_regs *backup) |
| { |
| int i; |
| |
| for (i = 0; i < RTW8723X_IQK_ADDA_REG_NUM; i++) |
| rtw_write32(rtwdev, rtw8723x_common.iqk_adda_regs[i], |
| backup->adda[i]); |
| |
| for (i = 0; i < RTW8723X_IQK_MAC8_REG_NUM; i++) |
| rtw_write8(rtwdev, rtw8723x_common.iqk_mac8_regs[i], |
| backup->mac8[i]); |
| for (i = 0; i < RTW8723X_IQK_MAC32_REG_NUM; i++) |
| rtw_write32(rtwdev, rtw8723x_common.iqk_mac32_regs[i], |
| backup->mac32[i]); |
| |
| for (i = 0; i < RTW8723X_IQK_BB_REG_NUM; i++) |
| rtw_write32(rtwdev, rtw8723x_common.iqk_bb_regs[i], |
| backup->bb[i]); |
| |
| rtw_write32_mask(rtwdev, REG_OFDM0_XAAGC1, MASKBYTE0, 0x50); |
| rtw_write32_mask(rtwdev, REG_OFDM0_XAAGC1, MASKBYTE0, backup->igia); |
| |
| rtw_write32_mask(rtwdev, REG_OFDM0_XBAGC1, MASKBYTE0, 0x50); |
| rtw_write32_mask(rtwdev, REG_OFDM0_XBAGC1, MASKBYTE0, backup->igib); |
| |
| rtw_write32(rtwdev, REG_TXIQK_TONE_A_11N, 0x01008c00); |
| rtw_write32(rtwdev, REG_RXIQK_TONE_A_11N, 0x01008c00); |
| } |
| |
| static |
| bool __rtw8723x_iqk_similarity_cmp(struct rtw_dev *rtwdev, |
| s32 result[][IQK_NR], |
| u8 c1, u8 c2) |
| { |
| u32 i, j, diff; |
| u32 bitmap = 0; |
| u8 candidate[PATH_NR] = {IQK_ROUND_INVALID, IQK_ROUND_INVALID}; |
| bool ret = true; |
| |
| s32 tmp1, tmp2; |
| |
| for (i = 0; i < IQK_NR; i++) { |
| tmp1 = iqkxy_to_s32(result[c1][i]); |
| tmp2 = iqkxy_to_s32(result[c2][i]); |
| |
| diff = abs(tmp1 - tmp2); |
| |
| if (diff <= MAX_TOLERANCE) |
| continue; |
| |
| if ((i == IQK_S1_RX_X || i == IQK_S0_RX_X) && !bitmap) { |
| if (result[c1][i] + result[c1][i + 1] == 0) |
| candidate[i / IQK_SX_NR] = c2; |
| else if (result[c2][i] + result[c2][i + 1] == 0) |
| candidate[i / IQK_SX_NR] = c1; |
| else |
| bitmap |= BIT(i); |
| } else { |
| bitmap |= BIT(i); |
| } |
| } |
| |
| if (bitmap != 0) |
| goto check_sim; |
| |
| for (i = 0; i < PATH_NR; i++) { |
| if (candidate[i] == IQK_ROUND_INVALID) |
| continue; |
| |
| for (j = i * IQK_SX_NR; j < i * IQK_SX_NR + 2; j++) |
| result[IQK_ROUND_HYBRID][j] = result[candidate[i]][j]; |
| ret = false; |
| } |
| |
| return ret; |
| |
| check_sim: |
| for (i = 0; i < IQK_NR; i++) { |
| j = i & ~1; /* 2 bits are a pair for IQ[X, Y] */ |
| if (bitmap & GENMASK(j + 1, j)) |
| continue; |
| |
| result[IQK_ROUND_HYBRID][i] = result[c1][i]; |
| } |
| |
| return false; |
| } |
| |
| static u8 __rtw8723x_pwrtrack_get_limit_ofdm(struct rtw_dev *rtwdev) |
| { |
| struct rtw_dm_info *dm_info = &rtwdev->dm_info; |
| u8 tx_rate = dm_info->tx_rate; |
| u8 limit_ofdm = 30; |
| |
| switch (tx_rate) { |
| case DESC_RATE1M...DESC_RATE5_5M: |
| case DESC_RATE11M: |
| break; |
| case DESC_RATE6M...DESC_RATE48M: |
| limit_ofdm = 36; |
| break; |
| case DESC_RATE54M: |
| limit_ofdm = 34; |
| break; |
| case DESC_RATEMCS0...DESC_RATEMCS2: |
| limit_ofdm = 38; |
| break; |
| case DESC_RATEMCS3...DESC_RATEMCS4: |
| limit_ofdm = 36; |
| break; |
| case DESC_RATEMCS5...DESC_RATEMCS7: |
| limit_ofdm = 34; |
| break; |
| default: |
| rtw_warn(rtwdev, "pwrtrack unhandled tx_rate 0x%x\n", tx_rate); |
| break; |
| } |
| |
| return limit_ofdm; |
| } |
| |
| static |
| void __rtw8723x_pwrtrack_set_xtal(struct rtw_dev *rtwdev, u8 therm_path, |
| u8 delta) |
| { |
| struct rtw_dm_info *dm_info = &rtwdev->dm_info; |
| const struct rtw_pwr_track_tbl *tbl = rtwdev->chip->pwr_track_tbl; |
| const s8 *pwrtrk_xtal; |
| s8 xtal_cap; |
| |
| if (dm_info->thermal_avg[therm_path] > |
| rtwdev->efuse.thermal_meter[therm_path]) |
| pwrtrk_xtal = tbl->pwrtrk_xtal_p; |
| else |
| pwrtrk_xtal = tbl->pwrtrk_xtal_n; |
| |
| xtal_cap = rtwdev->efuse.crystal_cap & 0x3F; |
| xtal_cap = clamp_t(s8, xtal_cap + pwrtrk_xtal[delta], 0, 0x3F); |
| rtw_write32_mask(rtwdev, REG_AFE_CTRL3, BIT_MASK_XTAL, |
| xtal_cap | (xtal_cap << 6)); |
| } |
| |
| static |
| void __rtw8723x_fill_txdesc_checksum(struct rtw_dev *rtwdev, |
| struct rtw_tx_pkt_info *pkt_info, |
| u8 *txdesc) |
| { |
| size_t words = 32 / 2; /* calculate the first 32 bytes (16 words) */ |
| __le16 chksum = 0; |
| __le16 *data = (__le16 *)(txdesc); |
| struct rtw_tx_desc *tx_desc = (struct rtw_tx_desc *)txdesc; |
| |
| le32p_replace_bits(&tx_desc->w7, 0, RTW_TX_DESC_W7_TXDESC_CHECKSUM); |
| |
| while (words--) |
| chksum ^= *data++; |
| |
| chksum = ~chksum; |
| |
| le32p_replace_bits(&tx_desc->w7, __le16_to_cpu(chksum), |
| RTW_TX_DESC_W7_TXDESC_CHECKSUM); |
| } |
| |
| static void __rtw8723x_coex_cfg_init(struct rtw_dev *rtwdev) |
| { |
| /* enable TBTT nterrupt */ |
| rtw_write8_set(rtwdev, REG_BCN_CTRL, BIT_EN_BCN_FUNCTION); |
| |
| /* BT report packet sample rate */ |
| /* 0x790[5:0]=0x5 */ |
| rtw_write8_mask(rtwdev, REG_BT_TDMA_TIME, BIT_MASK_SAMPLE_RATE, 0x5); |
| |
| /* enable BT counter statistics */ |
| rtw_write8(rtwdev, REG_BT_STAT_CTRL, 0x1); |
| |
| /* enable PTA (3-wire function form BT side) */ |
| rtw_write32_set(rtwdev, REG_GPIO_MUXCFG, BIT_BT_PTA_EN); |
| rtw_write32_set(rtwdev, REG_GPIO_MUXCFG, BIT_PO_BT_PTA_PINS); |
| |
| /* enable PTA (tx/rx signal form WiFi side) */ |
| rtw_write8_set(rtwdev, REG_QUEUE_CTRL, BIT_PTA_WL_TX_EN); |
| } |
| |
| const struct rtw8723x_common rtw8723x_common = { |
| .iqk_adda_regs = { |
| 0x85c, 0xe6c, 0xe70, 0xe74, 0xe78, 0xe7c, 0xe80, 0xe84, |
| 0xe88, 0xe8c, 0xed0, 0xed4, 0xed8, 0xedc, 0xee0, 0xeec |
| }, |
| .iqk_mac8_regs = {0x522, 0x550, 0x551}, |
| .iqk_mac32_regs = {0x40}, |
| .iqk_bb_regs = { |
| 0xc04, 0xc08, 0x874, 0xb68, 0xb6c, 0x870, 0x860, 0x864, 0xa04 |
| }, |
| |
| .ltecoex_addr = { |
| .ctrl = REG_LTECOEX_CTRL, |
| .wdata = REG_LTECOEX_WRITE_DATA, |
| .rdata = REG_LTECOEX_READ_DATA, |
| }, |
| .rf_sipi_addr = { |
| [RF_PATH_A] = { .hssi_1 = 0x820, .lssi_read = 0x8a0, |
| .hssi_2 = 0x824, .lssi_read_pi = 0x8b8}, |
| [RF_PATH_B] = { .hssi_1 = 0x828, .lssi_read = 0x8a4, |
| .hssi_2 = 0x82c, .lssi_read_pi = 0x8bc}, |
| }, |
| .dig = { |
| [0] = { .addr = 0xc50, .mask = 0x7f }, |
| [1] = { .addr = 0xc50, .mask = 0x7f }, |
| }, |
| .dig_cck = { |
| [0] = { .addr = 0xa0c, .mask = 0x3f00 }, |
| }, |
| .prioq_addrs = { |
| .prio[RTW_DMA_MAPPING_EXTRA] = { |
| .rsvd = REG_RQPN_NPQ + 2, .avail = REG_RQPN_NPQ + 3, |
| }, |
| .prio[RTW_DMA_MAPPING_LOW] = { |
| .rsvd = REG_RQPN + 1, .avail = REG_FIFOPAGE_CTRL_2 + 1, |
| }, |
| .prio[RTW_DMA_MAPPING_NORMAL] = { |
| .rsvd = REG_RQPN_NPQ, .avail = REG_RQPN_NPQ + 1, |
| }, |
| .prio[RTW_DMA_MAPPING_HIGH] = { |
| .rsvd = REG_RQPN, .avail = REG_FIFOPAGE_CTRL_2, |
| }, |
| .wsize = false, |
| }, |
| |
| .lck = __rtw8723x_lck, |
| .read_efuse = __rtw8723x_read_efuse, |
| .mac_init = __rtw8723x_mac_init, |
| .cfg_ldo25 = __rtw8723x_cfg_ldo25, |
| .set_tx_power_index = __rtw8723x_set_tx_power_index, |
| .efuse_grant = __rtw8723x_efuse_grant, |
| .false_alarm_statistics = __rtw8723x_false_alarm_statistics, |
| .iqk_backup_regs = __rtw8723x_iqk_backup_regs, |
| .iqk_restore_regs = __rtw8723x_iqk_restore_regs, |
| .iqk_similarity_cmp = __rtw8723x_iqk_similarity_cmp, |
| .pwrtrack_get_limit_ofdm = __rtw8723x_pwrtrack_get_limit_ofdm, |
| .pwrtrack_set_xtal = __rtw8723x_pwrtrack_set_xtal, |
| .coex_cfg_init = __rtw8723x_coex_cfg_init, |
| .fill_txdesc_checksum = __rtw8723x_fill_txdesc_checksum, |
| .debug_txpwr_limit = __rtw8723x_debug_txpwr_limit, |
| }; |
| EXPORT_SYMBOL(rtw8723x_common); |
| |
| MODULE_AUTHOR("Realtek Corporation"); |
| MODULE_AUTHOR("Fiona Klute <fiona.klute@gmx.de>"); |
| MODULE_DESCRIPTION("Common functions for Realtek 802.11n wireless 8723x drivers"); |
| MODULE_LICENSE("Dual BSD/GPL"); |