wifi: rtw89: phy: initialize AFE by firmware element table

A power-on sequence table is introduced to initialize AFE (Analogue Front
End) connecting to RF components. Build the sequence in firmware file,
and use a parser to execute the sequences including write/poll/delay
actions.

Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
Link: https://patch.msgid.link/20250915065314.38846-1-pkshih@realtek.com
This commit is contained in:
Ping-Ke Shih 2025-09-15 14:53:14 +08:00
parent e156d2ab36
commit 298f39f0d9
6 changed files with 140 additions and 0 deletions

View File

@ -5417,6 +5417,8 @@ int rtw89_core_start(struct rtw89_dev *rtwdev)
{
int ret;
rtw89_phy_init_bb_afe(rtwdev);
ret = rtw89_mac_init(rtwdev);
if (ret) {
rtw89_err(rtwdev, "mac init fail, ret:%d\n", ret);

View File

@ -4690,6 +4690,7 @@ struct rtw89_fw_elm_info {
struct rtw89_fw_txpwr_track_cfg *txpwr_trk;
struct rtw89_phy_rfk_log_fmt *rfk_log_fmt;
const struct rtw89_regd_data *regd;
const struct rtw89_fw_element_hdr *afe;
};
enum rtw89_fw_mss_dev_type {

View File

@ -1285,6 +1285,18 @@ int rtw89_recognize_regd_from_elm(struct rtw89_dev *rtwdev,
return 0;
}
static
int rtw89_build_afe_pwr_seq_from_elm(struct rtw89_dev *rtwdev,
const struct rtw89_fw_element_hdr *elm,
const union rtw89_fw_element_arg arg)
{
struct rtw89_fw_elm_info *elm_info = &rtwdev->fw.elm_info;
elm_info->afe = elm;
return 0;
}
static const struct rtw89_fw_element_handler __fw_element_handlers[] = {
[RTW89_FW_ELEMENT_ID_BBMCU0] = {__rtw89_fw_recognize_from_elm,
{ .fw_type = RTW89_FW_BBMCU0 }, NULL},
@ -1370,6 +1382,9 @@ static const struct rtw89_fw_element_handler __fw_element_handlers[] = {
[RTW89_FW_ELEMENT_ID_REGD] = {
rtw89_recognize_regd_from_elm, {}, "REGD",
},
[RTW89_FW_ELEMENT_ID_AFE_PWR_SEQ] = {
rtw89_build_afe_pwr_seq_from_elm, {}, "AFE",
},
};
int rtw89_fw_recognize_elements(struct rtw89_dev *rtwdev)

View File

@ -3984,6 +3984,7 @@ enum rtw89_fw_element_id {
RTW89_FW_ELEMENT_ID_TXPWR_DA_LMT_RU_2GHZ = 24,
RTW89_FW_ELEMENT_ID_TXPWR_DA_LMT_RU_5GHZ = 25,
RTW89_FW_ELEMENT_ID_TXPWR_DA_LMT_RU_6GHZ = 26,
RTW89_FW_ELEMENT_ID_AFE_PWR_SEQ = 27,
RTW89_FW_ELEMENT_ID_NUM,
};
@ -4089,6 +4090,30 @@ struct rtw89_fw_txpwr_track_cfg {
BIT(RTW89_FW_TXPWR_TRK_TYPE_2G_CCK_A_N) | \
BIT(RTW89_FW_TXPWR_TRK_TYPE_2G_CCK_A_P))
enum rtw89_fw_afe_action {
RTW89_FW_AFE_ACTION_WRITE = 0,
RTW89_FW_AFE_ACTION_DELAY = 1,
RTW89_FW_AFE_ACTION_POLL = 2,
};
enum rtw89_fw_afe_cat {
RTW89_FW_AFE_CAT_BB = 0,
RTW89_FW_AFE_CAT_BB1 = 1,
RTW89_FW_AFE_CAT_MAC = 2,
RTW89_FW_AFE_CAT_MAC1 = 3,
RTW89_FW_AFE_CAT_AFEDIG = 4,
RTW89_FW_AFE_CAT_AFEDIG1 = 5,
};
enum rtw89_fw_afe_class {
RTW89_FW_AFE_CLASS_P0 = 0,
RTW89_FW_AFE_CLASS_P1 = 1,
RTW89_FW_AFE_CLASS_P2 = 2,
RTW89_FW_AFE_CLASS_P3 = 3,
RTW89_FW_AFE_CLASS_P4 = 4,
RTW89_FW_AFE_CLASS_CMN = 5,
};
struct rtw89_fw_element_hdr {
__le32 id; /* enum rtw89_fw_element_id */
__le32 size; /* exclude header size */
@ -4126,6 +4151,17 @@ struct rtw89_fw_element_hdr {
u8 rsvd1[3];
__le16 offset[];
} __packed rfk_log_fmt;
struct {
u8 rsvd[8];
struct rtw89_phy_afe_info {
__le32 action; /* enum rtw89_fw_afe_action */
__le32 cat; /* enum rtw89_fw_afe_cat */
__le32 class; /* enum rtw89_fw_afe_class */
__le32 addr;
__le32 mask;
__le32 val;
} __packed infos[];
} __packed afe;
struct __rtw89_fw_txpwr_element txpwr;
struct __rtw89_fw_regd_element regd;
} __packed u;

View File

@ -1702,6 +1702,91 @@ void rtw89_phy_init_bb_reg(struct rtw89_dev *rtwdev)
rtw89_phy_bb_reset(rtwdev);
}
void rtw89_phy_init_bb_afe(struct rtw89_dev *rtwdev)
{
struct rtw89_fw_elm_info *elm_info = &rtwdev->fw.elm_info;
const struct rtw89_fw_element_hdr *afe_elm = elm_info->afe;
const struct rtw89_phy_afe_info *info;
u32 action, cat, class;
u32 addr, mask, val;
u32 poll, rpt;
u32 n, i;
if (!afe_elm)
return;
n = le32_to_cpu(afe_elm->size) / sizeof(*info);
for (i = 0; i < n; i++) {
info = &afe_elm->u.afe.infos[i];
class = le32_to_cpu(info->class);
switch (class) {
case RTW89_FW_AFE_CLASS_P0:
case RTW89_FW_AFE_CLASS_P1:
case RTW89_FW_AFE_CLASS_CMN:
/* Currently support two paths */
break;
case RTW89_FW_AFE_CLASS_P2:
case RTW89_FW_AFE_CLASS_P3:
case RTW89_FW_AFE_CLASS_P4:
default:
rtw89_warn(rtwdev, "unexpected AFE class %u\n", class);
continue;
}
addr = le32_to_cpu(info->addr);
mask = le32_to_cpu(info->mask);
val = le32_to_cpu(info->val);
cat = le32_to_cpu(info->cat);
action = le32_to_cpu(info->action);
switch (action) {
case RTW89_FW_AFE_ACTION_WRITE:
switch (cat) {
case RTW89_FW_AFE_CAT_MAC:
case RTW89_FW_AFE_CAT_MAC1:
rtw89_write32_mask(rtwdev, addr, mask, val);
break;
case RTW89_FW_AFE_CAT_AFEDIG:
case RTW89_FW_AFE_CAT_AFEDIG1:
rtw89_write32_mask(rtwdev, addr, mask, val);
break;
case RTW89_FW_AFE_CAT_BB:
rtw89_phy_write32_idx(rtwdev, addr, mask, val, RTW89_PHY_0);
break;
case RTW89_FW_AFE_CAT_BB1:
rtw89_phy_write32_idx(rtwdev, addr, mask, val, RTW89_PHY_1);
break;
default:
rtw89_warn(rtwdev,
"unexpected AFE writing action %u\n", action);
break;
}
break;
case RTW89_FW_AFE_ACTION_POLL:
for (poll = 0; poll <= 10; poll++) {
/*
* For CAT_BB, AFE reads register with mcu_offset 0,
* so both CAT_MAC and CAT_BB use the same method.
*/
rpt = rtw89_read32_mask(rtwdev, addr, mask);
if (rpt == val)
goto poll_done;
fsleep(1);
}
rtw89_warn(rtwdev, "failed to poll AFE cat=%u addr=0x%x mask=0x%x\n",
cat, addr, mask);
poll_done:
break;
case RTW89_FW_AFE_ACTION_DELAY:
fsleep(addr);
break;
}
}
}
static u32 rtw89_phy_nctl_poll(struct rtw89_dev *rtwdev)
{
rtw89_phy_write32(rtwdev, 0x8080, 0x4);

View File

@ -829,6 +829,7 @@ bool rtw89_phy_write_rf_v1(struct rtw89_dev *rtwdev, enum rtw89_rf_path rf_path,
bool rtw89_phy_write_rf_v2(struct rtw89_dev *rtwdev, enum rtw89_rf_path rf_path,
u32 addr, u32 mask, u32 data);
void rtw89_phy_init_bb_reg(struct rtw89_dev *rtwdev);
void rtw89_phy_init_bb_afe(struct rtw89_dev *rtwdev);
void rtw89_phy_init_rf_reg(struct rtw89_dev *rtwdev, bool noio);
void rtw89_phy_config_rf_reg_v1(struct rtw89_dev *rtwdev,
const struct rtw89_reg2_def *reg,