Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  rtw8822c.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/* Copyright(c) 2018-2019  Realtek Corporation
 */


#include <linux/module.h>
#include "main.h"
#include "coex.h"
#include "fw.h"
#include "tx.h"
#include "rx.h"
#include "phy.h"
#include "rtw8822c.h"
#include "rtw8822c_table.h"
#include "mac.h"
#include "reg.h"
#include "debug.h"
#include "util.h"
#include "bf.h"
#include "efuse.h"

#define IQK_DONE_8822C 0xaa

static void rtw8822c_config_trx_mode(struct rtw_dev *rtwdev, u8 tx_path,
         u8 rx_path, bool is_tx2_path);

static void rtw8822ce_efuse_parsing(struct rtw_efuse *efuse,
        struct rtw8822c_efuse *map)
{
 ether_addr_copy(efuse->addr, map->e.mac_addr);
}

static void rtw8822cu_efuse_parsing(struct rtw_efuse *efuse,
        struct rtw8822c_efuse *map)
{
 ether_addr_copy(efuse->addr, map->u.mac_addr);
}

static void rtw8822cs_efuse_parsing(struct rtw_efuse *efuse,
        struct rtw8822c_efuse *map)
{
 ether_addr_copy(efuse->addr, map->s.mac_addr);
}

static int rtw8822c_read_efuse(struct rtw_dev *rtwdev, u8 *log_map)
{
 struct rtw_efuse *efuse = &rtwdev->efuse;
 struct rtw8822c_efuse *map;
 int i;

 map = (struct rtw8822c_efuse *)log_map;

 efuse->usb_mode_switch = u8_get_bits(map->usb_mode, BIT(7));
 efuse->rfe_option = map->rfe_option;
 efuse->rf_board_option = map->rf_board_option;
 efuse->crystal_cap = map->xtal_k & XCAP_MASK;
 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[RF_PATH_A] = map->path_a_thermal;
 efuse->thermal_meter[RF_PATH_B] = map->path_b_thermal;
 efuse->thermal_meter_k =
   (map->path_a_thermal + map->path_b_thermal) >> 1;
 efuse->power_track_type = (map->tx_pwr_calibrate_rate >> 4) & 0xf;

 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:
  rtw8822ce_efuse_parsing(efuse, map);
  break;
 case RTW_HCI_TYPE_USB:
  rtw8822cu_efuse_parsing(efuse, map);
  break;
 case RTW_HCI_TYPE_SDIO:
  rtw8822cs_efuse_parsing(efuse, map);
  break;
 default:
  /* unsupported now */
  return -ENOTSUPP;
 }

 return 0;
}

static void rtw8822c_header_file_init(struct rtw_dev *rtwdev, bool pre)
{
 rtw_write32_set(rtwdev, REG_3WIRE, BIT_3WIRE_TX_EN | BIT_3WIRE_RX_EN);
 rtw_write32_set(rtwdev, REG_3WIRE, BIT_3WIRE_PI_ON);
 rtw_write32_set(rtwdev, REG_3WIRE2, BIT_3WIRE_TX_EN | BIT_3WIRE_RX_EN);
 rtw_write32_set(rtwdev, REG_3WIRE2, BIT_3WIRE_PI_ON);

 if (pre)
  rtw_write32_clr(rtwdev, REG_ENCCK, BIT_CCK_OFDM_BLK_EN);
 else
  rtw_write32_set(rtwdev, REG_ENCCK, BIT_CCK_OFDM_BLK_EN);
}

static void rtw8822c_bb_reset(struct rtw_dev *rtwdev)
{
 rtw_write16_set(rtwdev, REG_SYS_FUNC_EN, BIT_FEN_BB_RSTB);
 rtw_write16_clr(rtwdev, REG_SYS_FUNC_EN, BIT_FEN_BB_RSTB);
 rtw_write16_set(rtwdev, REG_SYS_FUNC_EN, BIT_FEN_BB_RSTB);
}

static void rtw8822c_dac_backup_reg(struct rtw_dev *rtwdev,
        struct rtw_backup_info *backup,
        struct rtw_backup_info *backup_rf)
{
 u32 path, i;
 u32 val;
 u32 reg;
 u32 rf_addr[DACK_RF_8822C] = {0x8f};
 u32 addrs[DACK_REG_8822C] = {0x180c, 0x1810, 0x410c, 0x4110,
         0x1c3c, 0x1c24, 0x1d70, 0x9b4,
         0x1a00, 0x1a14, 0x1d58, 0x1c38,
         0x1e24, 0x1e28, 0x1860, 0x4160};

 for (i = 0; i < DACK_REG_8822C; i++) {
  backup[i].len = 4;
  backup[i].reg = addrs[i];
  backup[i].val = rtw_read32(rtwdev, addrs[i]);
 }

 for (path = 0; path < DACK_PATH_8822C; path++) {
  for (i = 0; i < DACK_RF_8822C; i++) {
   reg = rf_addr[i];
   val = rtw_read_rf(rtwdev, path, reg, RFREG_MASK);
   backup_rf[path * i + i].reg = reg;
   backup_rf[path * i + i].val = val;
  }
 }
}

static void rtw8822c_dac_restore_reg(struct rtw_dev *rtwdev,
         struct rtw_backup_info *backup,
         struct rtw_backup_info *backup_rf)
{
 u32 path, i;
 u32 val;
 u32 reg;

 rtw_restore_reg(rtwdev, backup, DACK_REG_8822C);

 for (path = 0; path < DACK_PATH_8822C; path++) {
  for (i = 0; i < DACK_RF_8822C; i++) {
   val = backup_rf[path * i + i].val;
   reg = backup_rf[path * i + i].reg;
   rtw_write_rf(rtwdev, path, reg, RFREG_MASK, val);
  }
 }
}

static void rtw8822c_rf_minmax_cmp(struct rtw_dev *rtwdev, u32 value,
       u32 *min, u32 *max)
{
 if (value >= 0x200) {
  if (*min >= 0x200) {
   if (*min > value)
    *min = value;
  } else {
   *min = value;
  }
  if (*max >= 0x200) {
   if (*max < value)
    *max = value;
  }
 } else {
  if (*min < 0x200) {
   if (*min > value)
    *min = value;
  }

  if (*max  >= 0x200) {
   *max = value;
  } else {
   if (*max < value)
    *max = value;
  }
 }
}

static void __rtw8822c_dac_iq_sort(struct rtw_dev *rtwdev, u32 *v1, u32 *v2)
{
 if (*v1 >= 0x200 && *v2 >= 0x200) {
  if (*v1 > *v2)
   swap(*v1, *v2);
 } else if (*v1 < 0x200 && *v2 < 0x200) {
  if (*v1 > *v2)
   swap(*v1, *v2);
 } else if (*v1 < 0x200 && *v2 >= 0x200) {
  swap(*v1, *v2);
 }
}

static void rtw8822c_dac_iq_sort(struct rtw_dev *rtwdev, u32 *iv, u32 *qv)
{
 u32 i, j;

 for (i = 0; i < DACK_SN_8822C - 1; i++) {
  for (j = 0; j < (DACK_SN_8822C - 1 - i) ; j++) {
   __rtw8822c_dac_iq_sort(rtwdev, &iv[j], &iv[j + 1]);
   __rtw8822c_dac_iq_sort(rtwdev, &qv[j], &qv[j + 1]);
  }
 }
}

static void rtw8822c_dac_iq_offset(struct rtw_dev *rtwdev, u32 *vec, u32 *val)
{
 u32 p, m, t, i;

 m = 0;
 p = 0;
 for (i = 10; i < DACK_SN_8822C - 10; i++) {
  if (vec[i] > 0x200)
   m = (0x400 - vec[i]) + m;
  else
   p = vec[i] + p;
 }

 if (p > m) {
  t = p - m;
  t = t / (DACK_SN_8822C - 20);
 } else {
  t = m - p;
  t = t / (DACK_SN_8822C - 20);
  if (t != 0x0)
   t = 0x400 - t;
 }

 *val = t;
}

static u32 rtw8822c_get_path_write_addr(u8 path)
{
 u32 base_addr;

 switch (path) {
 case RF_PATH_A:
  base_addr = 0x1800;
  break;
 case RF_PATH_B:
  base_addr = 0x4100;
  break;
 default:
  WARN_ON(1);
  return -1;
 }

 return base_addr;
}

static u32 rtw8822c_get_path_read_addr(u8 path)
{
 u32 base_addr;

 switch (path) {
 case RF_PATH_A:
  base_addr = 0x2800;
  break;
 case RF_PATH_B:
  base_addr = 0x4500;
  break;
 default:
  WARN_ON(1);
  return -1;
 }

 return base_addr;
}

static bool rtw8822c_dac_iq_check(struct rtw_dev *rtwdev, u32 value)
{
 bool ret = true;

 if ((value >= 0x200 && (0x400 - value) > 0x64) ||
     (value < 0x200 && value > 0x64)) {
  ret = false;
  rtw_dbg(rtwdev, RTW_DBG_RFK, "[DACK] Error overflow\n");
 }

 return ret;
}

static void rtw8822c_dac_cal_iq_sample(struct rtw_dev *rtwdev, u32 *iv, u32 *qv)
{
 u32 temp;
 int i = 0, cnt = 0;

 while (i < DACK_SN_8822C && cnt < 10000) {
  cnt++;
  temp = rtw_read32_mask(rtwdev, 0x2dbc, 0x3fffff);
  iv[i] = (temp & 0x3ff000) >> 12;
  qv[i] = temp & 0x3ff;

  if (rtw8822c_dac_iq_check(rtwdev, iv[i]) &&
      rtw8822c_dac_iq_check(rtwdev, qv[i]))
   i++;
 }
}

static void rtw8822c_dac_cal_iq_search(struct rtw_dev *rtwdev,
           u32 *iv, u32 *qv,
           u32 *i_value, u32 *q_value)
{
 u32 i_max = 0, q_max = 0, i_min = 0, q_min = 0;
 u32 i_delta, q_delta;
 u32 temp;
 int i, cnt = 0;

 do {
  i_min = iv[0];
  i_max = iv[0];
  q_min = qv[0];
  q_max = qv[0];
  for (i = 0; i < DACK_SN_8822C; i++) {
   rtw8822c_rf_minmax_cmp(rtwdev, iv[i], &i_min, &i_max);
   rtw8822c_rf_minmax_cmp(rtwdev, qv[i], &q_min, &q_max);
  }

  if (i_max < 0x200 && i_min < 0x200)
   i_delta = i_max - i_min;
  else if (i_max >= 0x200 && i_min >= 0x200)
   i_delta = i_max - i_min;
  else
   i_delta = i_max + (0x400 - i_min);

  if (q_max < 0x200 && q_min < 0x200)
   q_delta = q_max - q_min;
  else if (q_max >= 0x200 && q_min >= 0x200)
   q_delta = q_max - q_min;
  else
   q_delta = q_max + (0x400 - q_min);

  rtw_dbg(rtwdev, RTW_DBG_RFK,
   "[DACK] i: min=0x%08x, max=0x%08x, delta=0x%08x\n",
   i_min, i_max, i_delta);
  rtw_dbg(rtwdev, RTW_DBG_RFK,
   "[DACK] q: min=0x%08x, max=0x%08x, delta=0x%08x\n",
   q_min, q_max, q_delta);

  rtw8822c_dac_iq_sort(rtwdev, iv, qv);

  if (i_delta > 5 || q_delta > 5) {
   temp = rtw_read32_mask(rtwdev, 0x2dbc, 0x3fffff);
   iv[0] = (temp & 0x3ff000) >> 12;
   qv[0] = temp & 0x3ff;
   temp = rtw_read32_mask(rtwdev, 0x2dbc, 0x3fffff);
   iv[DACK_SN_8822C - 1] = (temp & 0x3ff000) >> 12;
   qv[DACK_SN_8822C - 1] = temp & 0x3ff;
  } else {
   break;
  }
 } while (cnt++ < 100);

 rtw8822c_dac_iq_offset(rtwdev, iv, i_value);
 rtw8822c_dac_iq_offset(rtwdev, qv, q_value);
}

static void rtw8822c_dac_cal_rf_mode(struct rtw_dev *rtwdev,
         u32 *i_value, u32 *q_value)
{
 u32 iv[DACK_SN_8822C], qv[DACK_SN_8822C];
 u32 rf_a, rf_b;

 rf_a = rtw_read_rf(rtwdev, RF_PATH_A, 0x0, RFREG_MASK);
 rf_b = rtw_read_rf(rtwdev, RF_PATH_B, 0x0, RFREG_MASK);

 rtw_dbg(rtwdev, RTW_DBG_RFK, "[DACK] RF path-A=0x%05x\n", rf_a);
 rtw_dbg(rtwdev, RTW_DBG_RFK, "[DACK] RF path-B=0x%05x\n", rf_b);

 rtw8822c_dac_cal_iq_sample(rtwdev, iv, qv);
 rtw8822c_dac_cal_iq_search(rtwdev, iv, qv, i_value, q_value);
}

static void rtw8822c_dac_bb_setting(struct rtw_dev *rtwdev)
{
 rtw_write32_mask(rtwdev, 0x1d58, 0xff8, 0x1ff);
 rtw_write32_mask(rtwdev, 0x1a00, 0x3, 0x2);
 rtw_write32_mask(rtwdev, 0x1a14, 0x300, 0x3);
 rtw_write32(rtwdev, 0x1d70, 0x7e7e7e7e);
 rtw_write32_mask(rtwdev, 0x180c, 0x3, 0x0);
 rtw_write32_mask(rtwdev, 0x410c, 0x3, 0x0);
 rtw_write32(rtwdev, 0x1b00, 0x00000008);
 rtw_write8(rtwdev, 0x1bcc, 0x3f);
 rtw_write32(rtwdev, 0x1b00, 0x0000000a);
 rtw_write8(rtwdev, 0x1bcc, 0x3f);
 rtw_write32_mask(rtwdev, 0x1e24, BIT(31), 0x0);
 rtw_write32_mask(rtwdev, 0x1e28, 0xf, 0x3);
}

static void rtw8822c_dac_cal_adc(struct rtw_dev *rtwdev,
     u8 path, u32 *adc_ic, u32 *adc_qc)
{
 struct rtw_dm_info *dm_info = &rtwdev->dm_info;
 u32 ic = 0, qc = 0, temp = 0;
 u32 base_addr;
 u32 path_sel;
 int i;

 rtw_dbg(rtwdev, RTW_DBG_RFK, "[DACK] ADCK path(%d)\n", path);

 base_addr = rtw8822c_get_path_write_addr(path);
 switch (path) {
 case RF_PATH_A:
  path_sel = 0xa0000;
  break;
 case RF_PATH_B:
  path_sel = 0x80000;
  break;
 default:
  WARN_ON(1);
  return;
 }

 /* ADCK step1 */
 rtw_write32_mask(rtwdev, base_addr + 0x30, BIT(30), 0x0);
 if (path == RF_PATH_B)
  rtw_write32(rtwdev, base_addr + 0x30, 0x30db8041);
 rtw_write32(rtwdev, base_addr + 0x60, 0xf0040ff0);
 rtw_write32(rtwdev, base_addr + 0x0c, 0xdff00220);
 rtw_write32(rtwdev, base_addr + 0x10, 0x02dd08c4);
 rtw_write32(rtwdev, base_addr + 0x0c, 0x10000260);
 rtw_write_rf(rtwdev, RF_PATH_A, 0x0, RFREG_MASK, 0x10000);
 rtw_write_rf(rtwdev, RF_PATH_B, 0x0, RFREG_MASK, 0x10000);
 for (i = 0; i < 10; i++) {
  rtw_dbg(rtwdev, RTW_DBG_RFK, "[DACK] ADCK count=%d\n", i);
  rtw_write32(rtwdev, 0x1c3c, path_sel + 0x8003);
  rtw_write32(rtwdev, 0x1c24, 0x00010002);
  rtw8822c_dac_cal_rf_mode(rtwdev, &ic, &qc);
  rtw_dbg(rtwdev, RTW_DBG_RFK,
   "[DACK] before: i=0x%x, q=0x%x\n", ic, qc);

  /* compensation value */
  if (ic != 0x0) {
   ic = 0x400 - ic;
   *adc_ic = ic;
  }
  if (qc != 0x0) {
   qc = 0x400 - qc;
   *adc_qc = qc;
  }
  temp = (ic & 0x3ff) | ((qc & 0x3ff) << 10);
  rtw_write32(rtwdev, base_addr + 0x68, temp);
  dm_info->dack_adck[path] = temp;
  rtw_dbg(rtwdev, RTW_DBG_RFK, "[DACK] ADCK 0x%08x=0x08%x\n",
   base_addr + 0x68, temp);
  /* check ADC DC offset */
  rtw_write32(rtwdev, 0x1c3c, path_sel + 0x8103);
  rtw8822c_dac_cal_rf_mode(rtwdev, &ic, &qc);
  rtw_dbg(rtwdev, RTW_DBG_RFK,
   "[DACK] after: i=0x%08x, q=0x%08x\n", ic, qc);
  if (ic >= 0x200)
   ic = 0x400 - ic;
  if (qc >= 0x200)
   qc = 0x400 - qc;
  if (ic < 5 && qc < 5)
   break;
 }

 /* ADCK step2 */
 rtw_write32(rtwdev, 0x1c3c, 0x00000003);
 rtw_write32(rtwdev, base_addr + 0x0c, 0x10000260);
 rtw_write32(rtwdev, base_addr + 0x10, 0x02d508c4);

 /* release pull low switch on IQ path */
 rtw_write_rf(rtwdev, path, 0x8f, BIT(13), 0x1);
}

static void rtw8822c_dac_cal_step1(struct rtw_dev *rtwdev, u8 path)
{
 struct rtw_dm_info *dm_info = &rtwdev->dm_info;
 u32 base_addr;
 u32 read_addr;

 base_addr = rtw8822c_get_path_write_addr(path);
 read_addr = rtw8822c_get_path_read_addr(path);

 rtw_write32(rtwdev, base_addr + 0x68, dm_info->dack_adck[path]);
 rtw_write32(rtwdev, base_addr + 0x0c, 0xdff00220);
 if (path == RF_PATH_A) {
  rtw_write32(rtwdev, base_addr + 0x60, 0xf0040ff0);
  rtw_write32(rtwdev, 0x1c38, 0xffffffff);
 }
 rtw_write32(rtwdev, base_addr + 0x10, 0x02d508c5);
 rtw_write32(rtwdev, 0x9b4, 0xdb66db00);
 rtw_write32(rtwdev, base_addr + 0xb0, 0x0a11fb88);
 rtw_write32(rtwdev, base_addr + 0xbc, 0x0008ff81);
 rtw_write32(rtwdev, base_addr + 0xc0, 0x0003d208);
 rtw_write32(rtwdev, base_addr + 0xcc, 0x0a11fb88);
 rtw_write32(rtwdev, base_addr + 0xd8, 0x0008ff81);
 rtw_write32(rtwdev, base_addr + 0xdc, 0x0003d208);
 rtw_write32(rtwdev, base_addr + 0xb8, 0x60000000);
 mdelay(2);
 rtw_write32(rtwdev, base_addr + 0xbc, 0x000aff8d);
 mdelay(2);
 rtw_write32(rtwdev, base_addr + 0xb0, 0x0a11fb89);
 rtw_write32(rtwdev, base_addr + 0xcc, 0x0a11fb89);
 mdelay(1);
 rtw_write32(rtwdev, base_addr + 0xb8, 0x62000000);
 rtw_write32(rtwdev, base_addr + 0xd4, 0x62000000);
 mdelay(20);
 if (!check_hw_ready(rtwdev, read_addr + 0x08, 0x7fff80, 0xffff) ||
     !check_hw_ready(rtwdev, read_addr + 0x34, 0x7fff80, 0xffff))
  rtw_err(rtwdev, "failed to wait for dack ready\n");
 rtw_write32(rtwdev, base_addr + 0xb8, 0x02000000);
 mdelay(1);
 rtw_write32(rtwdev, base_addr + 0xbc, 0x0008ff87);
 rtw_write32(rtwdev, 0x9b4, 0xdb6db600);
 rtw_write32(rtwdev, base_addr + 0x10, 0x02d508c5);
 rtw_write32(rtwdev, base_addr + 0xbc, 0x0008ff87);
 rtw_write32(rtwdev, base_addr + 0x60, 0xf0000000);
}

static void rtw8822c_dac_cal_step2(struct rtw_dev *rtwdev,
       u8 path, u32 *ic_out, u32 *qc_out)
{
 u32 base_addr;
 u32 ic, qc, ic_in, qc_in;

 base_addr = rtw8822c_get_path_write_addr(path);
 rtw_write32_mask(rtwdev, base_addr + 0xbc, 0xf0000000, 0x0);
 rtw_write32_mask(rtwdev, base_addr + 0xc0, 0xf, 0x8);
 rtw_write32_mask(rtwdev, base_addr + 0xd8, 0xf0000000, 0x0);
 rtw_write32_mask(rtwdev, base_addr + 0xdc, 0xf, 0x8);

 rtw_write32(rtwdev, 0x1b00, 0x00000008);
 rtw_write8(rtwdev, 0x1bcc, 0x03f);
 rtw_write32(rtwdev, base_addr + 0x0c, 0xdff00220);
 rtw_write32(rtwdev, base_addr + 0x10, 0x02d508c5);
 rtw_write32(rtwdev, 0x1c3c, 0x00088103);

 rtw8822c_dac_cal_rf_mode(rtwdev, &ic_in, &qc_in);
 ic = ic_in;
 qc = qc_in;

 /* compensation value */
 if (ic != 0x0)
  ic = 0x400 - ic;
 if (qc != 0x0)
  qc = 0x400 - qc;
 if (ic < 0x300) {
  ic = ic * 2 * 6 / 5;
  ic = ic + 0x80;
 } else {
  ic = (0x400 - ic) * 2 * 6 / 5;
  ic = 0x7f - ic;
 }
 if (qc < 0x300) {
  qc = qc * 2 * 6 / 5;
  qc = qc + 0x80;
 } else {
  qc = (0x400 - qc) * 2 * 6 / 5;
  qc = 0x7f - qc;
 }

 *ic_out = ic;
 *qc_out = qc;

 rtw_dbg(rtwdev, RTW_DBG_RFK, "[DACK] before i=0x%x, q=0x%x\n", ic_in, qc_in);
 rtw_dbg(rtwdev, RTW_DBG_RFK, "[DACK] after i=0x%x, q=0x%x\n", ic, qc);
}

static void rtw8822c_dac_cal_step3(struct rtw_dev *rtwdev, u8 path,
       u32 adc_ic, u32 adc_qc,
       u32 *ic_in, u32 *qc_in,
       u32 *i_out, u32 *q_out)
{
 u32 base_addr;
 u32 read_addr;
 u32 ic, qc;
 u32 temp;

 base_addr = rtw8822c_get_path_write_addr(path);
 read_addr = rtw8822c_get_path_read_addr(path);
 ic = *ic_in;
 qc = *qc_in;

 rtw_write32(rtwdev, base_addr + 0x0c, 0xdff00220);
 rtw_write32(rtwdev, base_addr + 0x10, 0x02d508c5);
 rtw_write32(rtwdev, 0x9b4, 0xdb66db00);
 rtw_write32(rtwdev, base_addr + 0xb0, 0x0a11fb88);
 rtw_write32(rtwdev, base_addr + 0xbc, 0xc008ff81);
 rtw_write32(rtwdev, base_addr + 0xc0, 0x0003d208);
 rtw_write32_mask(rtwdev, base_addr + 0xbc, 0xf0000000, ic & 0xf);
 rtw_write32_mask(rtwdev, base_addr + 0xc0, 0xf, (ic & 0xf0) >> 4);
 rtw_write32(rtwdev, base_addr + 0xcc, 0x0a11fb88);
 rtw_write32(rtwdev, base_addr + 0xd8, 0xe008ff81);
 rtw_write32(rtwdev, base_addr + 0xdc, 0x0003d208);
 rtw_write32_mask(rtwdev, base_addr + 0xd8, 0xf0000000, qc & 0xf);
 rtw_write32_mask(rtwdev, base_addr + 0xdc, 0xf, (qc & 0xf0) >> 4);
 rtw_write32(rtwdev, base_addr + 0xb8, 0x60000000);
 mdelay(2);
 rtw_write32_mask(rtwdev, base_addr + 0xbc, 0xe, 0x6);
 mdelay(2);
 rtw_write32(rtwdev, base_addr + 0xb0, 0x0a11fb89);
 rtw_write32(rtwdev, base_addr + 0xcc, 0x0a11fb89);
 mdelay(1);
 rtw_write32(rtwdev, base_addr + 0xb8, 0x62000000);
 rtw_write32(rtwdev, base_addr + 0xd4, 0x62000000);
 mdelay(20);
 if (!check_hw_ready(rtwdev, read_addr + 0x24, 0x07f80000, ic) ||
     !check_hw_ready(rtwdev, read_addr + 0x50, 0x07f80000, qc))
  rtw_err(rtwdev, "failed to write IQ vector to hardware\n");
 rtw_write32(rtwdev, base_addr + 0xb8, 0x02000000);
 mdelay(1);
 rtw_write32_mask(rtwdev, base_addr + 0xbc, 0xe, 0x3);
 rtw_write32(rtwdev, 0x9b4, 0xdb6db600);

 /* check DAC DC offset */
 temp = ((adc_ic + 0x10) & 0x3ff) | (((adc_qc + 0x10) & 0x3ff) << 10);
 rtw_write32(rtwdev, base_addr + 0x68, temp);
 rtw_write32(rtwdev, base_addr + 0x10, 0x02d508c5);
 rtw_write32(rtwdev, base_addr + 0x60, 0xf0000000);
 rtw8822c_dac_cal_rf_mode(rtwdev, &ic, &qc);
 if (ic >= 0x10)
  ic = ic - 0x10;
 else
  ic = 0x400 - (0x10 - ic);

 if (qc >= 0x10)
  qc = qc - 0x10;
 else
  qc = 0x400 - (0x10 - qc);

 *i_out = ic;
 *q_out = qc;

 if (ic >= 0x200)
  ic = 0x400 - ic;
 if (qc >= 0x200)
  qc = 0x400 - qc;

 *ic_in = ic;
 *qc_in = qc;

 rtw_dbg(rtwdev, RTW_DBG_RFK,
  "[DACK] after DACK i=0x%x, q=0x%x\n", *i_out, *q_out);
}

static void rtw8822c_dac_cal_step4(struct rtw_dev *rtwdev, u8 path)
{
 u32 base_addr = rtw8822c_get_path_write_addr(path);

 rtw_write32(rtwdev, base_addr + 0x68, 0x0);
 rtw_write32(rtwdev, base_addr + 0x10, 0x02d508c4);
 rtw_write32_mask(rtwdev, base_addr + 0xbc, 0x1, 0x0);
 rtw_write32_mask(rtwdev, base_addr + 0x30, BIT(30), 0x1);
}

static void rtw8822c_dac_cal_backup_vec(struct rtw_dev *rtwdev,
     u8 path, u8 vec, u32 w_addr, u32 r_addr)
{
 struct rtw_dm_info *dm_info = &rtwdev->dm_info;
 u16 val;
 u32 i;

 if (WARN_ON(vec >= 2))
  return;

 for (i = 0; i < DACK_MSBK_BACKUP_NUM; i++) {
  rtw_write32_mask(rtwdev, w_addr, 0xf0000000, i);
  val = (u16)rtw_read32_mask(rtwdev, r_addr, 0x7fc0000);
  dm_info->dack_msbk[path][vec][i] = val;
 }
}

static void rtw8822c_dac_cal_backup_path(struct rtw_dev *rtwdev, u8 path)
{
 u32 w_off = 0x1c;
 u32 r_off = 0x2c;
 u32 w_addr, r_addr;

 if (WARN_ON(path >= 2))
  return;

 /* backup I vector */
 w_addr = rtw8822c_get_path_write_addr(path) + 0xb0;
 r_addr = rtw8822c_get_path_read_addr(path) + 0x10;
 rtw8822c_dac_cal_backup_vec(rtwdev, path, 0, w_addr, r_addr);

 /* backup Q vector */
 w_addr = rtw8822c_get_path_write_addr(path) + 0xb0 + w_off;
 r_addr = rtw8822c_get_path_read_addr(path) + 0x10 + r_off;
 rtw8822c_dac_cal_backup_vec(rtwdev, path, 1, w_addr, r_addr);
}

static void rtw8822c_dac_cal_backup_dck(struct rtw_dev *rtwdev)
{
 struct rtw_dm_info *dm_info = &rtwdev->dm_info;
 u8 val;

 val = (u8)rtw_read32_mask(rtwdev, REG_DCKA_I_0, 0xf0000000);
 dm_info->dack_dck[RF_PATH_A][0][0] = val;
 val = (u8)rtw_read32_mask(rtwdev, REG_DCKA_I_1, 0xf);
 dm_info->dack_dck[RF_PATH_A][0][1] = val;
 val = (u8)rtw_read32_mask(rtwdev, REG_DCKA_Q_0, 0xf0000000);
 dm_info->dack_dck[RF_PATH_A][1][0] = val;
 val = (u8)rtw_read32_mask(rtwdev, REG_DCKA_Q_1, 0xf);
 dm_info->dack_dck[RF_PATH_A][1][1] = val;

 val = (u8)rtw_read32_mask(rtwdev, REG_DCKB_I_0, 0xf0000000);
 dm_info->dack_dck[RF_PATH_B][0][0] = val;
 val = (u8)rtw_read32_mask(rtwdev, REG_DCKB_I_1, 0xf);
 dm_info->dack_dck[RF_PATH_B][1][0] = val;
 val = (u8)rtw_read32_mask(rtwdev, REG_DCKB_Q_0, 0xf0000000);
 dm_info->dack_dck[RF_PATH_B][0][1] = val;
 val = (u8)rtw_read32_mask(rtwdev, REG_DCKB_Q_1, 0xf);
 dm_info->dack_dck[RF_PATH_B][1][1] = val;
}

static void rtw8822c_dac_cal_backup(struct rtw_dev *rtwdev)
{
 u32 temp[3];

 temp[0] = rtw_read32(rtwdev, 0x1860);
 temp[1] = rtw_read32(rtwdev, 0x4160);
 temp[2] = rtw_read32(rtwdev, 0x9b4);

 /* set clock */
 rtw_write32(rtwdev, 0x9b4, 0xdb66db00);

 /* backup path-A I/Q */
 rtw_write32_clr(rtwdev, 0x1830, BIT(30));
 rtw_write32_mask(rtwdev, 0x1860, 0xfc000000, 0x3c);
 rtw8822c_dac_cal_backup_path(rtwdev, RF_PATH_A);

 /* backup path-B I/Q */
 rtw_write32_clr(rtwdev, 0x4130, BIT(30));
 rtw_write32_mask(rtwdev, 0x4160, 0xfc000000, 0x3c);
 rtw8822c_dac_cal_backup_path(rtwdev, RF_PATH_B);

 rtw8822c_dac_cal_backup_dck(rtwdev);
 rtw_write32_set(rtwdev, 0x1830, BIT(30));
 rtw_write32_set(rtwdev, 0x4130, BIT(30));

 rtw_write32(rtwdev, 0x1860, temp[0]);
 rtw_write32(rtwdev, 0x4160, temp[1]);
 rtw_write32(rtwdev, 0x9b4, temp[2]);
}

static void rtw8822c_dac_cal_restore_dck(struct rtw_dev *rtwdev)
{
 struct rtw_dm_info *dm_info = &rtwdev->dm_info;
 u8 val;

 rtw_write32_set(rtwdev, REG_DCKA_I_0, BIT(19));
 val = dm_info->dack_dck[RF_PATH_A][0][0];
 rtw_write32_mask(rtwdev, REG_DCKA_I_0, 0xf0000000, val);
 val = dm_info->dack_dck[RF_PATH_A][0][1];
 rtw_write32_mask(rtwdev, REG_DCKA_I_1, 0xf, val);

 rtw_write32_set(rtwdev, REG_DCKA_Q_0, BIT(19));
 val = dm_info->dack_dck[RF_PATH_A][1][0];
 rtw_write32_mask(rtwdev, REG_DCKA_Q_0, 0xf0000000, val);
 val = dm_info->dack_dck[RF_PATH_A][1][1];
 rtw_write32_mask(rtwdev, REG_DCKA_Q_1, 0xf, val);

 rtw_write32_set(rtwdev, REG_DCKB_I_0, BIT(19));
 val = dm_info->dack_dck[RF_PATH_B][0][0];
 rtw_write32_mask(rtwdev, REG_DCKB_I_0, 0xf0000000, val);
 val = dm_info->dack_dck[RF_PATH_B][0][1];
 rtw_write32_mask(rtwdev, REG_DCKB_I_1, 0xf, val);

 rtw_write32_set(rtwdev, REG_DCKB_Q_0, BIT(19));
 val = dm_info->dack_dck[RF_PATH_B][1][0];
 rtw_write32_mask(rtwdev, REG_DCKB_Q_0, 0xf0000000, val);
 val = dm_info->dack_dck[RF_PATH_B][1][1];
 rtw_write32_mask(rtwdev, REG_DCKB_Q_1, 0xf, val);
}

static void rtw8822c_dac_cal_restore_prepare(struct rtw_dev *rtwdev)
{
 rtw_write32(rtwdev, 0x9b4, 0xdb66db00);

 rtw_write32_mask(rtwdev, 0x18b0, BIT(27), 0x0);
 rtw_write32_mask(rtwdev, 0x18cc, BIT(27), 0x0);
 rtw_write32_mask(rtwdev, 0x41b0, BIT(27), 0x0);
 rtw_write32_mask(rtwdev, 0x41cc, BIT(27), 0x0);

 rtw_write32_mask(rtwdev, 0x1830, BIT(30), 0x0);
 rtw_write32_mask(rtwdev, 0x1860, 0xfc000000, 0x3c);
 rtw_write32_mask(rtwdev, 0x18b4, BIT(0), 0x1);
 rtw_write32_mask(rtwdev, 0x18d0, BIT(0), 0x1);

 rtw_write32_mask(rtwdev, 0x4130, BIT(30), 0x0);
 rtw_write32_mask(rtwdev, 0x4160, 0xfc000000, 0x3c);
 rtw_write32_mask(rtwdev, 0x41b4, BIT(0), 0x1);
 rtw_write32_mask(rtwdev, 0x41d0, BIT(0), 0x1);

 rtw_write32_mask(rtwdev, 0x18b0, 0xf00, 0x0);
 rtw_write32_mask(rtwdev, 0x18c0, BIT(14), 0x0);
 rtw_write32_mask(rtwdev, 0x18cc, 0xf00, 0x0);
 rtw_write32_mask(rtwdev, 0x18dc, BIT(14), 0x0);

 rtw_write32_mask(rtwdev, 0x18b0, BIT(0), 0x0);
 rtw_write32_mask(rtwdev, 0x18cc, BIT(0), 0x0);
 rtw_write32_mask(rtwdev, 0x18b0, BIT(0), 0x1);
 rtw_write32_mask(rtwdev, 0x18cc, BIT(0), 0x1);

 rtw8822c_dac_cal_restore_dck(rtwdev);

 rtw_write32_mask(rtwdev, 0x18c0, 0x38000, 0x7);
 rtw_write32_mask(rtwdev, 0x18dc, 0x38000, 0x7);
 rtw_write32_mask(rtwdev, 0x41c0, 0x38000, 0x7);
 rtw_write32_mask(rtwdev, 0x41dc, 0x38000, 0x7);

 rtw_write32_mask(rtwdev, 0x18b8, BIT(26) | BIT(25), 0x1);
 rtw_write32_mask(rtwdev, 0x18d4, BIT(26) | BIT(25), 0x1);

 rtw_write32_mask(rtwdev, 0x41b0, 0xf00, 0x0);
 rtw_write32_mask(rtwdev, 0x41c0, BIT(14), 0x0);
 rtw_write32_mask(rtwdev, 0x41cc, 0xf00, 0x0);
 rtw_write32_mask(rtwdev, 0x41dc, BIT(14), 0x0);

 rtw_write32_mask(rtwdev, 0x41b0, BIT(0), 0x0);
 rtw_write32_mask(rtwdev, 0x41cc, BIT(0), 0x0);
 rtw_write32_mask(rtwdev, 0x41b0, BIT(0), 0x1);
 rtw_write32_mask(rtwdev, 0x41cc, BIT(0), 0x1);

 rtw_write32_mask(rtwdev, 0x41b8, BIT(26) | BIT(25), 0x1);
 rtw_write32_mask(rtwdev, 0x41d4, BIT(26) | BIT(25), 0x1);
}

static bool rtw8822c_dac_cal_restore_wait(struct rtw_dev *rtwdev,
       u32 target_addr, u32 toggle_addr)
{
 u32 cnt = 0;

 do {
  rtw_write32_mask(rtwdev, toggle_addr, BIT(26) | BIT(25), 0x0);
  rtw_write32_mask(rtwdev, toggle_addr, BIT(26) | BIT(25), 0x2);

  if (rtw_read32_mask(rtwdev, target_addr, 0xf) == 0x6)
   return true;

 } while (cnt++ < 100);

 return false;
}

static bool rtw8822c_dac_cal_restore_path(struct rtw_dev *rtwdev, u8 path)
{
 struct rtw_dm_info *dm_info = &rtwdev->dm_info;
 u32 w_off = 0x1c;
 u32 r_off = 0x2c;
 u32 w_i, r_i, w_q, r_q;
 u32 value;
 u32 i;

 w_i = rtw8822c_get_path_write_addr(path) + 0xb0;
 r_i = rtw8822c_get_path_read_addr(path) + 0x08;
 w_q = rtw8822c_get_path_write_addr(path) + 0xb0 + w_off;
 r_q = rtw8822c_get_path_read_addr(path) + 0x08 + r_off;

 if (!rtw8822c_dac_cal_restore_wait(rtwdev, r_i, w_i + 0x8))
  return false;

 for (i = 0; i < DACK_MSBK_BACKUP_NUM; i++) {
  rtw_write32_mask(rtwdev, w_i + 0x4, BIT(2), 0x0);
  value = dm_info->dack_msbk[path][0][i];
  rtw_write32_mask(rtwdev, w_i + 0x4, 0xff8, value);
  rtw_write32_mask(rtwdev, w_i, 0xf0000000, i);
  rtw_write32_mask(rtwdev, w_i + 0x4, BIT(2), 0x1);
 }

 rtw_write32_mask(rtwdev, w_i + 0x4, BIT(2), 0x0);

 if (!rtw8822c_dac_cal_restore_wait(rtwdev, r_q, w_q + 0x8))
  return false;

 for (i = 0; i < DACK_MSBK_BACKUP_NUM; i++) {
  rtw_write32_mask(rtwdev, w_q + 0x4, BIT(2), 0x0);
  value = dm_info->dack_msbk[path][1][i];
  rtw_write32_mask(rtwdev, w_q + 0x4, 0xff8, value);
  rtw_write32_mask(rtwdev, w_q, 0xf0000000, i);
  rtw_write32_mask(rtwdev, w_q + 0x4, BIT(2), 0x1);
 }
 rtw_write32_mask(rtwdev, w_q + 0x4, BIT(2), 0x0);

 rtw_write32_mask(rtwdev, w_i + 0x8, BIT(26) | BIT(25), 0x0);
 rtw_write32_mask(rtwdev, w_q + 0x8, BIT(26) | BIT(25), 0x0);
 rtw_write32_mask(rtwdev, w_i + 0x4, BIT(0), 0x0);
 rtw_write32_mask(rtwdev, w_q + 0x4, BIT(0), 0x0);

 return true;
}

static bool __rtw8822c_dac_cal_restore(struct rtw_dev *rtwdev)
{
 if (!rtw8822c_dac_cal_restore_path(rtwdev, RF_PATH_A))
  return false;

 if (!rtw8822c_dac_cal_restore_path(rtwdev, RF_PATH_B))
  return false;

 return true;
}

static bool rtw8822c_dac_cal_restore(struct rtw_dev *rtwdev)
{
 struct rtw_dm_info *dm_info = &rtwdev->dm_info;
 u32 temp[3];

 /* sample the first element for both path's IQ vector */
 if (dm_info->dack_msbk[RF_PATH_A][0][0] == 0 &&
     dm_info->dack_msbk[RF_PATH_A][1][0] == 0 &&
     dm_info->dack_msbk[RF_PATH_B][0][0] == 0 &&
     dm_info->dack_msbk[RF_PATH_B][1][0] == 0)
  return false;

 temp[0] = rtw_read32(rtwdev, 0x1860);
 temp[1] = rtw_read32(rtwdev, 0x4160);
 temp[2] = rtw_read32(rtwdev, 0x9b4);

 rtw8822c_dac_cal_restore_prepare(rtwdev);
 if (!check_hw_ready(rtwdev, 0x2808, 0x7fff80, 0xffff) ||
     !check_hw_ready(rtwdev, 0x2834, 0x7fff80, 0xffff) ||
     !check_hw_ready(rtwdev, 0x4508, 0x7fff80, 0xffff) ||
     !check_hw_ready(rtwdev, 0x4534, 0x7fff80, 0xffff))
  return false;

 if (!__rtw8822c_dac_cal_restore(rtwdev)) {
  rtw_err(rtwdev, "failed to restore dack vectors\n");
  return false;
 }

 rtw_write32_mask(rtwdev, 0x1830, BIT(30), 0x1);
 rtw_write32_mask(rtwdev, 0x4130, BIT(30), 0x1);
 rtw_write32(rtwdev, 0x1860, temp[0]);
 rtw_write32(rtwdev, 0x4160, temp[1]);
 rtw_write32_mask(rtwdev, 0x18b0, BIT(27), 0x1);
 rtw_write32_mask(rtwdev, 0x18cc, BIT(27), 0x1);
 rtw_write32_mask(rtwdev, 0x41b0, BIT(27), 0x1);
 rtw_write32_mask(rtwdev, 0x41cc, BIT(27), 0x1);
 rtw_write32(rtwdev, 0x9b4, temp[2]);

 return true;
}

static void rtw8822c_rf_dac_cal(struct rtw_dev *rtwdev)
{
 struct rtw_backup_info backup_rf[DACK_RF_8822C * DACK_PATH_8822C];
 struct rtw_backup_info backup[DACK_REG_8822C];
 u32 ic = 0, qc = 0, i;
 u32 i_a = 0x0, q_a = 0x0, i_b = 0x0, q_b = 0x0;
 u32 ic_a = 0x0, qc_a = 0x0, ic_b = 0x0, qc_b = 0x0;
 u32 adc_ic_a = 0x0, adc_qc_a = 0x0, adc_ic_b = 0x0, adc_qc_b = 0x0;

 if (rtw8822c_dac_cal_restore(rtwdev))
  return;

 /* not able to restore, do it */

 rtw8822c_dac_backup_reg(rtwdev, backup, backup_rf);

 rtw8822c_dac_bb_setting(rtwdev);

 /* path-A */
 rtw8822c_dac_cal_adc(rtwdev, RF_PATH_A, &adc_ic_a, &adc_qc_a);
 for (i = 0; i < 10; i++) {
  rtw8822c_dac_cal_step1(rtwdev, RF_PATH_A);
  rtw8822c_dac_cal_step2(rtwdev, RF_PATH_A, &ic, &qc);
  ic_a = ic;
  qc_a = qc;

  rtw8822c_dac_cal_step3(rtwdev, RF_PATH_A, adc_ic_a, adc_qc_a,
           &ic, &qc, &i_a, &q_a);

  if (ic < 5 && qc < 5)
   break;
 }
 rtw8822c_dac_cal_step4(rtwdev, RF_PATH_A);

 /* path-B */
 rtw8822c_dac_cal_adc(rtwdev, RF_PATH_B, &adc_ic_b, &adc_qc_b);
 for (i = 0; i < 10; i++) {
  rtw8822c_dac_cal_step1(rtwdev, RF_PATH_B);
  rtw8822c_dac_cal_step2(rtwdev, RF_PATH_B, &ic, &qc);
  ic_b = ic;
  qc_b = qc;

  rtw8822c_dac_cal_step3(rtwdev, RF_PATH_B, adc_ic_b, adc_qc_b,
           &ic, &qc, &i_b, &q_b);

  if (ic < 5 && qc < 5)
   break;
 }
 rtw8822c_dac_cal_step4(rtwdev, RF_PATH_B);

 rtw_write32(rtwdev, 0x1b00, 0x00000008);
 rtw_write32_mask(rtwdev, 0x4130, BIT(30), 0x1);
 rtw_write8(rtwdev, 0x1bcc, 0x0);
 rtw_write32(rtwdev, 0x1b00, 0x0000000a);
 rtw_write8(rtwdev, 0x1bcc, 0x0);

 rtw8822c_dac_restore_reg(rtwdev, backup, backup_rf);

 /* backup results to restore, saving a lot of time */
 rtw8822c_dac_cal_backup(rtwdev);

 rtw_dbg(rtwdev, RTW_DBG_RFK, "[DACK] path A: ic=0x%x, qc=0x%x\n", ic_a, qc_a);
 rtw_dbg(rtwdev, RTW_DBG_RFK, "[DACK] path B: ic=0x%x, qc=0x%x\n", ic_b, qc_b);
 rtw_dbg(rtwdev, RTW_DBG_RFK, "[DACK] path A: i=0x%x, q=0x%x\n", i_a, q_a);
 rtw_dbg(rtwdev, RTW_DBG_RFK, "[DACK] path B: i=0x%x, q=0x%x\n", i_b, q_b);
}

static void rtw8822c_rf_x2_check(struct rtw_dev *rtwdev)
{
 u8 x2k_busy;

 mdelay(1);
 x2k_busy = rtw_read_rf(rtwdev, RF_PATH_A, 0xb8, BIT(15));
 if (x2k_busy == 1) {
  rtw_write_rf(rtwdev, RF_PATH_A, 0xb8, RFREG_MASK, 0xC4440);
  rtw_write_rf(rtwdev, RF_PATH_A, 0xba, RFREG_MASK, 0x6840D);
  rtw_write_rf(rtwdev, RF_PATH_A, 0xb8, RFREG_MASK, 0x80440);
  mdelay(1);
 }
}

static void rtw8822c_set_power_trim(struct rtw_dev *rtwdev, s8 bb_gain[2][8])
{
#define RF_SET_POWER_TRIM(_path, _seq, _idx)     \
  do {        \
   rtw_write_rf(rtwdev, _path, 0x33, RFREG_MASK, _seq); \
   rtw_write_rf(rtwdev, _path, 0x3f, RFREG_MASK,  \
         bb_gain[_path][_idx]);   \
  } while (0)
 u8 path;

 for (path = 0; path < rtwdev->hal.rf_path_num; path++) {
  rtw_write_rf(rtwdev, path, 0xee, BIT(19), 1);
  RF_SET_POWER_TRIM(path, 0x0, 0);
  RF_SET_POWER_TRIM(path, 0x1, 1);
  RF_SET_POWER_TRIM(path, 0x2, 2);
  RF_SET_POWER_TRIM(path, 0x3, 2);
  RF_SET_POWER_TRIM(path, 0x4, 3);
  RF_SET_POWER_TRIM(path, 0x5, 4);
  RF_SET_POWER_TRIM(path, 0x6, 5);
  RF_SET_POWER_TRIM(path, 0x7, 6);
  RF_SET_POWER_TRIM(path, 0x8, 7);
  RF_SET_POWER_TRIM(path, 0x9, 3);
  RF_SET_POWER_TRIM(path, 0xa, 4);
  RF_SET_POWER_TRIM(path, 0xb, 5);
  RF_SET_POWER_TRIM(path, 0xc, 6);
  RF_SET_POWER_TRIM(path, 0xd, 7);
  RF_SET_POWER_TRIM(path, 0xe, 7);
  rtw_write_rf(rtwdev, path, 0xee, BIT(19), 0);
 }
#undef RF_SET_POWER_TRIM
}

static void rtw8822c_power_trim(struct rtw_dev *rtwdev)
{
 u8 pg_pwr = 0xff, i, path, idx;
 s8 bb_gain[2][8] = {};
 u16 rf_efuse_2g[3] = {PPG_2GL_TXAB, PPG_2GM_TXAB, PPG_2GH_TXAB};
 u16 rf_efuse_5g[2][5] = {{PPG_5GL1_TXA, PPG_5GL2_TXA, PPG_5GM1_TXA,
      PPG_5GM2_TXA, PPG_5GH1_TXA},
     {PPG_5GL1_TXB, PPG_5GL2_TXB, PPG_5GM1_TXB,
      PPG_5GM2_TXB, PPG_5GH1_TXB} };
 bool set = false;

 for (i = 0; i < ARRAY_SIZE(rf_efuse_2g); i++) {
  rtw_read8_physical_efuse(rtwdev, rf_efuse_2g[i], &pg_pwr);
  if (pg_pwr == EFUSE_READ_FAIL)
   continue;
  set = true;
  bb_gain[RF_PATH_A][i] = FIELD_GET(PPG_2G_A_MASK, pg_pwr);
  bb_gain[RF_PATH_B][i] = FIELD_GET(PPG_2G_B_MASK, pg_pwr);
 }

 for (i = 0; i < ARRAY_SIZE(rf_efuse_5g[0]); i++) {
  for (path = 0; path < rtwdev->hal.rf_path_num; path++) {
   rtw_read8_physical_efuse(rtwdev, rf_efuse_5g[path][i],
       &pg_pwr);
   if (pg_pwr == EFUSE_READ_FAIL)
    continue;
   set = true;
   idx = i + ARRAY_SIZE(rf_efuse_2g);
   bb_gain[path][idx] = FIELD_GET(PPG_5G_MASK, pg_pwr);
  }
 }
 if (set)
  rtw8822c_set_power_trim(rtwdev, bb_gain);

 rtw_write32_mask(rtwdev, REG_DIS_DPD, DIS_DPD_MASK, DIS_DPD_RATEALL);
}

static void rtw8822c_thermal_trim(struct rtw_dev *rtwdev)
{
 u16 rf_efuse[2] = {PPG_THERMAL_A, PPG_THERMAL_B};
 u8 pg_therm = 0xff, thermal[2] = {0}, path;

 for (path = 0; path < rtwdev->hal.rf_path_num; path++) {
  rtw_read8_physical_efuse(rtwdev, rf_efuse[path], &pg_therm);
  if (pg_therm == EFUSE_READ_FAIL)
   return;
  /* Efuse value of BIT(0) shall be move to BIT(3), and the value
 * of BIT(1) to BIT(3) should be right shifted 1 bit.
 */

  thermal[path] = FIELD_GET(GENMASK(3, 1), pg_therm);
  thermal[path] |= FIELD_PREP(BIT(3), pg_therm & BIT(0));
  rtw_write_rf(rtwdev, path, 0x43, RF_THEMAL_MASK, thermal[path]);
 }
}

static void rtw8822c_pa_bias(struct rtw_dev *rtwdev)
{
 u16 rf_efuse_2g[2] = {PPG_PABIAS_2GA, PPG_PABIAS_2GB};
 u16 rf_efuse_5g[2] = {PPG_PABIAS_5GA, PPG_PABIAS_5GB};
 u8 pg_pa_bias = 0xff, path;

 for (path = 0; path < rtwdev->hal.rf_path_num; path++) {
  rtw_read8_physical_efuse(rtwdev, rf_efuse_2g[path],
      &pg_pa_bias);
  if (pg_pa_bias == EFUSE_READ_FAIL)
   return;
  pg_pa_bias = FIELD_GET(PPG_PABIAS_MASK, pg_pa_bias);
  rtw_write_rf(rtwdev, path, RF_PA, RF_PABIAS_2G_MASK, pg_pa_bias);
 }
 for (path = 0; path < rtwdev->hal.rf_path_num; path++) {
  rtw_read8_physical_efuse(rtwdev, rf_efuse_5g[path],
      &pg_pa_bias);
  pg_pa_bias = FIELD_GET(PPG_PABIAS_MASK, pg_pa_bias);
  rtw_write_rf(rtwdev, path, RF_PA, RF_PABIAS_5G_MASK, pg_pa_bias);
 }
}

static void rtw8822c_rfk_handshake(struct rtw_dev *rtwdev, bool is_before_k)
{
 struct rtw_dm_info *dm = &rtwdev->dm_info;
 u8 u1b_tmp;
 u8 u4b_tmp;
 int ret;

 if (is_before_k) {
  rtw_dbg(rtwdev, RTW_DBG_RFK,
   "[RFK] WiFi / BT RFK handshake start!!\n");

  if (!dm->is_bt_iqk_timeout) {
   ret = read_poll_timeout(rtw_read32_mask, u4b_tmp,
      u4b_tmp == 0, 20, 600000, false,
      rtwdev, REG_PMC_DBG_CTRL1,
      BITS_PMC_BT_IQK_STS);
   if (ret) {
    rtw_dbg(rtwdev, RTW_DBG_RFK,
     "[RFK] Wait BT IQK finish timeout!!\n");
    dm->is_bt_iqk_timeout = true;
   }
  }

  rtw_fw_inform_rfk_status(rtwdev, true);

  ret = read_poll_timeout(rtw_read8_mask, u1b_tmp,
     u1b_tmp == 1, 20, 100000, false,
     rtwdev, REG_ARFR4, BIT_WL_RFK);
  if (ret)
   rtw_dbg(rtwdev, RTW_DBG_RFK,
    "[RFK] Send WiFi RFK start H2C cmd FAIL!!\n");
 } else {
  rtw_fw_inform_rfk_status(rtwdev, false);
  ret = read_poll_timeout(rtw_read8_mask, u1b_tmp,
     u1b_tmp == 1, 20, 100000, false,
     rtwdev, REG_ARFR4,
     BIT_WL_RFK);
  if (ret)
   rtw_dbg(rtwdev, RTW_DBG_RFK,
    "[RFK] Send WiFi RFK finish H2C cmd FAIL!!\n");

  rtw_dbg(rtwdev, RTW_DBG_RFK,
   "[RFK] WiFi / BT RFK handshake finish!!\n");
 }
}

static void rtw8822c_rfk_power_save(struct rtw_dev *rtwdev,
        bool is_power_save)
{
 u8 path;

 for (path = 0; path < rtwdev->hal.rf_path_num; path++) {
  rtw_write32_mask(rtwdev, REG_NCTL0, BIT_SEL_PATH, path);
  rtw_write32_mask(rtwdev, REG_DPD_CTL1_S0, BIT_PS_EN,
     is_power_save ? 0 : 1);
 }
}

static void rtw8822c_txgapk_backup_bb_reg(struct rtw_dev *rtwdev, const u32 reg[],
       u32 reg_backup[], u32 reg_num)
{
 u32 i;

 for (i = 0; i < reg_num; i++) {
  reg_backup[i] = rtw_read32(rtwdev, reg[i]);

  rtw_dbg(rtwdev, RTW_DBG_RFK, "[TXGAPK] Backup BB 0x%x = 0x%x\n",
   reg[i], reg_backup[i]);
 }
}

static void rtw8822c_txgapk_reload_bb_reg(struct rtw_dev *rtwdev,
       const u32 reg[], u32 reg_backup[],
       u32 reg_num)
{
 u32 i;

 for (i = 0; i < reg_num; i++) {
  rtw_write32(rtwdev, reg[i], reg_backup[i]);
  rtw_dbg(rtwdev, RTW_DBG_RFK, "[TXGAPK] Reload BB 0x%x = 0x%x\n",
   reg[i], reg_backup[i]);
 }
}

static bool check_rf_status(struct rtw_dev *rtwdev, u8 status)
{
 u8 reg_rf0_a, reg_rf0_b;

 reg_rf0_a = (u8)rtw_read_rf(rtwdev, RF_PATH_A,
        RF_MODE_TRXAGC, BIT_RF_MODE);
 reg_rf0_b = (u8)rtw_read_rf(rtwdev, RF_PATH_B,
        RF_MODE_TRXAGC, BIT_RF_MODE);

 if (reg_rf0_a == status || reg_rf0_b == status)
  return false;

 return true;
}

static void rtw8822c_txgapk_tx_pause(struct rtw_dev *rtwdev)
{
 bool status;
 int ret;

 rtw_write8(rtwdev, REG_TXPAUSE, BIT_AC_QUEUE);
 rtw_write32_mask(rtwdev, REG_TX_FIFO, BIT_STOP_TX, 0x2);

 ret = read_poll_timeout_atomic(check_rf_status, status, status,
           2, 5000, false, rtwdev, 2);
 if (ret)
  rtw_warn(rtwdev, "failed to pause TX\n");

 rtw_dbg(rtwdev, RTW_DBG_RFK, "[TXGAPK] Tx pause!!\n");
}

static void rtw8822c_txgapk_bb_dpk(struct rtw_dev *rtwdev, u8 path)
{
 rtw_dbg(rtwdev, RTW_DBG_RFK, "[TXGAPK] ======>%s\n", __func__);

 rtw_write32_mask(rtwdev, REG_ENFN, BIT_IQK_DPK_EN, 0x1);
 rtw_write32_mask(rtwdev, REG_CH_DELAY_EXTR2,
    BIT_IQK_DPK_CLOCK_SRC, 0x1);
 rtw_write32_mask(rtwdev, REG_CH_DELAY_EXTR2,
    BIT_IQK_DPK_RESET_SRC, 0x1);
 rtw_write32_mask(rtwdev, REG_CH_DELAY_EXTR2, BIT_EN_IOQ_IQK_DPK, 0x1);
 rtw_write32_mask(rtwdev, REG_CH_DELAY_EXTR2, BIT_TST_IQK2SET_SRC, 0x0);
 rtw_write32_mask(rtwdev, REG_CCA_OFF, BIT_CCA_ON_BY_PW, 0x1ff);

 if (path == RF_PATH_A) {
  rtw_write32_mask(rtwdev, REG_RFTXEN_GCK_A,
     BIT_RFTXEN_GCK_FORCE_ON, 0x1);
  rtw_write32_mask(rtwdev, REG_3WIRE, BIT_DIS_SHARERX_TXGAT, 0x1);
  rtw_write32_mask(rtwdev, REG_DIS_SHARE_RX_A,
     BIT_TX_SCALE_0DB, 0x1);
  rtw_write32_mask(rtwdev, REG_3WIRE, BIT_3WIRE_EN, 0x0);
 } else if (path == RF_PATH_B) {
  rtw_write32_mask(rtwdev, REG_RFTXEN_GCK_B,
     BIT_RFTXEN_GCK_FORCE_ON, 0x1);
  rtw_write32_mask(rtwdev, REG_3WIRE2,
     BIT_DIS_SHARERX_TXGAT, 0x1);
  rtw_write32_mask(rtwdev, REG_DIS_SHARE_RX_B,
     BIT_TX_SCALE_0DB, 0x1);
  rtw_write32_mask(rtwdev, REG_3WIRE2, BIT_3WIRE_EN, 0x0);
 }
 rtw_write32_mask(rtwdev, REG_CCKSB, BIT_BBMODE, 0x2);
}

static void rtw8822c_txgapk_afe_dpk(struct rtw_dev *rtwdev, u8 path)
{
 u32 reg;

 rtw_dbg(rtwdev, RTW_DBG_RFK, "[TXGAPK] ======>%s\n", __func__);

 if (path == RF_PATH_A) {
  reg = REG_ANAPAR_A;
 } else if (path == RF_PATH_B) {
  reg = REG_ANAPAR_B;
 } else {
  rtw_err(rtwdev, "[TXGAPK] unknown path %d!!\n", path);
  return;
 }

 rtw_write32_mask(rtwdev, REG_IQK_CTRL, MASKDWORD, MASKDWORD);
 rtw_write32_mask(rtwdev, reg, MASKDWORD, 0x700f0001);
 rtw_write32_mask(rtwdev, reg, MASKDWORD, 0x700f0001);
 rtw_write32_mask(rtwdev, reg, MASKDWORD, 0x701f0001);
 rtw_write32_mask(rtwdev, reg, MASKDWORD, 0x702f0001);
 rtw_write32_mask(rtwdev, reg, MASKDWORD, 0x703f0001);
 rtw_write32_mask(rtwdev, reg, MASKDWORD, 0x704f0001);
 rtw_write32_mask(rtwdev, reg, MASKDWORD, 0x705f0001);
 rtw_write32_mask(rtwdev, reg, MASKDWORD, 0x706f0001);
 rtw_write32_mask(rtwdev, reg, MASKDWORD, 0x707f0001);
 rtw_write32_mask(rtwdev, reg, MASKDWORD, 0x708f0001);
 rtw_write32_mask(rtwdev, reg, MASKDWORD, 0x709f0001);
 rtw_write32_mask(rtwdev, reg, MASKDWORD, 0x70af0001);
 rtw_write32_mask(rtwdev, reg, MASKDWORD, 0x70bf0001);
 rtw_write32_mask(rtwdev, reg, MASKDWORD, 0x70cf0001);
 rtw_write32_mask(rtwdev, reg, MASKDWORD, 0x70df0001);
 rtw_write32_mask(rtwdev, reg, MASKDWORD, 0x70ef0001);
 rtw_write32_mask(rtwdev, reg, MASKDWORD, 0x70ff0001);
 rtw_write32_mask(rtwdev, reg, MASKDWORD, 0x70ff0001);
}

static void rtw8822c_txgapk_afe_dpk_restore(struct rtw_dev *rtwdev, u8 path)
{
 u32 reg;

 rtw_dbg(rtwdev, RTW_DBG_RFK, "[TXGAPK] ======>%s\n", __func__);

 if (path == RF_PATH_A) {
  reg = REG_ANAPAR_A;
 } else if (path == RF_PATH_B) {
  reg = REG_ANAPAR_B;
 } else {
  rtw_err(rtwdev, "[TXGAPK] unknown path %d!!\n", path);
  return;
 }
 rtw_write32_mask(rtwdev, REG_IQK_CTRL, MASKDWORD, 0xffa1005e);
 rtw_write32_mask(rtwdev, reg, MASKDWORD, 0x700b8041);
 rtw_write32_mask(rtwdev, reg, MASKDWORD, 0x70144041);
 rtw_write32_mask(rtwdev, reg, MASKDWORD, 0x70244041);
 rtw_write32_mask(rtwdev, reg, MASKDWORD, 0x70344041);
 rtw_write32_mask(rtwdev, reg, MASKDWORD, 0x70444041);
 rtw_write32_mask(rtwdev, reg, MASKDWORD, 0x705b8041);
 rtw_write32_mask(rtwdev, reg, MASKDWORD, 0x70644041);
 rtw_write32_mask(rtwdev, reg, MASKDWORD, 0x707b8041);
 rtw_write32_mask(rtwdev, reg, MASKDWORD, 0x708b8041);
 rtw_write32_mask(rtwdev, reg, MASKDWORD, 0x709b8041);
 rtw_write32_mask(rtwdev, reg, MASKDWORD, 0x70ab8041);
 rtw_write32_mask(rtwdev, reg, MASKDWORD, 0x70bb8041);
 rtw_write32_mask(rtwdev, reg, MASKDWORD, 0x70cb8041);
 rtw_write32_mask(rtwdev, reg, MASKDWORD, 0x70db8041);
 rtw_write32_mask(rtwdev, reg, MASKDWORD, 0x70eb8041);
 rtw_write32_mask(rtwdev, reg, MASKDWORD, 0x70fb8041);
}

static void rtw8822c_txgapk_bb_dpk_restore(struct rtw_dev *rtwdev, u8 path)
{
 rtw_dbg(rtwdev, RTW_DBG_RFK, "[TXGAPK] ======>%s\n", __func__);

 rtw_write_rf(rtwdev, path, RF_DEBUG, BIT_DE_TX_GAIN, 0x0);
 rtw_write_rf(rtwdev, path, RF_DIS_BYPASS_TXBB, BIT_TIA_BYPASS, 0x0);
 rtw_write_rf(rtwdev, path, RF_DIS_BYPASS_TXBB, BIT_TXBB, 0x0);

 rtw_write32_mask(rtwdev, REG_NCTL0, BIT_SEL_PATH, 0x0);
 rtw_write32_mask(rtwdev, REG_IQK_CTL1, BIT_TX_CFIR, 0x0);
 rtw_write32_mask(rtwdev, REG_SINGLE_TONE_SW, BIT_IRQ_TEST_MODE, 0x0);
 rtw_write32_mask(rtwdev, REG_R_CONFIG, MASKBYTE0, 0x00);
 rtw_write32_mask(rtwdev, REG_NCTL0, BIT_SEL_PATH, 0x1);
 rtw_write32_mask(rtwdev, REG_IQK_CTL1, BIT_TX_CFIR, 0x0);
 rtw_write32_mask(rtwdev, REG_SINGLE_TONE_SW, BIT_IRQ_TEST_MODE, 0x0);
 rtw_write32_mask(rtwdev, REG_R_CONFIG, MASKBYTE0, 0x00);
 rtw_write32_mask(rtwdev, REG_NCTL0, BIT_SEL_PATH, 0x0);
 rtw_write32_mask(rtwdev, REG_CCA_OFF, BIT_CCA_ON_BY_PW, 0x0);

 if (path == RF_PATH_A) {
  rtw_write32_mask(rtwdev, REG_RFTXEN_GCK_A,
     BIT_RFTXEN_GCK_FORCE_ON, 0x0);
  rtw_write32_mask(rtwdev, REG_3WIRE, BIT_DIS_SHARERX_TXGAT, 0x0);
  rtw_write32_mask(rtwdev, REG_DIS_SHARE_RX_A,
     BIT_TX_SCALE_0DB, 0x0);
  rtw_write32_mask(rtwdev, REG_3WIRE, BIT_3WIRE_EN, 0x3);
 } else if (path == RF_PATH_B) {
  rtw_write32_mask(rtwdev, REG_RFTXEN_GCK_B,
     BIT_RFTXEN_GCK_FORCE_ON, 0x0);
  rtw_write32_mask(rtwdev, REG_3WIRE2,
     BIT_DIS_SHARERX_TXGAT, 0x0);
  rtw_write32_mask(rtwdev, REG_DIS_SHARE_RX_B,
     BIT_TX_SCALE_0DB, 0x0);
  rtw_write32_mask(rtwdev, REG_3WIRE2, BIT_3WIRE_EN, 0x3);
 }

 rtw_write32_mask(rtwdev, REG_CCKSB, BIT_BBMODE, 0x0);
 rtw_write32_mask(rtwdev, REG_IQK_CTL1, BIT_CFIR_EN, 0x5);
}

static bool _rtw8822c_txgapk_gain_valid(struct rtw_dev *rtwdev, u32 gain)
{
 if ((FIELD_GET(BIT_GAIN_TX_PAD_H, gain) >= 0xc) &&
     (FIELD_GET(BIT_GAIN_TX_PAD_L, gain) >= 0xe))
  return true;

 return false;
}

static void _rtw8822c_txgapk_write_gain_bb_table(struct rtw_dev *rtwdev,
       u8 band, u8 path)
{
 struct rtw_gapk_info *txgapk = &rtwdev->dm_info.gapk;
 u32 v, tmp_3f = 0;
 u8 gain, check_txgain;

 rtw_write32_mask(rtwdev, REG_NCTL0, BIT_SEL_PATH, path);

 switch (band) {
 case RF_BAND_2G_OFDM:
  rtw_write32_mask(rtwdev, REG_TABLE_SEL, BIT_Q_GAIN_SEL, 0x0);
  break;
 case RF_BAND_5G_L:
  rtw_write32_mask(rtwdev, REG_TABLE_SEL, BIT_Q_GAIN_SEL, 0x2);
  break;
 case RF_BAND_5G_M:
  rtw_write32_mask(rtwdev, REG_TABLE_SEL, BIT_Q_GAIN_SEL, 0x3);
  break;
 case RF_BAND_5G_H:
  rtw_write32_mask(rtwdev, REG_TABLE_SEL, BIT_Q_GAIN_SEL, 0x4);
  break;
 default:
  break;
 }

 rtw_write32_mask(rtwdev, REG_TX_GAIN_SET, MASKBYTE0, 0x88);

 check_txgain = 0;
 for (gain = 0; gain < RF_GAIN_NUM; gain++) {
  v = txgapk->rf3f_bp[band][gain][path];
  if (_rtw8822c_txgapk_gain_valid(rtwdev, v)) {
   if (!check_txgain) {
    tmp_3f = txgapk->rf3f_bp[band][gain][path];
    check_txgain = 1;
   }
   rtw_dbg(rtwdev, RTW_DBG_RFK,
    "[TXGAPK] tx_gain=0x%03X >= 0xCEX\n",
    txgapk->rf3f_bp[band][gain][path]);
  } else {
   tmp_3f = txgapk->rf3f_bp[band][gain][path];
  }

  rtw_write32_mask(rtwdev, REG_TABLE_SEL, BIT_Q_GAIN, tmp_3f);
  rtw_write32_mask(rtwdev, REG_TABLE_SEL, BIT_I_GAIN, gain);
  rtw_write32_mask(rtwdev, REG_TABLE_SEL, BIT_GAIN_RST, 0x1);
  rtw_write32_mask(rtwdev, REG_TABLE_SEL, BIT_GAIN_RST, 0x0);

  rtw_dbg(rtwdev, RTW_DBG_RFK,
   "[TXGAPK] Band=%d 0x1b98[11:0]=0x%03X path=%d\n",
   band, tmp_3f, path);
 }
}

static void rtw8822c_txgapk_write_gain_bb_table(struct rtw_dev *rtwdev)
{
 u8 path, band;

 rtw_dbg(rtwdev, RTW_DBG_RFK, "[TXGAPK] ======>%s channel=%d\n",
  __func__, rtwdev->dm_info.gapk.channel);

 for (band = 0; band < RF_BAND_MAX; band++) {
  for (path = 0; path < rtwdev->hal.rf_path_num; path++) {
   _rtw8822c_txgapk_write_gain_bb_table(rtwdev,
            band, path);
  }
 }
}

static void rtw8822c_txgapk_read_offset(struct rtw_dev *rtwdev, u8 path)
{
 static const u32 cfg1_1b00[2] = {0x00000d18, 0x00000d2a};
 static const u32 cfg2_1b00[2] = {0x00000d19, 0x00000d2b};
 static const u32 set_pi[2] = {REG_RSV_CTRL, REG_WLRF1};
 static const u32 path_setting[2] = {REG_ORITXCODE, REG_ORITXCODE2};
 struct rtw_gapk_info *txgapk = &rtwdev->dm_info.gapk;
 u8 channel = txgapk->channel;
 u32 val;
 int i;

 if (path >= ARRAY_SIZE(cfg1_1b00) ||
     path >= ARRAY_SIZE(cfg2_1b00) ||
     path >= ARRAY_SIZE(set_pi) ||
     path >= ARRAY_SIZE(path_setting)) {
  rtw_warn(rtwdev, "[TXGAPK] wrong path %d\n", path);
  return;
 }

 rtw_write32_mask(rtwdev, REG_ANTMAP0, BIT_ANT_PATH, path + 1);
 rtw_write32_mask(rtwdev, REG_TXLGMAP, MASKDWORD, 0xe4e40000);
 rtw_write32_mask(rtwdev, REG_TXANTSEG, BIT_ANTSEG, 0x3);
 rtw_write32_mask(rtwdev, path_setting[path], MASK20BITS, 0x33312);
 rtw_write32_mask(rtwdev, path_setting[path], BIT_PATH_EN, 0x1);
 rtw_write32_mask(rtwdev, set_pi[path], BITS_RFC_DIRECT, 0x0);
 rtw_write_rf(rtwdev, path, RF_LUTDBG, BIT_TXA_TANK, 0x1);
 rtw_write_rf(rtwdev, path, RF_IDAC, BIT_TX_MODE, 0x820);
 rtw_write32_mask(rtwdev, REG_NCTL0, BIT_SEL_PATH, path);
 rtw_write32_mask(rtwdev, REG_IQKSTAT, MASKBYTE0, 0x0);

 rtw_write32_mask(rtwdev, REG_TX_TONE_IDX, MASKBYTE0, 0x018);
 fsleep(1000);
 if (channel >= 1 && channel <= 14)
  rtw_write32_mask(rtwdev, REG_R_CONFIG, MASKBYTE0, BIT_2G_SWING);
 else
  rtw_write32_mask(rtwdev, REG_R_CONFIG, MASKBYTE0, BIT_5G_SWING);
 fsleep(1000);

 rtw_write32_mask(rtwdev, REG_NCTL0, MASKDWORD, cfg1_1b00[path]);
 rtw_write32_mask(rtwdev, REG_NCTL0, MASKDWORD, cfg2_1b00[path]);

 read_poll_timeout(rtw_read32_mask, val,
     val == 0x55, 1000, 100000, false,
     rtwdev, REG_RPT_CIP, BIT_RPT_CIP_STATUS);

 rtw_write32_mask(rtwdev, set_pi[path], BITS_RFC_DIRECT, 0x2);
 rtw_write32_mask(rtwdev, REG_NCTL0, BIT_SEL_PATH, path);
 rtw_write32_mask(rtwdev, REG_RXSRAM_CTL, BIT_RPT_EN, 0x1);
 rtw_write32_mask(rtwdev, REG_RXSRAM_CTL, BIT_RPT_SEL, 0x12);
 rtw_write32_mask(rtwdev, REG_TX_GAIN_SET, BIT_GAPK_RPT_IDX, 0x3);
 val = rtw_read32(rtwdev, REG_STAT_RPT);

 txgapk->offset[0][path] = (s8)FIELD_GET(BIT_GAPK_RPT0, val);
 txgapk->offset[1][path] = (s8)FIELD_GET(BIT_GAPK_RPT1, val);
 txgapk->offset[2][path] = (s8)FIELD_GET(BIT_GAPK_RPT2, val);
 txgapk->offset[3][path] = (s8)FIELD_GET(BIT_GAPK_RPT3, val);
 txgapk->offset[4][path] = (s8)FIELD_GET(BIT_GAPK_RPT4, val);
 txgapk->offset[5][path] = (s8)FIELD_GET(BIT_GAPK_RPT5, val);
 txgapk->offset[6][path] = (s8)FIELD_GET(BIT_GAPK_RPT6, val);
 txgapk->offset[7][path] = (s8)FIELD_GET(BIT_GAPK_RPT7, val);

 rtw_write32_mask(rtwdev, REG_TX_GAIN_SET, BIT_GAPK_RPT_IDX, 0x4);
 val = rtw_read32(rtwdev, REG_STAT_RPT);

 txgapk->offset[8][path] = (s8)FIELD_GET(BIT_GAPK_RPT0, val);
 txgapk->offset[9][path] = (s8)FIELD_GET(BIT_GAPK_RPT1, val);

 for (i = 0; i < RF_HW_OFFSET_NUM; i++)
  if (txgapk->offset[i][path] & BIT(3))
   txgapk->offset[i][path] = txgapk->offset[i][path] |
        0xf0;
 for (i = 0; i < RF_HW_OFFSET_NUM; i++)
  rtw_dbg(rtwdev, RTW_DBG_RFK,
   "[TXGAPK] offset %d %d path=%d\n",
   txgapk->offset[i][path], i, path);
}

static void rtw8822c_txgapk_calculate_offset(struct rtw_dev *rtwdev, u8 path)
{
 static const u32 bb_reg[] = {REG_ANTMAP0, REG_TXLGMAP, REG_TXANTSEG,
         REG_ORITXCODE, REG_ORITXCODE2};
 struct rtw_gapk_info *txgapk = &rtwdev->dm_info.gapk;
 u8 channel = txgapk->channel;
 u32 reg_backup[ARRAY_SIZE(bb_reg)] = {0};

 rtw_dbg(rtwdev, RTW_DBG_RFK, "[TXGAPK] ======>%s channel=%d\n",
  __func__, channel);

 rtw8822c_txgapk_backup_bb_reg(rtwdev, bb_reg,
          reg_backup, ARRAY_SIZE(bb_reg));

 if (channel >= 1 && channel <= 14) {
  rtw_write32_mask(rtwdev,
     REG_SINGLE_TONE_SW, BIT_IRQ_TEST_MODE, 0x0);
  rtw_write32_mask(rtwdev, REG_NCTL0, BIT_SEL_PATH, path);
  rtw_write32_mask(rtwdev, REG_R_CONFIG, BIT_IQ_SWITCH, 0x3f);
  rtw_write32_mask(rtwdev, REG_IQK_CTL1, BIT_TX_CFIR, 0x0);
  rtw_write_rf(rtwdev, path, RF_DEBUG, BIT_DE_TX_GAIN, 0x1);
  rtw_write_rf(rtwdev, path, RF_MODE_TRXAGC, RFREG_MASK, 0x5000f);
  rtw_write_rf(rtwdev, path, RF_TX_GAIN_OFFSET, BIT_RF_GAIN, 0x0);
  rtw_write_rf(rtwdev, path, RF_RXG_GAIN, BIT_RXG_GAIN, 0x1);
  rtw_write_rf(rtwdev, path, RF_MODE_TRXAGC, BIT_RXAGC, 0x0f);
  rtw_write_rf(rtwdev, path, RF_DEBUG, BIT_DE_TRXBW, 0x1);
  rtw_write_rf(rtwdev, path, RF_BW_TRXBB, BIT_BW_TXBB, 0x1);
  rtw_write_rf(rtwdev, path, RF_BW_TRXBB, BIT_BW_RXBB, 0x0);
  rtw_write_rf(rtwdev, path, RF_EXT_TIA_BW, BIT_PW_EXT_TIA, 0x1);

  rtw_write32_mask(rtwdev, REG_IQKSTAT, MASKBYTE0, 0x00);
  rtw_write32_mask(rtwdev, REG_TABLE_SEL, BIT_Q_GAIN_SEL, 0x0);

  rtw8822c_txgapk_read_offset(rtwdev, path);
  rtw_dbg(rtwdev, RTW_DBG_RFK, "=============================\n");

 } else {
  rtw_write32_mask(rtwdev,
     REG_SINGLE_TONE_SW, BIT_IRQ_TEST_MODE, 0x0);
  rtw_write32_mask(rtwdev, REG_NCTL0, BIT_SEL_PATH, path);
  rtw_write32_mask(rtwdev, REG_R_CONFIG, BIT_IQ_SWITCH, 0x3f);
  rtw_write32_mask(rtwdev, REG_IQK_CTL1, BIT_TX_CFIR, 0x0);
  rtw_write_rf(rtwdev, path, RF_DEBUG, BIT_DE_TX_GAIN, 0x1);
  rtw_write_rf(rtwdev, path, RF_MODE_TRXAGC, RFREG_MASK, 0x50011);
  rtw_write_rf(rtwdev, path, RF_TXA_LB_SW, BIT_TXA_LB_ATT, 0x3);
  rtw_write_rf(rtwdev, path, RF_TXA_LB_SW, BIT_LB_ATT, 0x3);
  rtw_write_rf(rtwdev, path, RF_TXA_LB_SW, BIT_LB_SW, 0x1);
  rtw_write_rf(rtwdev, path,
        RF_RXA_MIX_GAIN, BIT_RXA_MIX_GAIN, 0x2);
  rtw_write_rf(rtwdev, path, RF_MODE_TRXAGC, BIT_RXAGC, 0x12);
  rtw_write_rf(rtwdev, path, RF_DEBUG, BIT_DE_TRXBW, 0x1);
  rtw_write_rf(rtwdev, path, RF_BW_TRXBB, BIT_BW_RXBB, 0x0);
  rtw_write_rf(rtwdev, path, RF_EXT_TIA_BW, BIT_PW_EXT_TIA, 0x1);
  rtw_write_rf(rtwdev, path, RF_MODE_TRXAGC, BIT_RF_MODE, 0x5);

  rtw_write32_mask(rtwdev, REG_IQKSTAT, MASKBYTE0, 0x0);

  if (channel >= 36 && channel <= 64)
   rtw_write32_mask(rtwdev,
      REG_TABLE_SEL, BIT_Q_GAIN_SEL, 0x2);
  else if (channel >= 100 && channel <= 144)
   rtw_write32_mask(rtwdev,
      REG_TABLE_SEL, BIT_Q_GAIN_SEL, 0x3);
  else if (channel >= 149 && channel <= 177)
   rtw_write32_mask(rtwdev,
      REG_TABLE_SEL, BIT_Q_GAIN_SEL, 0x4);

  rtw8822c_txgapk_read_offset(rtwdev, path);
  rtw_dbg(rtwdev, RTW_DBG_RFK, "=============================\n");
 }
 rtw8822c_txgapk_reload_bb_reg(rtwdev, bb_reg,
          reg_backup, ARRAY_SIZE(bb_reg));
}

static void rtw8822c_txgapk_rf_restore(struct rtw_dev *rtwdev, u8 path)
{
 rtw_dbg(rtwdev, RTW_DBG_RFK, "[TXGAPK] ======>%s\n", __func__);

 if (path >= rtwdev->hal.rf_path_num)
  return;

 rtw_write_rf(rtwdev, path, RF_MODE_TRXAGC, BIT_RF_MODE, 0x3);
 rtw_write_rf(rtwdev, path, RF_DEBUG, BIT_DE_TRXBW, 0x0);
 rtw_write_rf(rtwdev, path, RF_EXT_TIA_BW, BIT_PW_EXT_TIA, 0x0);
}

static u32 rtw8822c_txgapk_cal_gain(struct rtw_dev *rtwdev, u32 gain, s8 offset)
{
 u32 gain_x2, new_gain;

 rtw_dbg(rtwdev, RTW_DBG_RFK, "[TXGAPK] ======>%s\n", __func__);

 if (_rtw8822c_txgapk_gain_valid(rtwdev, gain)) {
  new_gain = gain;
  rtw_dbg(rtwdev, RTW_DBG_RFK,
   "[TXGAPK] gain=0x%03X(>=0xCEX) offset=%d new_gain=0x%03X\n",
   gain, offset, new_gain);
  return new_gain;
 }

 gain_x2 = (gain << 1) + offset;
 new_gain = (gain_x2 >> 1) | (gain_x2 & BIT(0) ? BIT_GAIN_EXT : 0);

 rtw_dbg(rtwdev, RTW_DBG_RFK,
  "[TXGAPK] gain=0x%X offset=%d new_gain=0x%X\n",
  gain, offset, new_gain);

 return new_gain;
}

static void rtw8822c_txgapk_write_tx_gain(struct rtw_dev *rtwdev)
{
 struct rtw_gapk_info *txgapk = &rtwdev->dm_info.gapk;
 u32 i, j, tmp = 0x20, tmp_3f, v;
 s8 offset_tmp[RF_GAIN_NUM] = {0};
 u8 path, band = RF_BAND_2G_OFDM, channel = txgapk->channel;

 rtw_dbg(rtwdev, RTW_DBG_RFK, "[TXGAPK] ======>%s\n", __func__);

 if (channel >= 1 && channel <= 14) {
  tmp = 0x20;
  band = RF_BAND_2G_OFDM;
 } else if (channel >= 36 && channel <= 64) {
  tmp = 0x200;
  band = RF_BAND_5G_L;
 } else if (channel >= 100 && channel <= 144) {
  tmp = 0x280;
  band = RF_BAND_5G_M;
 } else if (channel >= 149 && channel <= 177) {
  tmp = 0x300;
  band = RF_BAND_5G_H;
 } else {
  rtw_err(rtwdev, "[TXGAPK] unknown channel %d!!\n", channel);
  return;
 }

 for (path = 0; path < rtwdev->hal.rf_path_num; path++) {
  for (i = 0; i < RF_GAIN_NUM; i++) {
   offset_tmp[i] = 0;
   for (j = i; j < RF_GAIN_NUM; j++) {
    v = txgapk->rf3f_bp[band][j][path];
    if (_rtw8822c_txgapk_gain_valid(rtwdev, v))
     continue;

    offset_tmp[i] += txgapk->offset[j][path];
    txgapk->fianl_offset[i][path] = offset_tmp[i];
   }

   v = txgapk->rf3f_bp[band][i][path];
   if (_rtw8822c_txgapk_gain_valid(rtwdev, v)) {
    rtw_dbg(rtwdev, RTW_DBG_RFK,
     "[TXGAPK] tx_gain=0x%03X >= 0xCEX\n",
     txgapk->rf3f_bp[band][i][path]);
   } else {
    txgapk->rf3f_fs[path][i] = offset_tmp[i];
    rtw_dbg(rtwdev, RTW_DBG_RFK,
     "[TXGAPK] offset %d %d\n",
     offset_tmp[i], i);
   }
  }

  rtw_write_rf(rtwdev, path, RF_LUTWE2, RFREG_MASK, 0x10000);
  for (i = 0; i < RF_GAIN_NUM; i++) {
   rtw_write_rf(rtwdev, path,
         RF_LUTWA, RFREG_MASK, tmp + i);

   tmp_3f = rtw8822c_txgapk_cal_gain(rtwdev,
         txgapk->rf3f_bp[band][i][path],
         offset_tmp[i]);
   rtw_write_rf(rtwdev, path, RF_LUTWD0,
         BIT_GAIN_EXT | BIT_DATA_L, tmp_3f);

   rtw_dbg(rtwdev, RTW_DBG_RFK,
    "[TXGAPK] 0x33=0x%05X 0x3f=0x%04X\n",
    tmp + i, tmp_3f);
  }
  rtw_write_rf(rtwdev, path, RF_LUTWE2, RFREG_MASK, 0x0);
 }
}

static void rtw8822c_txgapk_save_all_tx_gain_table(struct rtw_dev *rtwdev)
{
 struct rtw_gapk_info *txgapk = &rtwdev->dm_info.gapk;
 static const u32 three_wire[2] = {REG_3WIRE, REG_3WIRE2};
 static const u8 ch_num[RF_BAND_MAX] = {1, 1, 36, 100, 149};
 static const u8 band_num[RF_BAND_MAX] = {0x0, 0x0, 0x1, 0x3, 0x5};
 static const u8 cck[RF_BAND_MAX] = {0x1, 0x0, 0x0, 0x0, 0x0};
 u8 path, band, gain, rf0_idx;
 u32 rf18, v;

 if (rtwdev->dm_info.dm_flags & BIT(RTW_DM_CAP_TXGAPK))
  return;

 rtw_dbg(rtwdev, RTW_DBG_RFK, "[TXGAPK] ======>%s\n", __func__);

 if (txgapk->read_txgain == 1) {
  rtw_dbg(rtwdev, RTW_DBG_RFK,
   "[TXGAPK] Already Read txgapk->read_txgain return!!!\n");
  rtw8822c_txgapk_write_gain_bb_table(rtwdev);
  return;
 }

 for (band = 0; band < RF_BAND_MAX; band++) {
  for (path = 0; path < rtwdev->hal.rf_path_num; path++) {
   rf18 = rtw_read_rf(rtwdev, path, RF_CFGCH, RFREG_MASK);

   rtw_write32_mask(rtwdev,
      three_wire[path], BIT_3WIRE_EN, 0x0);
   rtw_write_rf(rtwdev, path,
         RF_CFGCH, MASKBYTE0, ch_num[band]);
   rtw_write_rf(rtwdev, path,
         RF_CFGCH, BIT_BAND, band_num[band]);
   rtw_write_rf(rtwdev, path,
         RF_BW_TRXBB, BIT_DBG_CCK_CCA, cck[band]);
   rtw_write_rf(rtwdev, path,
         RF_BW_TRXBB, BIT_TX_CCK_IND, cck[band]);
   gain = 0;
   for (rf0_idx = 1; rf0_idx < 32; rf0_idx += 3) {
    rtw_write_rf(rtwdev, path, RF_MODE_TRXAGC,
          MASKBYTE0, rf0_idx);
    v = rtw_read_rf(rtwdev, path,
      RF_TX_RESULT, RFREG_MASK);
    txgapk->rf3f_bp[band][gain][path] = v & BIT_DATA_L;

    rtw_dbg(rtwdev, RTW_DBG_RFK,
     "[TXGAPK] 0x5f=0x%03X band=%d path=%d\n",
     txgapk->rf3f_bp[band][gain][path],
     band, path);
    gain++;
   }
   rtw_write_rf(rtwdev, path, RF_CFGCH, RFREG_MASK, rf18);
   rtw_write32_mask(rtwdev,
      three_wire[path], BIT_3WIRE_EN, 0x3);
  }
 }
 rtw8822c_txgapk_write_gain_bb_table(rtwdev);
 txgapk->read_txgain = 1;
}

static void rtw8822c_txgapk(struct rtw_dev *rtwdev)
{
 static const u32 bb_reg[2] = {REG_TX_PTCL_CTRL, REG_TX_FIFO};
 struct rtw_gapk_info *txgapk = &rtwdev->dm_info.gapk;
 u32 bb_reg_backup[2];
 u8 path;

 rtw_dbg(rtwdev, RTW_DBG_RFK, "[TXGAPK] ======>%s\n", __func__);

 rtw8822c_txgapk_save_all_tx_gain_table(rtwdev);

 if (txgapk->read_txgain == 0) {
  rtw_dbg(rtwdev, RTW_DBG_RFK,
   "[TXGAPK] txgapk->read_txgain == 0 return!!!\n");
  return;
 }

 if (rtwdev->efuse.power_track_type >= 4 &&
     rtwdev->efuse.power_track_type <= 7) {
  rtw_dbg(rtwdev, RTW_DBG_RFK,
   "[TXGAPK] Normal Mode in TSSI mode. return!!!\n");
  return;
 }

 rtw8822c_txgapk_backup_bb_reg(rtwdev, bb_reg,
          bb_reg_backup, ARRAY_SIZE(bb_reg));
 rtw8822c_txgapk_tx_pause(rtwdev);
 for (path = 0; path < rtwdev->hal.rf_path_num; path++) {
  txgapk->channel = rtw_read_rf(rtwdev, path,
           RF_CFGCH, RFREG_MASK) & MASKBYTE0;
  rtw8822c_txgapk_bb_dpk(rtwdev, path);
  rtw8822c_txgapk_afe_dpk(rtwdev, path);
  rtw8822c_txgapk_calculate_offset(rtwdev, path);
  rtw8822c_txgapk_rf_restore(rtwdev, path);
  rtw8822c_txgapk_afe_dpk_restore(rtwdev, path);
  rtw8822c_txgapk_bb_dpk_restore(rtwdev, path);
 }
 rtw8822c_txgapk_write_tx_gain(rtwdev);
 rtw8822c_txgapk_reload_bb_reg(rtwdev, bb_reg,
          bb_reg_backup, ARRAY_SIZE(bb_reg));
}

static void rtw8822c_do_gapk(struct rtw_dev *rtwdev)
{
 struct rtw_dm_info *dm = &rtwdev->dm_info;

 if (dm->dm_flags & BIT(RTW_DM_CAP_TXGAPK)) {
  rtw_dbg(rtwdev, RTW_DBG_RFK, "[TXGAPK] feature disable!!!\n");
  return;
 }
 rtw8822c_rfk_handshake(rtwdev, true);
 rtw8822c_txgapk(rtwdev);
 rtw8822c_rfk_handshake(rtwdev, false);
}

static void rtw8822c_rf_init(struct rtw_dev *rtwdev)
{
 rtw8822c_rf_dac_cal(rtwdev);
 rtw8822c_rf_x2_check(rtwdev);
 rtw8822c_thermal_trim(rtwdev);
 rtw8822c_power_trim(rtwdev);
 rtw8822c_pa_bias(rtwdev);
}

static void rtw8822c_pwrtrack_init(struct rtw_dev *rtwdev)
{
 struct rtw_dm_info *dm_info = &rtwdev->dm_info;
 u8 path;

 for (path = RF_PATH_A; path < RTW_RF_PATH_MAX; path++) {
  dm_info->delta_power_index[path] = 0;
  ewma_thermal_init(&dm_info->avg_thermal[path]);
  dm_info->thermal_avg[path] = 0xff;
 }

 dm_info->pwr_trk_triggered = false;
 dm_info->thermal_meter_k = rtwdev->efuse.thermal_meter_k;
 dm_info->thermal_meter_lck = rtwdev->efuse.thermal_meter_k;
}

static void rtw8822c_phy_set_param(struct rtw_dev *rtwdev)
{
 struct rtw_dm_info *dm_info = &rtwdev->dm_info;
 struct rtw_hal *hal = &rtwdev->hal;
 u8 crystal_cap;
 u8 cck_gi_u_bnd_msb = 0;
 u8 cck_gi_u_bnd_lsb = 0;
 u8 cck_gi_l_bnd_msb = 0;
 u8 cck_gi_l_bnd_lsb = 0;
 bool is_tx2_path;

 /* power on BB/RF domain */
 rtw_write8_set(rtwdev, REG_SYS_FUNC_EN,
         BIT_FEN_BB_GLB_RST | BIT_FEN_BB_RSTB);
 rtw_write8_set(rtwdev, REG_RF_CTRL,
         BIT_RF_EN | BIT_RF_RSTB | BIT_RF_SDM_RSTB);
 rtw_write32_set(rtwdev, REG_WLRF1, BIT_WLRF1_BBRF_EN);

 /* disable low rate DPD */
 rtw_write32_mask(rtwdev, REG_DIS_DPD, DIS_DPD_MASK, DIS_DPD_RATEALL);

 /* pre init before header files config */
 rtw8822c_header_file_init(rtwdev, true);

 rtw_phy_load_tables(rtwdev);

 crystal_cap = rtwdev->efuse.crystal_cap & 0x7f;
 rtw_write32_mask(rtwdev, REG_ANAPAR_XTAL_0, 0xfffc00,
    crystal_cap | (crystal_cap << 7));

 /* post init after header files config */
 rtw8822c_header_file_init(rtwdev, false);

 is_tx2_path = false;
 rtw8822c_config_trx_mode(rtwdev, hal->antenna_tx, hal->antenna_rx,
     is_tx2_path);
 rtw_phy_init(rtwdev);

 cck_gi_u_bnd_msb = (u8)rtw_read32_mask(rtwdev, 0x1a98, 0xc000);
 cck_gi_u_bnd_lsb = (u8)rtw_read32_mask(rtwdev, 0x1aa8, 0xf0000);
 cck_gi_l_bnd_msb = (u8)rtw_read32_mask(rtwdev, 0x1a98, 0xc0);
 cck_gi_l_bnd_lsb = (u8)rtw_read32_mask(rtwdev, 0x1a70, 0x0f000000);

 dm_info->cck_gi_u_bnd = ((cck_gi_u_bnd_msb << 4) | (cck_gi_u_bnd_lsb));
 dm_info->cck_gi_l_bnd = ((cck_gi_l_bnd_msb << 4) | (cck_gi_l_bnd_lsb));

 rtw8822c_rf_init(rtwdev);
 rtw8822c_pwrtrack_init(rtwdev);

 rtw_bf_phy_init(rtwdev);
}

#define WLAN_TXQ_RPT_EN  0x1F
#define WLAN_SLOT_TIME  0x09
#define WLAN_PIFS_TIME  0x1C
#define WLAN_SIFS_CCK_CONT_TX 0x0A
#define WLAN_SIFS_OFDM_CONT_TX 0x0E
#define WLAN_SIFS_CCK_TRX 0x0A
#define WLAN_SIFS_OFDM_TRX 0x10
#define WLAN_NAV_MAX  0xC8
#define WLAN_RDG_NAV  0x05
#define WLAN_TXOP_NAV  0x1B
#define WLAN_CCK_RX_TSF  0x30
#define WLAN_OFDM_RX_TSF 0x30
#define WLAN_TBTT_PROHIBIT 0x04 /* unit : 32us */
#define WLAN_TBTT_HOLD_TIME 0x064 /* unit : 32us */
#define WLAN_DRV_EARLY_INT 0x04
#define WLAN_BCN_CTRL_CLT0 0x10
#define WLAN_BCN_DMA_TIME 0x02
#define WLAN_BCN_MAX_ERR 0xFF
#define WLAN_SIFS_CCK_DUR_TUNE 0x0A
#define WLAN_SIFS_OFDM_DUR_TUNE 0x10
#define WLAN_SIFS_CCK_CTX 0x0A
#define WLAN_SIFS_CCK_IRX 0x0A
#define WLAN_SIFS_OFDM_CTX 0x0E
#define WLAN_SIFS_OFDM_IRX 0x0E
#define WLAN_EIFS_DUR_TUNE 0x40
#define WLAN_EDCA_VO_PARAM 0x002FA226
#define WLAN_EDCA_VI_PARAM 0x005EA328
#define WLAN_EDCA_BE_PARAM 0x005EA42B
#define WLAN_EDCA_BK_PARAM 0x0000A44F

#define WLAN_RX_FILTER0  0xFFFFFFFF
#define WLAN_RX_FILTER2  0xFFFF
#define WLAN_RCR_CFG  0xE400220E
#define WLAN_RXPKT_MAX_SZ 12288
#define WLAN_RXPKT_MAX_SZ_512 (WLAN_RXPKT_MAX_SZ >> 9)

#define WLAN_AMPDU_MAX_TIME  0x70
#define WLAN_RTS_LEN_TH   0xFF
#define WLAN_RTS_TX_TIME_TH  0x08
#define WLAN_MAX_AGG_PKT_LIMIT  0x3f
#define WLAN_RTS_MAX_AGG_PKT_LIMIT 0x3f
#define WLAN_PRE_TXCNT_TIME_TH  0x1E0
#define FAST_EDCA_VO_TH  0x06
#define FAST_EDCA_VI_TH  0x06
#define FAST_EDCA_BE_TH  0x06
#define FAST_EDCA_BK_TH  0x06
#define WLAN_BAR_RETRY_LIMIT  0x01
#define WLAN_BAR_ACK_TYPE  0x05
#define WLAN_RA_TRY_RATE_AGG_LIMIT 0x08
#define WLAN_RESP_TXRATE  0x84
#define WLAN_ACK_TO   0x21
#define WLAN_ACK_TO_CCK   0x6A
#define WLAN_DATA_RATE_FB_CNT_1_4 0x01000000
#define WLAN_DATA_RATE_FB_CNT_5_8 0x08070504
#define WLAN_RTS_RATE_FB_CNT_5_8 0x08070504
#define WLAN_DATA_RATE_FB_RATE0  0xFE01F010
#define WLAN_DATA_RATE_FB_RATE0_H 0x40000000
#define WLAN_RTS_RATE_FB_RATE1  0x003FF010
#define WLAN_RTS_RATE_FB_RATE1_H 0x40000000
#define WLAN_RTS_RATE_FB_RATE4  0x0600F010
#define WLAN_RTS_RATE_FB_RATE4_H 0x400003E0
#define WLAN_RTS_RATE_FB_RATE5  0x0600F015
#define WLAN_RTS_RATE_FB_RATE5_H 0x000000E0
#define WLAN_MULTI_ADDR   0xFFFFFFFF

#define WLAN_TX_FUNC_CFG1  0x30
#define WLAN_TX_FUNC_CFG2  0x30
#define WLAN_MAC_OPT_NORM_FUNC1  0x98
#define WLAN_MAC_OPT_LB_FUNC1  0x80
#define WLAN_MAC_OPT_FUNC2  0xb0810041
#define WLAN_MAC_INT_MIG_CFG  0x33330000

#define WLAN_SIFS_CFG (WLAN_SIFS_CCK_CONT_TX | \
   (WLAN_SIFS_OFDM_CONT_TX << BIT_SHIFT_SIFS_OFDM_CTX) | \
   (WLAN_SIFS_CCK_TRX << BIT_SHIFT_SIFS_CCK_TRX) | \
   (WLAN_SIFS_OFDM_TRX << BIT_SHIFT_SIFS_OFDM_TRX))

#define WLAN_SIFS_DUR_TUNE (WLAN_SIFS_CCK_DUR_TUNE | \
    (WLAN_SIFS_OFDM_DUR_TUNE << 8))

#define WLAN_TBTT_TIME (WLAN_TBTT_PROHIBIT |\
   (WLAN_TBTT_HOLD_TIME << BIT_SHIFT_TBTT_HOLD_TIME_AP))

#define WLAN_NAV_CFG  (WLAN_RDG_NAV | (WLAN_TXOP_NAV << 16))
#define WLAN_RX_TSF_CFG  (WLAN_CCK_RX_TSF | (WLAN_OFDM_RX_TSF) << 8)

#define MAC_CLK_SPEED 80 /* 80M */
#define EFUSE_PCB_INFO_OFFSET 0xCA

static int rtw8822c_mac_init(struct rtw_dev *rtwdev)
{
 u8 value8;
 u16 value16;
 u32 value32;
 u16 pre_txcnt;

 /* txq control */
 value8 = rtw_read8(rtwdev, REG_FWHW_TXQ_CTRL);
 value8 |= (BIT(7) & ~BIT(1) & ~BIT(2));
 rtw_write8(rtwdev, REG_FWHW_TXQ_CTRL, value8);
 rtw_write8(rtwdev, REG_FWHW_TXQ_CTRL + 1, WLAN_TXQ_RPT_EN);
 /* sifs control */
 rtw_write16(rtwdev, REG_SPEC_SIFS, WLAN_SIFS_DUR_TUNE);
 rtw_write32(rtwdev, REG_SIFS, WLAN_SIFS_CFG);
 rtw_write16(rtwdev, REG_RESP_SIFS_CCK,
      WLAN_SIFS_CCK_CTX | WLAN_SIFS_CCK_IRX << 8);
 rtw_write16(rtwdev, REG_RESP_SIFS_OFDM,
      WLAN_SIFS_OFDM_CTX | WLAN_SIFS_OFDM_IRX << 8);
 /* rate fallback control */
 rtw_write32(rtwdev, REG_DARFRC, WLAN_DATA_RATE_FB_CNT_1_4);
 rtw_write32(rtwdev, REG_DARFRCH, WLAN_DATA_RATE_FB_CNT_5_8);
 rtw_write32(rtwdev, REG_RARFRCH, WLAN_RTS_RATE_FB_CNT_5_8);
 rtw_write32(rtwdev, REG_ARFR0, WLAN_DATA_RATE_FB_RATE0);
 rtw_write32(rtwdev, REG_ARFRH0, WLAN_DATA_RATE_FB_RATE0_H);
 rtw_write32(rtwdev, REG_ARFR1_V1, WLAN_RTS_RATE_FB_RATE1);
 rtw_write32(rtwdev, REG_ARFRH1_V1, WLAN_RTS_RATE_FB_RATE1_H);
 rtw_write32(rtwdev, REG_ARFR4, WLAN_RTS_RATE_FB_RATE4);
 rtw_write32(rtwdev, REG_ARFRH4, WLAN_RTS_RATE_FB_RATE4_H);
 rtw_write32(rtwdev, REG_ARFR5, WLAN_RTS_RATE_FB_RATE5);
 rtw_write32(rtwdev, REG_ARFRH5, WLAN_RTS_RATE_FB_RATE5_H);
 /* protocol configuration */
 rtw_write8(rtwdev, REG_AMPDU_MAX_TIME_V1, WLAN_AMPDU_MAX_TIME);
 rtw_write8_set(rtwdev, REG_TX_HANG_CTRL, BIT_EN_EOF_V1);
 pre_txcnt = WLAN_PRE_TXCNT_TIME_TH | BIT_EN_PRECNT;
 rtw_write8(rtwdev, REG_PRECNT_CTRL, (u8)(pre_txcnt & 0xFF));
 rtw_write8(rtwdev, REG_PRECNT_CTRL + 1, (u8)(pre_txcnt >> 8));
 value32 = WLAN_RTS_LEN_TH | (WLAN_RTS_TX_TIME_TH << 8) |
    (WLAN_MAX_AGG_PKT_LIMIT << 16) |
    (WLAN_RTS_MAX_AGG_PKT_LIMIT << 24);
 rtw_write32(rtwdev, REG_PROT_MODE_CTRL, value32);
 rtw_write16(rtwdev, REG_BAR_MODE_CTRL + 2,
      WLAN_BAR_RETRY_LIMIT | WLAN_RA_TRY_RATE_AGG_LIMIT << 8);
 rtw_write8(rtwdev, REG_FAST_EDCA_VOVI_SETTING, FAST_EDCA_VO_TH);
 rtw_write8(rtwdev, REG_FAST_EDCA_VOVI_SETTING + 2, FAST_EDCA_VI_TH);
 rtw_write8(rtwdev, REG_FAST_EDCA_BEBK_SETTING, FAST_EDCA_BE_TH);
 rtw_write8(rtwdev, REG_FAST_EDCA_BEBK_SETTING + 2, FAST_EDCA_BK_TH);
 /* close BA parser */
 rtw_write8_clr(rtwdev, REG_LIFETIME_EN, BIT_BA_PARSER_EN);
 rtw_write32_clr(rtwdev, REG_RRSR, BITS_RRSR_RSC);

 /* EDCA configuration */
 rtw_write32(rtwdev, REG_EDCA_VO_PARAM, WLAN_EDCA_VO_PARAM);
 rtw_write32(rtwdev, REG_EDCA_VI_PARAM, WLAN_EDCA_VI_PARAM);
 rtw_write32(rtwdev, REG_EDCA_BE_PARAM, WLAN_EDCA_BE_PARAM);
 rtw_write32(rtwdev, REG_EDCA_BK_PARAM, WLAN_EDCA_BK_PARAM);
 rtw_write8(rtwdev, REG_PIFS, WLAN_PIFS_TIME);
 rtw_write8_clr(rtwdev, REG_TX_PTCL_CTRL + 1, BIT_SIFS_BK_EN >> 8);
 rtw_write8_set(rtwdev, REG_RD_CTRL + 1,
         (BIT_DIS_TXOP_CFE | BIT_DIS_LSIG_CFE |
   BIT_DIS_STBC_CFE) >> 8);

 /* MAC clock configuration */
 rtw_write32_clr(rtwdev, REG_AFE_CTRL1, BIT_MAC_CLK_SEL);
 rtw_write8(rtwdev, REG_USTIME_TSF, MAC_CLK_SPEED);
 rtw_write8(rtwdev, REG_USTIME_EDCA, MAC_CLK_SPEED);

 rtw_write8_set(rtwdev, REG_MISC_CTRL,
         BIT_EN_FREE_CNT | BIT_DIS_SECOND_CCA);
 rtw_write8_clr(rtwdev, REG_TIMER0_SRC_SEL, BIT_TSFT_SEL_TIMER0);
 rtw_write16(rtwdev, REG_TXPAUSE, 0x0000);
 rtw_write8(rtwdev, REG_SLOT, WLAN_SLOT_TIME);
 rtw_write32(rtwdev, REG_RD_NAV_NXT, WLAN_NAV_CFG);
 rtw_write16(rtwdev, REG_RXTSF_OFFSET_CCK, WLAN_RX_TSF_CFG);
 /* Set beacon cotnrol - enable TSF and other related functions */
 rtw_write8_set(rtwdev, REG_BCN_CTRL, BIT_EN_BCN_FUNCTION);
 /* Set send beacon related registers */
 rtw_write32(rtwdev, REG_TBTT_PROHIBIT, WLAN_TBTT_TIME);
 rtw_write8(rtwdev, REG_DRVERLYINT, WLAN_DRV_EARLY_INT);
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=98 H=85 G=91

¤ Dauer der Verarbeitung: 0.22 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge