rtw89: add Realtek 802.11ax driver
Ping-Ke Shih
maintainerv6 cover rtw89: add Realtek 802.11ax driver
This driver named rtw89, which is the next generation of rtw88, supports Realtek 8852AE 802.11ax 2x2 chip whose new features are OFDMA, DBCC, Spatial reuse, TWT and BSS coloring; now some of them aren't implemented though.
The chip architecture is entirely different from the chips supported by rtw88 like RTL8822CE 802.11ac chip. First of all, register address ranges are totally redefined, so it's impossible to reuse register definition. To communicate with firmware, new H2C/C2H format is proposed. In order to have better utilization, TX DMA flow is changed to two stages DMA. To provide rich RX status information, additional RX PPDU packets are added.
Since there are so many differences mentioned above, we decide to propose a new driver. It has many authors, they are listed in alphabetic order:
Chin-Yen Lee <[email protected]>
Ping-Ke Shih <[email protected]>
Po Hao Huang <[email protected]>
Tzu-En Huang <[email protected]>
Vincent Fann <[email protected]>
Yan-Hsuan Chuang <[email protected]>
Zong-Zhe Yang <[email protected]>
v6:
- improve BT coexistence performance
- improve power save mode
- improve OFDMA performance
- remove some unused code
- fix can't obtain IPv6 address
- support device ID 10ec:a85a
- refine PCI interrupt flow
- firmware: rtw89: 8852a: update fw to v0.13.28.0
https://github.com/pkshih/linux-firmware.git pick the commit 344db6dca0610341599cd3e5f8b51fcb6ae06fb5
v5:
- add OFDMA feature
- improve BT coexistence performance
- improve power save mode
- support SAR
- fine tune performance
- firmware: rtw89: 8852a: update fw to v0.13.24.0
https://github.com/pkshih/linux-firmware.git pick the commit a5b79c4790da3eb3690e23554225ef8db464f2c6
v4:
- add basic BT coexistence features
- add power save mode, so an new patch (two files) is added
- fine tune performance
- add debugfs for debugging coex, bb, ...
v3:
- fix "networking block comments" reported by checkpatch
- Add MODULE_DEVICE_TABLE() generated by Thomas Backlund <[email protected]>
- Add missed BB settings
- error handle of RX BD and DESC length
- reduce debug level of C2H ACKs
- fix rekey failure due to wrong operator
v2:
- fix compiler warnings made by W=1
- sort header file alphabetically
- fix "networking block comments" reported by checkpatch
Ping-Ke Shih (24):
rtw89: add CAM files
rtw89: add BT coexistence files
rtw89: add core and trx files
rtw89: add debug files
rtw89: add efuse files
rtw89: add files to download and communicate with firmware
rtw89: add MAC files
rtw89: implement mac80211 ops
rtw89: add pci files
rtw89: add phy files
rtw89: define register names
rtw89: add regulatory support
rtw89: 8852a: add 8852a specific files
rtw89: 8852a: add 8852a RFK files
rtw89: 8852a: add 8852a RFK tables
rtw89: 8852a: add 8852a tables (1 of 5)
rtw89: 8852a: add 8852a tables (2 of 5)
rtw89: 8852a: add 8852a tables (3 of 5)
rtw89: 8852a: add 8852a tables (4 of 5)
rtw89: 8852a: add 8852a tables (5 of 5)
rtw89: add ser to recover error reported by firmware
rtw89: add PS files
rtw89: add SAR files
rtw89: add Kconfig and Makefile
drivers/net/wireless/realtek/Kconfig | 1 + drivers/net/wireless/realtek/Makefile | 1 + drivers/net/wireless/realtek/rtw89/Kconfig | 50 + drivers/net/wireless/realtek/rtw89/Makefile | 25 + drivers/net/wireless/realtek/rtw89/cam.c | 700 + drivers/net/wireless/realtek/rtw89/cam.h | 165 + drivers/net/wireless/realtek/rtw89/coex.c | 5743 ++ drivers/net/wireless/realtek/rtw89/coex.h | 179 + drivers/net/wireless/realtek/rtw89/core.c | 2428 + drivers/net/wireless/realtek/rtw89/core.h | 3360 ++ drivers/net/wireless/realtek/rtw89/debug.c | 2404 + drivers/net/wireless/realtek/rtw89/debug.h | 77 + drivers/net/wireless/realtek/rtw89/efuse.c | 188 + drivers/net/wireless/realtek/rtw89/efuse.h | 13 + drivers/net/wireless/realtek/rtw89/fw.c | 1600 + drivers/net/wireless/realtek/rtw89/fw.h | 1394 + drivers/net/wireless/realtek/rtw89/mac.c | 3833 ++ drivers/net/wireless/realtek/rtw89/mac.h | 860 + drivers/net/wireless/realtek/rtw89/mac80211.c | 673 + drivers/net/wireless/realtek/rtw89/pci.c | 3060 + drivers/net/wireless/realtek/rtw89/pci.h | 616 + drivers/net/wireless/realtek/rtw89/phy.c | 2748 + drivers/net/wireless/realtek/rtw89/phy.h | 305 + drivers/net/wireless/realtek/rtw89/ps.c | 154 + drivers/net/wireless/realtek/rtw89/ps.h | 16 + drivers/net/wireless/realtek/rtw89/reg.h | 2135 + drivers/net/wireless/realtek/rtw89/regd.c | 353 + drivers/net/wireless/realtek/rtw89/rtw8852a.c | 2030 + drivers/net/wireless/realtek/rtw89/rtw8852a.h | 109 + .../net/wireless/realtek/rtw89/rtw8852a_rfk.c | 3801 ++ .../net/wireless/realtek/rtw89/rtw8852a_rfk.h | 22 + .../realtek/rtw89/rtw8852a_rfk_table.c | 1596 + .../realtek/rtw89/rtw8852a_rfk_table.h | 132 + .../wireless/realtek/rtw89/rtw8852a_table.c | 48725 ++++++++++++++++ .../wireless/realtek/rtw89/rtw8852a_table.h | 28 + drivers/net/wireless/realtek/rtw89/sar.c | 190 + drivers/net/wireless/realtek/rtw89/sar.h | 26 + drivers/net/wireless/realtek/rtw89/ser.c | 491 + drivers/net/wireless/realtek/rtw89/ser.h | 15 + drivers/net/wireless/realtek/rtw89/txrx.h | 393 + drivers/net/wireless/realtek/rtw89/util.h | 17 + 41 files changed, 90656 insertions(+) create mode 100644 drivers/net/wireless/realtek/rtw89/Kconfig create mode 100644 drivers/net/wireless/realtek/rtw89/Makefile create mode 100644 drivers/net/wireless/realtek/rtw89/cam.c create mode 100644 drivers/net/wireless/realtek/rtw89/cam.h create mode 100644 drivers/net/wireless/realtek/rtw89/coex.c create mode 100644 drivers/net/wireless/realtek/rtw89/coex.h create mode 100644 drivers/net/wireless/realtek/rtw89/core.c create mode 100644 drivers/net/wireless/realtek/rtw89/core.h create mode 100644 drivers/net/wireless/realtek/rtw89/debug.c create mode 100644 drivers/net/wireless/realtek/rtw89/debug.h create mode 100644 drivers/net/wireless/realtek/rtw89/efuse.c create mode 100644 drivers/net/wireless/realtek/rtw89/efuse.h create mode 100644 drivers/net/wireless/realtek/rtw89/fw.c create mode 100644 drivers/net/wireless/realtek/rtw89/fw.h create mode 100644 drivers/net/wireless/realtek/rtw89/mac.c create mode 100644 drivers/net/wireless/realtek/rtw89/mac.h create mode 100644 drivers/net/wireless/realtek/rtw89/mac80211.c create mode 100644 drivers/net/wireless/realtek/rtw89/pci.c create mode 100644 drivers/net/wireless/realtek/rtw89/pci.h create mode 100644 drivers/net/wireless/realtek/rtw89/phy.c create mode 100644 drivers/net/wireless/realtek/rtw89/phy.h create mode 100644 drivers/net/wireless/realtek/rtw89/ps.c create mode 100644 drivers/net/wireless/realtek/rtw89/ps.h create mode 100644 drivers/net/wireless/realtek/rtw89/reg.h create mode 100644 drivers/net/wireless/realtek/rtw89/regd.c create mode 100644 drivers/net/wireless/realtek/rtw89/rtw8852a.c create mode 100644 drivers/net/wireless/realtek/rtw89/rtw8852a.h create mode 100644 drivers/net/wireless/realtek/rtw89/rtw8852a_rfk.c create mode 100644 drivers/net/wireless/realtek/rtw89/rtw8852a_rfk.h create mode 100644 drivers/net/wireless/realtek/rtw89/rtw8852a_rfk_table.c create mode 100644 drivers/net/wireless/realtek/rtw89/rtw8852a_rfk_table.h create mode 100644 drivers/net/wireless/realtek/rtw89/rtw8852a_table.c create mode 100644 drivers/net/wireless/realtek/rtw89/rtw8852a_table.h create mode 100644 drivers/net/wireless/realtek/rtw89/sar.c create mode 100644 drivers/net/wireless/realtek/rtw89/sar.h create mode 100644 drivers/net/wireless/realtek/rtw89/ser.c create mode 100644 drivers/net/wireless/realtek/rtw89/ser.h create mode 100644 drivers/net/wireless/realtek/rtw89/txrx.h create mode 100644 drivers/net/wireless/realtek/rtw89/util.h
Signature · 1 line
2.25.1
Ping-Ke Shih
maintainerPATCH v6 1/24 rtw89: add CAM files
Three kinds of CAM, security, address and bssid, are implemented in the chip, and then we control them via H2C commands.
Every vif contains one address and one bssid CAM entries, and both are allocated while vif is adding. Once any state is changed, driver notices firmware. For example, add/remove vif, sta assoc/disassoc, and changed security key.
When connecting to an AP with security, a security CAM entry is allocated from security CAM pool and filled key values, and then attached to the address CAM that contains seven keys entries for unicast, group and BIP keys. Since the functions of these seven keys are predefined by firmware, we should attach key to proper position depends on security entry mode and key type.
--- drivers/net/wireless/realtek/rtw89/cam.c | 700 +++++++++++++++++++++++ drivers/net/wireless/realtek/rtw89/cam.h | 165 ++++++ 2 files changed, 865 insertions(+) create mode 100644 drivers/net/wireless/realtek/rtw89/cam.c create mode 100644 drivers/net/wireless/realtek/rtw89/cam.h
drivers/net/wireless/realtek/rtw89/cam.c new file +700 -0
@@ -0,0 +1,700 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* Copyright(c) 2019-2020 Realtek Corporation +*/ + +#include "cam.h" +#include "debug.h" +#include "fw.h" +#include "mac.h" + +static struct sk_buff * +rtw89_cam_get_sec_key_cmd (struct rtw89_dev *rtwdev, +struct rtw89_sec_cam_entry *sec_cam, +bool ext_key) +{ +struct sk_buff *skb; +u32 cmd_len =H2C_SEC_CAM_LEN ; +u32 key32[4 ]; +u8 *cmd; +int i, j; + + skb =rtw89_fw_h2c_alloc_skb_with_hdr (cmd_len); +if (!skb) +return NULL ; + +skb_put_zero (skb, cmd_len); + +for (i =0 ; i <4 ; i++) { + j = i *4 ; + j += ext_key ?16 :0 ; + key32[i] =FIELD_PREP (GENMASK (7 ,0 ), sec_cam->key[j +0 ]) | +FIELD_PREP (GENMASK (15 ,8 ), sec_cam->key[j +1 ]) | +FIELD_PREP (GENMASK (23 ,16 ), sec_cam->key[j +2 ]) | +FIELD_PREP (GENMASK (31 ,24 ), sec_cam->key[j +3 ]); + } + + cmd = skb->data; +RTW89_SET_FWCMD_SEC_IDX (cmd, sec_cam->sec_cam_idx + (ext_key ?1 :0 )); +RTW89_SET_FWCMD_SEC_OFFSET (cmd, sec_cam->offset); +RTW89_SET_FWCMD_SEC_LEN (cmd, sec_cam->len); +RTW89_SET_FWCMD_SEC_TYPE (cmd, sec_cam->type); +RTW89_SET_FWCMD_SEC_EXT_KEY (cmd, ext_key); +RTW89_SET_FWCMD_SEC_SPP_MODE (cmd, sec_cam->spp_mode); +RTW89_SET_FWCMD_SEC_KEY0 (cmd, key32[0 ]); +RTW89_SET_FWCMD_SEC_KEY1 (cmd, key32[1 ]); +RTW89_SET_FWCMD_SEC_KEY2 (cmd, key32[2 ]); +RTW89_SET_FWCMD_SEC_KEY3 (cmd, key32[3 ]); + +return skb; +} + +static int rtw89_cam_send_sec_key_cmd (struct rtw89_dev *rtwdev, +struct rtw89_sec_cam_entry *sec_cam) +{ +struct sk_buff *skb, *ext_skb; +int ret; + + skb =rtw89_cam_get_sec_key_cmd (rtwdev, sec_cam, false); +if (!skb) { +rtw89_err (rtwdev,"failed to get sec key command\n" ); +return -ENOMEM ; + } + +rtw89_h2c_pkt_set_hdr (rtwdev, skb, +FWCMD_TYPE_H2C , +H2C_CAT_MAC , +H2C_CL_MAC_SEC_CAM , +H2C_FUNC_MAC_SEC_UPD ,1 ,0 , +H2C_SEC_CAM_LEN ); + ret =rtw89_h2c_tx (rtwdev, skb, false); +if (ret) { +rtw89_err (rtwdev,"failed to send sec key h2c: %d\n" , ret); +dev_kfree_skb (skb); +return ret; + } + +if (!sec_cam->ext_key) +return 0 ; + + ext_skb =rtw89_cam_get_sec_key_cmd (rtwdev, sec_cam, true); +if (!skb) { +rtw89_err (rtwdev,"failed to get ext sec key command\n" ); +return -ENOMEM ; + } + +rtw89_h2c_pkt_set_hdr (rtwdev, ext_skb, +FWCMD_TYPE_H2C , +H2C_CAT_MAC , +H2C_CL_MAC_SEC_CAM , +H2C_FUNC_MAC_SEC_UPD , +1 ,0 ,H2C_SEC_CAM_LEN ); + ret =rtw89_h2c_tx (rtwdev, ext_skb, false); +if (ret) { +rtw89_err (rtwdev,"failed to send ext sec key h2c: %d\n" , ret); +dev_kfree_skb (ext_skb); +return ret; + } + +return 0 ; +} + +static int rtw89_cam_get_avail_sec_cam (struct rtw89_dev *rtwdev, +u8 *sec_cam_idx,bool ext_key) +{ +const struct rtw89_chip_info *chip = rtwdev->chip; +struct rtw89_cam_info *cam_info = &rtwdev->cam_info; +u8 sec_cam_num = chip->scam_num; +u8 idx =0 ; + +if (!ext_key) { + idx =find_first_zero_bit (cam_info->sec_cam_map, sec_cam_num); +if (idx >= sec_cam_num) +return -EBUSY ; + +set_bit (idx, cam_info->sec_cam_map); + *sec_cam_idx = idx; + +return 0 ; + } + +again: + idx =find_next_zero_bit (cam_info->sec_cam_map, sec_cam_num, idx); +if (idx >= sec_cam_num -1 ) +return -EBUSY ; +/* ext keys need two cam entries for 256-bit key */ +if (test_bit (idx +1 , cam_info->sec_cam_map)) { + idx++; + goto again; + } + +set_bit (idx, cam_info->sec_cam_map); +set_bit (idx +1 , cam_info->sec_cam_map); + *sec_cam_idx = idx; + +return 0 ; +} + +static int rtw89_cam_get_addr_cam_key_idx (struct rtw89_addr_cam_entry *addr_cam, +struct rtw89_sec_cam_entry *sec_cam, +struct ieee80211_key_conf *key, +u8 *key_idx) +{ +u8 idx; + +/* RTW89_ADDR_CAM_SEC_NONE : not enabled +* RTW89_ADDR_CAM_SEC_ALL_UNI : 0 - 6 unicast +* RTW89_ADDR_CAM_SEC_NORMAL : 0 - 1 unicast, 2 - 4 group, 5 - 6 BIP +* RTW89_ADDR_CAM_SEC_4GROUP : 0 - 1 unicast, 2 - 5 group, 6 BIP +*/ +switch (addr_cam->sec_ent_mode) { +case RTW89_ADDR_CAM_SEC_NONE : +return -EINVAL ; +case RTW89_ADDR_CAM_SEC_ALL_UNI : +if (!(key->flags &IEEE80211_KEY_FLAG_PAIRWISE )) +return -EINVAL ; + idx =find_first_zero_bit (addr_cam->sec_cam_map, +RTW89_SEC_CAM_IN_ADDR_CAM ); +if (idx >=RTW89_SEC_CAM_IN_ADDR_CAM ) +return -EBUSY ; + *key_idx = idx; +break ; +case RTW89_ADDR_CAM_SEC_NORMAL : +if (sec_cam->type ==RTW89_SEC_KEY_TYPE_BIP_CCMP128 ) { + idx =find_next_zero_bit (addr_cam->sec_cam_map, +RTW89_SEC_CAM_IN_ADDR_CAM ,5 ); +if (idx >6 ) +return -EBUSY ; + *key_idx = idx; +break ; + } + +if (key->flags &IEEE80211_KEY_FLAG_PAIRWISE ) { + idx =find_next_zero_bit (addr_cam->sec_cam_map, +RTW89_SEC_CAM_IN_ADDR_CAM ,0 ); +if (idx >1 ) +return -EBUSY ; + *key_idx = idx; +break ; + } + +/* Group keys */ + idx =find_next_zero_bit (addr_cam->sec_cam_map, +RTW89_SEC_CAM_IN_ADDR_CAM ,2 ); +if (idx >4 ) +return -EBUSY ; + *key_idx = idx; +break ; +case RTW89_ADDR_CAM_SEC_4GROUP : +if (sec_cam->type ==RTW89_SEC_KEY_TYPE_BIP_CCMP128 ) { +if (test_bit (6 , addr_cam->sec_cam_map)) +return -EINVAL ; + *key_idx =6 ; +break ; + } + +if (key->flags &IEEE80211_KEY_FLAG_PAIRWISE ) { + idx =find_next_zero_bit (addr_cam->sec_cam_map, +RTW89_SEC_CAM_IN_ADDR_CAM ,0 ); +if (idx >1 ) +return -EBUSY ; + *key_idx = idx; +break ; + } + +/* Group keys */ + idx =find_next_zero_bit (addr_cam->sec_cam_map, +RTW89_SEC_CAM_IN_ADDR_CAM ,2 ); +if (idx >5 ) +return -EBUSY ; + *key_idx = idx; +break ; + } + +return 0 ; +} + +static int rtw89_cam_attach_sec_cam (struct rtw89_dev *rtwdev, +struct ieee80211_vif *vif, +struct ieee80211_sta *sta, +struct ieee80211_key_conf *key, +struct rtw89_sec_cam_entry *sec_cam) +{ +struct rtw89_vif *rtwvif; +struct rtw89_addr_cam_entry *addr_cam; +u8 key_idx =0 ; +int ret; + +if (!vif) { +rtw89_err (rtwdev,"No iface for adding sec cam\n" ); +return -EINVAL ; + } + + rtwvif = (struct rtw89_vif *)vif->drv_priv; + addr_cam = &rtwvif->addr_cam; + ret =rtw89_cam_get_addr_cam_key_idx (addr_cam, sec_cam, key, &key_idx); +if (ret) { +rtw89_err (rtwdev,"failed to get addr cam key idx %d, %d\n" , + addr_cam->sec_ent_mode, sec_cam->type); +return ret; + } + + key->hw_key_idx = key_idx; + addr_cam->sec_ent_keyid[key_idx] = key->keyidx; + addr_cam->sec_ent[key_idx] = sec_cam->sec_cam_idx; + addr_cam->sec_entries[key_idx] = sec_cam; +set_bit (key_idx, addr_cam->sec_cam_map); + ret =rtw89_fw_h2c_cam (rtwdev, rtwvif); +if (ret) { +rtw89_err (rtwdev,"failed to update addr cam sec entry: %d\n" , + ret); +clear_bit (key_idx, addr_cam->sec_cam_map); + addr_cam->sec_entries[key_idx] =NULL ; +return ret; + } + +return 0 ; +} + +static int rtw89_cam_sec_key_install (struct rtw89_dev *rtwdev, +struct ieee80211_vif *vif, +struct ieee80211_sta *sta, +struct ieee80211_key_conf *key, +u8 hw_key_type,bool ext_key) +{ +struct rtw89_sec_cam_entry *sec_cam =NULL ; +struct rtw89_cam_info *cam_info = &rtwdev->cam_info; +u8 sec_cam_idx; +int ret; + +/* maximum key length 256-bit */ +if (key->keylen >32 ) { +rtw89_err (rtwdev,"invalid sec key length %d\n" , key->keylen); +return -EINVAL ; + } + + ret =rtw89_cam_get_avail_sec_cam (rtwdev, &sec_cam_idx, ext_key); +if (ret) { +rtw89_warn (rtwdev,"no available sec cam: %d ext: %d\n" , + ret, ext_key); +return ret; + } + + sec_cam =kzalloc (sizeof (*sec_cam),GFP_KERNEL ); +if (!sec_cam) { + ret = -ENOMEM ; + goto err_release_cam; + } + + sec_cam->sec_cam_idx = sec_cam_idx; + sec_cam->type = hw_key_type; + sec_cam->len =RTW89_SEC_CAM_LEN ; + sec_cam->ext_key = ext_key; +memcpy (sec_cam->key, key->key, key->keylen); + ret =rtw89_cam_send_sec_key_cmd (rtwdev, sec_cam); +if (ret) { +rtw89_err (rtwdev,"failed to send sec key cmd: %d\n" , ret); + goto err_release_cam; + } + +/* associate with addr cam */ + ret =rtw89_cam_attach_sec_cam (rtwdev, vif, sta, key, sec_cam); +if (ret) { +rtw89_err (rtwdev,"failed to attach sec cam: %d\n" , ret); + goto err_release_cam; + } + +return 0 ; + +err_release_cam: +kfree (sec_cam); +clear_bit (sec_cam_idx, cam_info->sec_cam_map); +if (ext_key) +clear_bit (sec_cam_idx +1 , cam_info->sec_cam_map); + +return ret; +} + +int rtw89_cam_sec_key_add (struct rtw89_dev *rtwdev, +struct ieee80211_vif *vif, +struct ieee80211_sta *sta, +struct ieee80211_key_conf *key) +{ +u8 hw_key_type; +bool ext_key = false; +int ret; + +switch (key->cipher) { +case WLAN_CIPHER_SUITE_WEP40 : + hw_key_type =RTW89_SEC_KEY_TYPE_WEP40 ; +break ; +case WLAN_CIPHER_SUITE_WEP104 : + hw_key_type =RTW89_SEC_KEY_TYPE_WEP104 ; +break ; +case WLAN_CIPHER_SUITE_CCMP : + hw_key_type =RTW89_SEC_KEY_TYPE_CCMP128 ; + key->flags |=IEEE80211_KEY_FLAG_SW_MGMT_TX ; +break ; +case WLAN_CIPHER_SUITE_CCMP_256 : + hw_key_type =RTW89_SEC_KEY_TYPE_CCMP256 ; + key->flags |=IEEE80211_KEY_FLAG_SW_MGMT_TX ; + ext_key = true; +break ; +case WLAN_CIPHER_SUITE_GCMP : + hw_key_type =RTW89_SEC_KEY_TYPE_GCMP128 ; + key->flags |=IEEE80211_KEY_FLAG_SW_MGMT_TX ; +break ; +case WLAN_CIPHER_SUITE_GCMP_256 : + hw_key_type =RTW89_SEC_KEY_TYPE_GCMP256 ; + key->flags |=IEEE80211_KEY_FLAG_SW_MGMT_TX ; + ext_key = true; +break ; +case WLAN_CIPHER_SUITE_TKIP : +case WLAN_CIPHER_SUITE_AES_CMAC : +case WLAN_CIPHER_SUITE_BIP_CMAC_256 : +case WLAN_CIPHER_SUITE_BIP_GMAC_128 : +case WLAN_CIPHER_SUITE_BIP_GMAC_256 : +/* suppress error messages */ +return -EOPNOTSUPP ; +default : +return -ENOTSUPP ; + } + + key->flags |=IEEE80211_KEY_FLAG_GENERATE_IV ; + + ret =rtw89_cam_sec_key_install (rtwdev, vif, sta, key, hw_key_type, + ext_key); +if (ret) { +rtw89_err (rtwdev,"failed to install key type %d ext %d: %d\n" , + hw_key_type, ext_key, ret); +return ret; + } + +return 0 ; +} + +int rtw89_cam_sec_key_del (struct rtw89_dev *rtwdev, +struct ieee80211_vif *vif, +struct ieee80211_sta *sta, +struct ieee80211_key_conf *key, +bool inform_fw) +{ +struct rtw89_cam_info *cam_info = &rtwdev->cam_info; +struct rtw89_vif *rtwvif; +struct rtw89_addr_cam_entry *addr_cam; +struct rtw89_sec_cam_entry *sec_cam; +u8 key_idx = key->hw_key_idx; +u8 sec_cam_idx; +int ret =0 ; + +if (!vif) { +rtw89_err (rtwdev,"No iface for deleting sec cam\n" ); +return -EINVAL ; + } + + rtwvif = (struct rtw89_vif *)vif->drv_priv; + addr_cam = &rtwvif->addr_cam; + sec_cam = addr_cam->sec_entries[key_idx]; + +/* detach sec cam from addr cam */ +clear_bit (key_idx, addr_cam->sec_cam_map); + addr_cam->sec_entries[key_idx] =NULL ; +if (inform_fw) { + ret =rtw89_fw_h2c_cam (rtwdev, rtwvif); +if (ret) +rtw89_err (rtwdev,"failed to update cam del key: %d\n" , ret); + } + +/* clear valid bit in addr cam will disable sec cam, +* so we don't need to send H2C command again +*/ + sec_cam_idx = sec_cam->sec_cam_idx; +clear_bit (sec_cam_idx, cam_info->sec_cam_map); +if (sec_cam->ext_key) +clear_bit (sec_cam_idx +1 , cam_info->sec_cam_map); + +kfree (sec_cam); + +return ret; +} + +static void rtw89_cam_reset_key_iter (struct ieee80211_hw *hw, +struct ieee80211_vif *vif, +struct ieee80211_sta *sta, +struct ieee80211_key_conf *key, +void *data) +{ +struct rtw89_dev *rtwdev = (struct rtw89_dev *)data; +struct rtw89_vif *rtwvif = (struct rtw89_vif *)vif->drv_priv; + +rtw89_cam_sec_key_del (rtwdev, vif, sta, key, false); +rtw89_cam_deinit (rtwdev, rtwvif); +} + +void rtw89_cam_deinit (struct rtw89_dev *rtwdev,struct rtw89_vif *rtwvif) +{ +struct rtw89_cam_info *cam_info = &rtwdev->cam_info; +struct rtw89_addr_cam_entry *addr_cam = &rtwvif->addr_cam; +struct rtw89_bssid_cam_entry *bssid_cam = &rtwvif->bssid_cam; + + addr_cam->valid = false; + bssid_cam->valid = false; +clear_bit (addr_cam->addr_cam_idx, cam_info->addr_cam_map); +clear_bit (bssid_cam->bssid_cam_idx, cam_info->bssid_cam_map); +} + +void rtw89_cam_reset_keys (struct rtw89_dev *rtwdev) +{ +rcu_read_lock (); +ieee80211_iter_keys_rcu (rtwdev->hw,NULL , rtw89_cam_reset_key_iter, rtwdev); +rcu_read_unlock (); +} + +static int rtw89_cam_get_avail_addr_cam (struct rtw89_dev *rtwdev, +u8 *addr_cam_idx) +{ +const struct rtw89_chip_info *chip = rtwdev->chip; +struct rtw89_cam_info *cam_info = &rtwdev->cam_info; +u8 addr_cam_num = chip->acam_num; +u8 idx; + + idx =find_first_zero_bit (cam_info->addr_cam_map, addr_cam_num); +if (idx >= addr_cam_num) +return -EBUSY ; + +set_bit (idx, cam_info->addr_cam_map); + *addr_cam_idx = idx; + +return 0 ; +} + +static int rtw89_cam_init_addr_cam (struct rtw89_dev *rtwdev, +struct rtw89_vif *rtwvif) +{ +struct rtw89_addr_cam_entry *addr_cam = &rtwvif->addr_cam; +u8 addr_cam_idx; +int i; +int ret; + + ret =rtw89_cam_get_avail_addr_cam (rtwdev, &addr_cam_idx); +if (ret) { +rtw89_err (rtwdev,"failed to get available addr cam\n" ); +return ret; + } + + addr_cam->addr_cam_idx = addr_cam_idx; + addr_cam->len =ADDR_CAM_ENT_SIZE ; + addr_cam->offset =0 ; + addr_cam->valid = true; + addr_cam->addr_mask =0 ; + addr_cam->mask_sel =RTW89_NO_MSK ; +bitmap_zero (addr_cam->sec_cam_map,RTW89_SEC_CAM_IN_ADDR_CAM ); +ether_addr_copy (addr_cam->sma, rtwvif->mac_addr); + +for (i =0 ; i <RTW89_SEC_CAM_IN_ADDR_CAM ; i++) { + addr_cam->sec_ent_keyid[i] =0 ; + addr_cam->sec_ent[i] =0 ; + } + +return 0 ; +} + +static int rtw89_cam_get_avail_bssid_cam (struct rtw89_dev *rtwdev, +u8 *bssid_cam_idx) +{ +const struct rtw89_chip_info *chip = rtwdev->chip; +struct rtw89_cam_info *cam_info = &rtwdev->cam_info; +u8 bssid_cam_num = chip->bcam_num; +u8 idx; + + idx =find_first_zero_bit (cam_info->bssid_cam_map, bssid_cam_num); +if (idx >= bssid_cam_num) +return -EBUSY ; + +set_bit (idx, cam_info->bssid_cam_map); + *bssid_cam_idx = idx; + +return 0 ; +} + +static int rtw89_cam_init_bssid_cam (struct rtw89_dev *rtwdev, +struct rtw89_vif *rtwvif) +{ +struct rtw89_bssid_cam_entry *bssid_cam = &rtwvif->bssid_cam; +u8 bssid_cam_idx; +int ret; + + ret =rtw89_cam_get_avail_bssid_cam (rtwdev, &bssid_cam_idx); +if (ret) { +rtw89_err (rtwdev,"failed to get available bssid cam\n" ); +return ret; + } + + bssid_cam->bssid_cam_idx = bssid_cam_idx; + bssid_cam->phy_idx = rtwvif->phy_idx; + bssid_cam->len =BSSID_CAM_ENT_SIZE ; + bssid_cam->offset =0 ; + bssid_cam->valid = true; +ether_addr_copy (bssid_cam->bssid, rtwvif->bssid); + +return 0 ; +} + +void rtw89_cam_bssid_changed (struct rtw89_dev *rtwdev,struct rtw89_vif *rtwvif) +{ +struct ieee80211_vif *vif =rtwvif_to_vif (rtwvif); +struct rtw89_addr_cam_entry *addr_cam = &rtwvif->addr_cam; +struct rtw89_bssid_cam_entry *bssid_cam = &rtwvif->bssid_cam; + +if (vif->type ==NL80211_IFTYPE_STATION ) +ether_addr_copy (addr_cam->tma, rtwvif->bssid); +ether_addr_copy (bssid_cam->bssid, rtwvif->bssid); +} + +int rtw89_cam_init (struct rtw89_dev *rtwdev,struct rtw89_vif *rtwvif) +{ +struct rtw89_addr_cam_entry *addr_cam = &rtwvif->addr_cam; +struct rtw89_bssid_cam_entry *bssid_cam = &rtwvif->bssid_cam; +int ret; + + ret =rtw89_cam_init_addr_cam (rtwdev, rtwvif); +if (ret) { +rtw89_err (rtwdev,"failed to init addr cam\n" ); +return ret; + } + + ret =rtw89_cam_init_bssid_cam (rtwdev, rtwvif); +if (ret) { +rtw89_err (rtwdev,"failed to init bssid cam\n" ); +return ret; + } + +/* associate addr cam with bssid cam */ + addr_cam->bssid_cam_idx = bssid_cam->bssid_cam_idx; + +return 0 ; +} + +int rtw89_cam_fill_bssid_cam_info (struct rtw89_dev *rtwdev, +struct rtw89_vif *rtwvif,u8 *cmd) +{ +struct ieee80211_vif *vif =rtwvif_to_vif (rtwvif); +struct rtw89_bssid_cam_entry *bssid_cam = &rtwvif->bssid_cam; +u8 bss_color = vif->bss_conf.he_bss_color.color; + +FWCMD_SET_ADDR_BSSID_IDX (cmd, bssid_cam->bssid_cam_idx); +FWCMD_SET_ADDR_BSSID_OFFSET (cmd, bssid_cam->offset); +FWCMD_SET_ADDR_BSSID_LEN (cmd, bssid_cam->len); +FWCMD_SET_ADDR_BSSID_VALID (cmd, bssid_cam->valid); +FWCMD_SET_ADDR_BSSID_BB_SEL (cmd, bssid_cam->phy_idx); +FWCMD_SET_ADDR_BSSID_BSS_COLOR (cmd, bss_color); + +FWCMD_SET_ADDR_BSSID_BSSID0 (cmd, bssid_cam->bssid[0 ]); +FWCMD_SET_ADDR_BSSID_BSSID1 (cmd, bssid_cam->bssid[1 ]); +FWCMD_SET_ADDR_BSSID_BSSID2 (cmd, bssid_cam->bssid[2 ]); +FWCMD_SET_ADDR_BSSID_BSSID3 (cmd, bssid_cam->bssid[3 ]); +FWCMD_SET_ADDR_BSSID_BSSID4 (cmd, bssid_cam->bssid[4 ]); +FWCMD_SET_ADDR_BSSID_BSSID5 (cmd, bssid_cam->bssid[5 ]); + +return 0 ; +} + +static u8 rtw89_cam_addr_hash (u8 start,u8 *addr) +{ +u8 hash =0 ; +u8 i; + +for (i = start; i <ETH_ALEN ; i++) + hash ^= addr[i]; + +return hash; +} + +void rtw89_cam_fill_addr_cam_info (struct rtw89_dev *rtwdev, +struct rtw89_vif *rtwvif, +u8 *cmd) +{ +struct ieee80211_vif *vif =rtwvif_to_vif (rtwvif); +struct ieee80211_sta *sta; +struct rtw89_sta *rtwsta; +struct rtw89_addr_cam_entry *addr_cam = &rtwvif->addr_cam; +u8 sma_hash, tma_hash, addr_msk_start; +u8 sma_start =0 ; +u8 tma_start =0 ; + +if (addr_cam->addr_mask !=0 ) { + addr_msk_start =__ffs (addr_cam->addr_mask); +if (addr_cam->mask_sel ==RTW89_SMA ) + sma_start = addr_msk_start; +else if (addr_cam->mask_sel ==RTW89_TMA ) + tma_start = addr_msk_start; + } + sma_hash =rtw89_cam_addr_hash (sma_start, rtwvif->mac_addr); + tma_hash =rtw89_cam_addr_hash (tma_start, addr_cam->tma); + +FWCMD_SET_ADDR_IDX (cmd, addr_cam->addr_cam_idx); +FWCMD_SET_ADDR_OFFSET (cmd, addr_cam->offset); +FWCMD_SET_ADDR_LEN (cmd, addr_cam->len); + +FWCMD_SET_ADDR_VALID (cmd, addr_cam->valid); +FWCMD_SET_ADDR_NET_TYPE (cmd, rtwvif->net_type); +FWCMD_SET_ADDR_BCN_HIT_COND (cmd, rtwvif->bcn_hit_cond); +FWCMD_SET_ADDR_HIT_RULE (cmd, rtwvif->hit_rule); +FWCMD_SET_ADDR_BB_SEL (cmd, rtwvif->phy_idx); +FWCMD_SET_ADDR_ADDR_MASK (cmd, addr_cam->addr_mask); +FWCMD_SET_ADDR_MASK_SEL (cmd, addr_cam->mask_sel); +FWCMD_SET_ADDR_SMA_HASH (cmd, sma_hash); +FWCMD_SET_ADDR_TMA_HASH (cmd, tma_hash); + +FWCMD_SET_ADDR_BSSID_CAM_IDX (cmd, addr_cam->bssid_cam_idx); + +FWCMD_SET_ADDR_SMA0 (cmd, rtwvif->mac_addr[0 ]); +FWCMD_SET_ADDR_SMA1 (cmd, rtwvif->mac_addr[1 ]); +FWCMD_SET_ADDR_SMA2 (cmd, rtwvif->mac_addr[2 ]); +FWCMD_SET_ADDR_SMA3 (cmd, rtwvif->mac_addr[3 ]); +FWCMD_SET_ADDR_SMA4 (cmd, rtwvif->mac_addr[4 ]); +FWCMD_SET_ADDR_SMA5 (cmd, rtwvif->mac_addr[5 ]); + +FWCMD_SET_ADDR_TMA0 (cmd, addr_cam->tma[0 ]); +FWCMD_SET_ADDR_TMA1 (cmd, addr_cam->tma[1 ]); +FWCMD_SET_ADDR_TMA2 (cmd, addr_cam->tma[2 ]); +FWCMD_SET_ADDR_TMA3 (cmd, addr_cam->tma[3 ]); +FWCMD_SET_ADDR_TMA4 (cmd, addr_cam->tma[4 ]); +FWCMD_SET_ADDR_TMA5 (cmd, addr_cam->tma[5 ]); + +FWCMD_SET_ADDR_PORT_INT (cmd, rtwvif->port); +FWCMD_SET_ADDR_TSF_SYNC (cmd, rtwvif->port); +FWCMD_SET_ADDR_TF_TRS (cmd, rtwvif->trigger); +FWCMD_SET_ADDR_LSIG_TXOP (cmd, rtwvif->lsig_txop); +FWCMD_SET_ADDR_TGT_IND (cmd, rtwvif->tgt_ind); +FWCMD_SET_ADDR_FRM_TGT_IND (cmd, rtwvif->frm_tgt_ind); + +if (vif->type ==NL80211_IFTYPE_STATION ) { + sta = rtwvif->mgd.ap; +if (sta) { + rtwsta = (struct rtw89_sta *)sta->drv_priv; +FWCMD_SET_ADDR_MACID (cmd, rtwsta->mac_id); +FWCMD_SET_ADDR_AID12 (cmd, vif->bss_conf.aid &0xfff ); + } + } +FWCMD_SET_ADDR_WOL_PATTERN (cmd, rtwvif->wowlan_pattern); +FWCMD_SET_ADDR_WOL_UC (cmd, rtwvif->wowlan_uc); +FWCMD_SET_ADDR_WOL_MAGIC (cmd, rtwvif->wowlan_magic); +FWCMD_SET_ADDR_WAPI (cmd, addr_cam->wapi); +FWCMD_SET_ADDR_SEC_ENT_MODE (cmd, addr_cam->sec_ent_mode); +FWCMD_SET_ADDR_SEC_ENT0_KEYID (cmd, addr_cam->sec_ent_keyid[0 ]); +FWCMD_SET_ADDR_SEC_ENT1_KEYID (cmd, addr_cam->sec_ent_keyid[1 ]); +FWCMD_SET_ADDR_SEC_ENT2_KEYID (cmd, addr_cam->sec_ent_keyid[2 ]); +FWCMD_SET_ADDR_SEC_ENT3_KEYID (cmd, addr_cam->sec_ent_keyid[3 ]); +FWCMD_SET_ADDR_SEC_ENT4_KEYID (cmd, addr_cam->sec_ent_keyid[4 ]); +FWCMD_SET_ADDR_SEC_ENT5_KEYID (cmd, addr_cam->sec_ent_keyid[5 ]); +FWCMD_SET_ADDR_SEC_ENT6_KEYID (cmd, addr_cam->sec_ent_keyid[6 ]); + +FWCMD_SET_ADDR_SEC_ENT_VALID (cmd, addr_cam->sec_cam_map[0 ] &0xff ); +FWCMD_SET_ADDR_SEC_ENT0 (cmd, addr_cam->sec_ent[0 ]); +FWCMD_SET_ADDR_SEC_ENT1 (cmd, addr_cam->sec_ent[1 ]); +FWCMD_SET_ADDR_SEC_ENT2 (cmd, addr_cam->sec_ent[2 ]); +FWCMD_SET_ADDR_SEC_ENT3 (cmd, addr_cam->sec_ent[3 ]); +FWCMD_SET_ADDR_SEC_ENT4 (cmd, addr_cam->sec_ent[4 ]); +FWCMD_SET_ADDR_SEC_ENT5 (cmd, addr_cam->sec_ent[5 ]); +FWCMD_SET_ADDR_SEC_ENT6 (cmd, addr_cam->sec_ent[6 ]); +}
drivers/net/wireless/realtek/rtw89/cam.h new file +165 -0
165 lines not on this page · raw message
Signature · 1 line
2.25.1
Re: [PATCH v6 01/24] rtw89: add CAM files
Quoted 16 lines · Ping-Ke Shih
Three kinds of CAM, security, address and bssid, are implemented in the chip, and then we control them via H2C commands.
Every vif contains one address and one bssid CAM entries, and both are allocated while vif is adding. Once any state is changed, driver notices firmware. For example, add/remove vif, sta assoc/disassoc, and changed security key.
When connecting to an AP with security, a security CAM entry is allocated from security CAM pool and filled key values, and then attached to the address CAM that contains seven keys entries for unicast, group and BIP keys. Since the functions of these seven keys are predefined by firmware, we should attach key to proper position depends on security entry mode and key type.
Signed-off-by Ping-Ke Shih <[email protected]>
[...]
Quoted 30 lines
drivers/net/wireless/realtek/rtw89/cam.c new file +30
-0+int rtw89_cam_sec_key_add (struct rtw89_dev *rtwdev, +struct ieee80211_vif *vif, +struct ieee80211_sta *sta, +struct ieee80211_key_conf *key) +{ +u8 hw_key_type; +bool ext_key = false; +int ret; + +switch (key->cipher) { +case WLAN_CIPHER_SUITE_WEP40 : + hw_key_type =RTW89_SEC_KEY_TYPE_WEP40 ; +break ; +case WLAN_CIPHER_SUITE_WEP104 : + hw_key_type =RTW89_SEC_KEY_TYPE_WEP104 ; +break ; +case WLAN_CIPHER_SUITE_CCMP : + hw_key_type =RTW89_SEC_KEY_TYPE_CCMP128 ; + key->flags |=IEEE80211_KEY_FLAG_SW_MGMT_TX ; +break ; +case WLAN_CIPHER_SUITE_CCMP_256 : + hw_key_type =RTW89_SEC_KEY_TYPE_CCMP256 ; + key->flags |=IEEE80211_KEY_FLAG_SW_MGMT_TX ; + ext_key = true; +break ; +case WLAN_CIPHER_SUITE_GCMP : + hw_key_type =RTW89_SEC_KEY_TYPE_GCMP128 ; + key->flags |=IEEE80211_KEY_FLAG_SW_MGMT_TX ; +break ; +case WLAN_CIPHER_SUITE_GCMP_256 :
drivers/net/wireless/realtek/rtw89/cam.c line 356
Quoted 10 lines
drivers/net/wireless/realtek/rtw89/cam.c new file +10
-0+ hw_key_type =RTW89_SEC_KEY_TYPE_GCMP256 ; + key->flags |=IEEE80211_KEY_FLAG_SW_MGMT_TX ; + ext_key = true; +break ; +case WLAN_CIPHER_SUITE_TKIP : +case WLAN_CIPHER_SUITE_AES_CMAC : +case WLAN_CIPHER_SUITE_BIP_CMAC_256 : +case WLAN_CIPHER_SUITE_BIP_GMAC_128 : +case WLAN_CIPHER_SUITE_BIP_GMAC_256 : +/* suppress error messages */
What error messages? I don't understand the comment.
drivers/net/wireless/realtek/rtw89/cam.c line 359
Quoted 3 lines
drivers/net/wireless/realtek/rtw89/cam.c new file +3
-0+return -EOPNOTSUPP ; +default : +return -ENOTSUPP ;
I would merge default with other cases above and have just one return call.
Signature · 3 lines
https://patchwork.kernel.org/project/linux-wireless/list/ https://wireless.wiki.kernel.org/en/developers/documentation/submittingpatches
Ping-Ke Shih
maintainerPATCH v6 2/24 rtw89: add BT coexistence files
In reply to Ping-Ke ShihBT coexistence uses TDMA-based mechanism to coordinate with WiFi and BT. Now, we implement basic coexistence features for wide use cases, such as HID and A2DP. More will be supported later.
--- drivers/net/wireless/realtek/rtw89/coex.c | 5743 +++++++++++++++++++++ drivers/net/wireless/realtek/rtw89/coex.h | 179 + 2 files changed, 5922 insertions(+) create mode 100644 drivers/net/wireless/realtek/rtw89/coex.c create mode 100644 drivers/net/wireless/realtek/rtw89/coex.h
drivers/net/wireless/realtek/rtw89/coex.c new file +5743 -0
@@ -0,0 +1,5743 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* Copyright(c) 2019-2020 Realtek Corporation +*/ + +#include "coex.h" +#include "debug.h" +#include "fw.h" +#include "mac.h" +#include "ps.h" +#include "reg.h" + +#define FCXDEF_STEP 50/* MUST <= FCXMAX_STEP and match with wl fw*/ + +enum btc_fbtc_tdma_template { +CXTD_OFF =0x0 , +CXTD_OFF_B2 , +CXTD_OFF_EXT , +CXTD_FIX , +CXTD_PFIX , +CXTD_AUTO , +CXTD_PAUTO , +CXTD_AUTO2 , +CXTD_PAUTO2 , +CXTD_MAX , +}; + +enum btc_fbtc_tdma_type { +CXTDMA_OFF =0x0 , +CXTDMA_FIX =0x1 , +CXTDMA_AUTO =0x2 , +CXTDMA_AUTO2 =0x3 , +CXTDMA_MAX +}; + +enum btc_fbtc_tdma_rx_flow_ctrl { +CXFLC_OFF =0x0 , +CXFLC_NULLP =0x1 , +CXFLC_QOSNULL =0x2 , +CXFLC_CTS =0x3 , +CXFLC_MAX +}; + +enum btc_fbtc_tdma_wlan_tx_pause { +CXTPS_OFF =0x0 ,/* no wl tx pause*/ +CXTPS_ON =0x1 , +CXTPS_MAX +}; + +enum btc_mlme_state { +MLME_NO_LINK , +MLME_LINKING , +MLME_LINKED , +}; + +#define FCXONESLOT_VER 1 +struct btc_fbtc_1slot { +u8 fver; +u8 sid;/* slot id */ +struct rtw89_btc_fbtc_slot slot; +} __packed; + +static const struct rtw89_btc_fbtc_tdma t_def[] = { + [CXTD_OFF ] = {CXTDMA_OFF ,CXFLC_OFF ,CXTPS_OFF ,0 ,0 ,0 ,0 ,0 }, + [CXTD_OFF_B2 ] = {CXTDMA_OFF ,CXFLC_OFF ,CXTPS_OFF ,0 ,0 ,1 ,0 ,0 }, + [CXTD_OFF_EXT ] = {CXTDMA_OFF ,CXFLC_OFF ,CXTPS_OFF ,0 ,0 ,3 ,0 ,0 }, + [CXTD_FIX ] = {CXTDMA_FIX ,CXFLC_OFF ,CXTPS_OFF ,0 ,0 ,0 ,0 ,0 }, + [CXTD_PFIX ] = {CXTDMA_FIX ,CXFLC_NULLP ,CXTPS_ON ,0 ,5 ,0 ,0 ,0 }, + [CXTD_AUTO ] = {CXTDMA_AUTO ,CXFLC_OFF ,CXTPS_OFF ,0 ,0 ,0 ,0 ,0 }, + [CXTD_PAUTO ] = {CXTDMA_AUTO ,CXFLC_NULLP ,CXTPS_ON ,0 ,5 ,0 ,0 ,0 }, + [CXTD_AUTO2 ] = {CXTDMA_AUTO2 ,CXFLC_OFF ,CXTPS_OFF ,0 ,0 ,0 ,0 ,0 }, + [CXTD_PAUTO2 ] = {CXTDMA_AUTO2 ,CXFLC_NULLP ,CXTPS_ON ,0 ,5 ,0 ,0 ,0 } +}; + +#define __DEF_FBTC_SLOT (__dur, __cxtbl, __cxtype) \ + { .dur = cpu_to_le16(__dur), .cxtbl = cpu_to_le32(__cxtbl), \ + .cxtype = cpu_to_le16(__cxtype),} + +static const struct rtw89_btc_fbtc_slot s_def[] = { + [CXST_OFF ] =__DEF_FBTC_SLOT (100 ,0x55555555 ,SLOT_MIX ), + [CXST_B2W ] =__DEF_FBTC_SLOT (5 ,0x5a5a5a5a ,SLOT_ISO ), + [CXST_W1 ] =__DEF_FBTC_SLOT (70 ,0x5a5a5a5a ,SLOT_ISO ), + [CXST_W2 ] =__DEF_FBTC_SLOT (70 ,0x5a5a5aaa ,SLOT_ISO ), + [CXST_W2B ] =__DEF_FBTC_SLOT (15 ,0x5a5a5a5a ,SLOT_ISO ), + [CXST_B1 ] =__DEF_FBTC_SLOT (100 ,0x55555555 ,SLOT_MIX ), + [CXST_B2 ] =__DEF_FBTC_SLOT (7 ,0x6a5a5a5a ,SLOT_MIX ), + [CXST_B3 ] =__DEF_FBTC_SLOT (5 ,0x55555555 ,SLOT_MIX ), + [CXST_B4 ] =__DEF_FBTC_SLOT (50 ,0x55555555 ,SLOT_MIX ), + [CXST_LK ] =__DEF_FBTC_SLOT (20 ,0x5a5a5a5a ,SLOT_ISO ), + [CXST_BLK ] =__DEF_FBTC_SLOT (250 ,0x55555555 ,SLOT_MIX ), + [CXST_E2G ] =__DEF_FBTC_SLOT (20 ,0x6a5a5a5a ,SLOT_MIX ), + [CXST_E5G ] =__DEF_FBTC_SLOT (20 ,0xffffffff ,SLOT_MIX ), + [CXST_EBT ] =__DEF_FBTC_SLOT (20 ,0x55555555 ,SLOT_MIX ), + [CXST_ENULL ] =__DEF_FBTC_SLOT (7 ,0xaaaaaaaa ,SLOT_ISO ), + [CXST_WLK ] =__DEF_FBTC_SLOT (250 ,0x6a5a6a5a ,SLOT_MIX ), + [CXST_W1FDD ] =__DEF_FBTC_SLOT (35 ,0xfafafafa ,SLOT_ISO ), + [CXST_B1FDD ] =__DEF_FBTC_SLOT (100 ,0xffffffff ,SLOT_MIX ), +}; + +static const u32 cxtbl[] = { +0xffffffff ,/* 0 */ +0xaaaaaaaa ,/* 1 */ +0x55555555 ,/* 2 */ +0x66555555 ,/* 3 */ +0x66556655 ,/* 4 */ +0x5a5a5a5a ,/* 5 */ +0x5a5a5aaa ,/* 6 */ +0xaa5a5a5a ,/* 7 */ +0x6a5a5a5a ,/* 8 */ +0x6a5a5aaa ,/* 9 */ +0x6a5a6a5a ,/* 10 */ +0x6a5a6aaa ,/* 11 */ +0x6afa5afa ,/* 12 */ +0xaaaa5aaa ,/* 13 */ +0xaaffffaa ,/* 14 */ +0xaa5555aa ,/* 15 */ +0xfafafafa ,/* 16 */ +0xffffddff ,/* 17 */ +0xdaffdaff ,/* 18 */ +0xfafadafa /* 19 */ +}; + +struct rtw89_btc_btf_tlv { +u8 type; +u8 len; +u8 val[1 ]; +} __packed; + +enum btc_btf_set_report_en { +RPT_EN_TDMA =BIT (0 ), +RPT_EN_CYCLE =BIT (1 ), +RPT_EN_MREG =BIT (2 ), +RPT_EN_BT_VER_INFO =BIT (3 ), +RPT_EN_BT_SCAN_INFO =BIT (4 ), +RPT_EN_BT_AFH_MAP =BIT (5 ), +RPT_EN_BT_DEVICE_INFO =BIT (6 ), +RPT_EN_WL_ALL =GENMASK (2 ,0 ), +RPT_EN_BT_ALL =GENMASK (6 ,3 ), +RPT_EN_ALL =GENMASK (6 ,0 ), +}; + +#define BTF_SET_REPORT_VER 1 +struct rtw89_btc_btf_set_report { +u8 fver; +__le32 enable; +__le32 para; +} __packed; + +#define BTF_SET_SLOT_TABLE_VER 1 +struct rtw89_btc_btf_set_slot_table { +u8 fver; +u8 tbl_num; +u8 buf[]; +} __packed; + +#define BTF_SET_MON_REG_VER 1 +struct rtw89_btc_btf_set_mon_reg { +u8 fver; +u8 reg_num; +u8 buf[]; +} __packed; + +enum btc_btf_set_cx_policy { +CXPOLICY_TDMA =0x0 , +CXPOLICY_SLOT =0x1 , +CXPOLICY_TYPE =0x2 , +CXPOLICY_MAX , +}; + +enum btc_b2w_scoreboard { +BTC_BSCB_ACT =BIT (0 ), +BTC_BSCB_ON =BIT (1 ), +BTC_BSCB_WHQL =BIT (2 ), +BTC_BSCB_BT_S1 =BIT (3 ), +BTC_BSCB_A2DP_ACT =BIT (4 ), +BTC_BSCB_RFK_RUN =BIT (5 ), +BTC_BSCB_RFK_REQ =BIT (6 ), +BTC_BSCB_LPS =BIT (7 ), +BTC_BSCB_WLRFK =BIT (11 ), +BTC_BSCB_BT_HILNA =BIT (13 ), +BTC_BSCB_BT_CONNECT =BIT (16 ), +BTC_BSCB_PATCH_CODE =BIT (30 ), +BTC_BSCB_ALL =GENMASK (30 ,0 ), +}; + +enum btc_phymap { +BTC_PHY_0 =BIT (0 ), +BTC_PHY_1 =BIT (1 ), +BTC_PHY_ALL =BIT (0 ) |BIT (1 ), +}; + +enum btc_cx_state_map { +BTC_WIDLE =0 , +BTC_WBUSY_BNOSCAN , +BTC_WBUSY_BSCAN , +BTC_WSCAN_BNOSCAN , +BTC_WSCAN_BSCAN , +BTC_WLINKING +}; + +enum btc_ant_phase { +BTC_ANT_WPOWERON =0 , +BTC_ANT_WINIT , +BTC_ANT_WONLY , +BTC_ANT_WOFF , +BTC_ANT_W2G , +BTC_ANT_W5G , +BTC_ANT_W25G , +BTC_ANT_FREERUN , +BTC_ANT_WRFK , +BTC_ANT_BRFK , +BTC_ANT_MAX +}; + +enum btc_plt { +BTC_PLT_NONE =0 , +BTC_PLT_LTE_RX =BIT (0 ), +BTC_PLT_GNT_BT_TX =BIT (1 ), +BTC_PLT_GNT_BT_RX =BIT (2 ), +BTC_PLT_GNT_WL =BIT (3 ), +BTC_PLT_BT =BIT (1 ) |BIT (2 ), +BTC_PLT_ALL =0xf +}; + +enum btc_cx_poicy_main_type { +BTC_CXP_OFF =0 , +BTC_CXP_OFFB , +BTC_CXP_OFFE , +BTC_CXP_FIX , +BTC_CXP_PFIX , +BTC_CXP_AUTO , +BTC_CXP_PAUTO , +BTC_CXP_AUTO2 , +BTC_CXP_PAUTO2 , +BTC_CXP_MANUAL , +BTC_CXP_USERDEF0 , +BTC_CXP_MAIN_MAX +}; + +enum btc_cx_poicy_type { +/* TDMA off + pri: BT > WL */ +BTC_CXP_OFF_BT = (BTC_CXP_OFF <<8 ) |0 , + +/* TDMA off + pri: WL > BT */ +BTC_CXP_OFF_WL = (BTC_CXP_OFF <<8 ) |1 , + +/* TDMA off + pri: BT = WL */ +BTC_CXP_OFF_EQ0 = (BTC_CXP_OFF <<8 ) |2 , + +/* TDMA off + pri: BT = WL > BT_Lo */ +BTC_CXP_OFF_EQ1 = (BTC_CXP_OFF <<8 ) |3 , + +/* TDMA off + pri: WL = BT, BT_Rx > WL_Lo_Tx */ +BTC_CXP_OFF_EQ2 = (BTC_CXP_OFF <<8 ) |4 , + +/* TDMA off + pri: WL_Rx = BT, BT_HI > WL_Tx > BT_Lo */ +BTC_CXP_OFF_EQ3 = (BTC_CXP_OFF <<8 ) |5 , + +/* TDMA off + pri: BT_Hi > WL > BT_Lo */ +BTC_CXP_OFF_BWB0 = (BTC_CXP_OFF <<8 ) |6 , + +/* TDMA off + pri: WL_Hi-Tx > BT_Hi_Rx, BT_Hi > WL > BT_Lo */ +BTC_CXP_OFF_BWB1 = (BTC_CXP_OFF <<8 ) |7 , + +/* TDMA off+Bcn-Protect + pri: WL_Hi-Tx > BT_Hi_Rx, BT_Hi > WL > BT_Lo*/ +BTC_CXP_OFFB_BWB0 = (BTC_CXP_OFFB <<8 ) |0 , + +/* TDMA off + Ext-Ctrl + pri: default */ +BTC_CXP_OFFE_DEF = (BTC_CXP_OFFE <<8 ) |0 , + +/* TDMA off + Ext-Ctrl + pri: E2G-slot block all BT */ +BTC_CXP_OFFE_DEF2 = (BTC_CXP_OFFE <<8 ) |1 , + +/* TDMA Fix slot-0: W1:B1 = 30:30 */ +BTC_CXP_FIX_TD3030 = (BTC_CXP_FIX <<8 ) |0 , + +/* TDMA Fix slot-1: W1:B1 = 50:50 */ +BTC_CXP_FIX_TD5050 = (BTC_CXP_FIX <<8 ) |1 , + +/* TDMA Fix slot-2: W1:B1 = 20:30 */ +BTC_CXP_FIX_TD2030 = (BTC_CXP_FIX <<8 ) |2 , + +/* TDMA Fix slot-3: W1:B1 = 40:10 */ +BTC_CXP_FIX_TD4010 = (BTC_CXP_FIX <<8 ) |3 , + +/* TDMA Fix slot-4: W1:B1 = 70:10 */ +BTC_CXP_FIX_TD7010 = (BTC_CXP_FIX <<8 ) |4 , + +/* TDMA Fix slot-5: W1:B1 = 20:60 */ +BTC_CXP_FIX_TD2060 = (BTC_CXP_FIX <<8 ) |5 , + +/* TDMA Fix slot-6: W1:B1 = 30:60 */ +BTC_CXP_FIX_TD3060 = (BTC_CXP_FIX <<8 ) |6 , + +/* TDMA Fix slot-7: W1:B1 = 20:80 */ +BTC_CXP_FIX_TD2080 = (BTC_CXP_FIX <<8 ) |7 , + +/* TDMA Fix slot-8: W1:B1 = user-define */ +BTC_CXP_FIX_TDW1B1 = (BTC_CXP_FIX <<8 ) |8 , + +/* TDMA Fix slot-9: W1:B1 = 40:20 */ +BTC_CXP_FIX_TD4020 = (BTC_CXP_FIX <<8 ) |9 , + +/* PS-TDMA Fix slot-0: W1:B1 = 30:30 */ +BTC_CXP_PFIX_TD3030 = (BTC_CXP_PFIX <<8 ) |0 , + +/* PS-TDMA Fix slot-1: W1:B1 = 50:50 */ +BTC_CXP_PFIX_TD5050 = (BTC_CXP_PFIX <<8 ) |1 , + +/* PS-TDMA Fix slot-2: W1:B1 = 20:30 */ +BTC_CXP_PFIX_TD2030 = (BTC_CXP_PFIX <<8 ) |2 , + +/* PS-TDMA Fix slot-3: W1:B1 = 20:60 */ +BTC_CXP_PFIX_TD2060 = (BTC_CXP_PFIX <<8 ) |3 , + +/* PS-TDMA Fix slot-4: W1:B1 = 30:70 */ +BTC_CXP_PFIX_TD3070 = (BTC_CXP_PFIX <<8 ) |4 , + +/* PS-TDMA Fix slot-5: W1:B1 = 20:80 */ +BTC_CXP_PFIX_TD2080 = (BTC_CXP_PFIX <<8 ) |5 , + +/* PS-TDMA Fix slot-6: W1:B1 = user-define */ +BTC_CXP_PFIX_TDW1B1 = (BTC_CXP_PFIX <<8 ) |6 , + +/* TDMA Auto slot-0: W1:B1 = 50:200 */ +BTC_CXP_AUTO_TD50200 = (BTC_CXP_AUTO <<8 ) |0 , + +/* TDMA Auto slot-1: W1:B1 = 60:200 */ +BTC_CXP_AUTO_TD60200 = (BTC_CXP_AUTO <<8 ) |1 , + +/* TDMA Auto slot-2: W1:B1 = 20:200 */ +BTC_CXP_AUTO_TD20200 = (BTC_CXP_AUTO <<8 ) |2 , + +/* TDMA Auto slot-3: W1:B1 = user-define */ +BTC_CXP_AUTO_TDW1B1 = (BTC_CXP_AUTO <<8 ) |3 , + +/* PS-TDMA Auto slot-0: W1:B1 = 50:200 */ +BTC_CXP_PAUTO_TD50200 = (BTC_CXP_PAUTO <<8 ) |0 , + +/* PS-TDMA Auto slot-1: W1:B1 = 60:200 */ +BTC_CXP_PAUTO_TD60200 = (BTC_CXP_PAUTO <<8 ) |1 , + +/* PS-TDMA Auto slot-2: W1:B1 = 20:200 */ +BTC_CXP_PAUTO_TD20200 = (BTC_CXP_PAUTO <<8 ) |2 , + +/* PS-TDMA Auto slot-3: W1:B1 = user-define */ +BTC_CXP_PAUTO_TDW1B1 = (BTC_CXP_PAUTO <<8 ) |3 , + +/* TDMA Auto slot2-0: W1:B4 = 30:50 */ +BTC_CXP_AUTO2_TD3050 = (BTC_CXP_AUTO2 <<8 ) |0 , + +/* TDMA Auto slot2-1: W1:B4 = 30:70 */ +BTC_CXP_AUTO2_TD3070 = (BTC_CXP_AUTO2 <<8 ) |1 , + +/* TDMA Auto slot2-2: W1:B4 = 50:50 */ +BTC_CXP_AUTO2_TD5050 = (BTC_CXP_AUTO2 <<8 ) |2 , + +/* TDMA Auto slot2-3: W1:B4 = 60:60 */ +BTC_CXP_AUTO2_TD6060 = (BTC_CXP_AUTO2 <<8 ) |3 , + +/* TDMA Auto slot2-4: W1:B4 = 20:80 */ +BTC_CXP_AUTO2_TD2080 = (BTC_CXP_AUTO2 <<8 ) |4 , + +/* TDMA Auto slot2-5: W1:B4 = user-define */ +BTC_CXP_AUTO2_TDW1B4 = (BTC_CXP_AUTO2 <<8 ) |5 , + +/* PS-TDMA Auto slot2-0: W1:B4 = 30:50 */ +BTC_CXP_PAUTO2_TD3050 = (BTC_CXP_PAUTO2 <<8 ) |0 , + +/* PS-TDMA Auto slot2-1: W1:B4 = 30:70 */ +BTC_CXP_PAUTO2_TD3070 = (BTC_CXP_PAUTO2 <<8 ) |1 , + +/* PS-TDMA Auto slot2-2: W1:B4 = 50:50 */ +BTC_CXP_PAUTO2_TD5050 = (BTC_CXP_PAUTO2 <<8 ) |2 , + +/* PS-TDMA Auto slot2-3: W1:B4 = 60:60 */ +BTC_CXP_PAUTO2_TD6060 = (BTC_CXP_PAUTO2 <<8 ) |3 , + +/* PS-TDMA Auto slot2-4: W1:B4 = 20:80 */ +BTC_CXP_PAUTO2_TD2080 = (BTC_CXP_PAUTO2 <<8 ) |4 , + +/* PS-TDMA Auto slot2-5: W1:B4 = user-define */ +BTC_CXP_PAUTO2_TDW1B4 = (BTC_CXP_PAUTO2 <<8 ) |5 , + +BTC_CXP_MAX =0xffff +}; + +enum btc_wl_rfk_result { +BTC_WRFK_REJECT =0 , +BTC_WRFK_ALLOW =1 , +}; + +enum btc_coex_info_map_en { +BTC_COEX_INFO_CX =BIT (0 ), +BTC_COEX_INFO_WL =BIT (1 ), +BTC_COEX_INFO_BT =BIT (2 ), +BTC_COEX_INFO_DM =BIT (3 ), +BTC_COEX_INFO_MREG =BIT (4 ), +BTC_COEX_INFO_SUMMARY =BIT (5 ), +BTC_COEX_INFO_ALL =GENMASK (7 ,0 ), +}; + +#define BTC_CXP_MASK GENMASK(15, 8) + +enum btc_w2b_scoreboard { +BTC_WSCB_ACTIVE =BIT (0 ), +BTC_WSCB_ON =BIT (1 ), +BTC_WSCB_SCAN =BIT (2 ), +BTC_WSCB_UNDERTEST =BIT (3 ), +BTC_WSCB_RXGAIN =BIT (4 ), +BTC_WSCB_WLBUSY =BIT (7 ), +BTC_WSCB_EXTFEM =BIT (8 ), +BTC_WSCB_TDMA =BIT (9 ), +BTC_WSCB_FIX2M =BIT (10 ), +BTC_WSCB_WLRFK =BIT (11 ), +BTC_WSCB_BTRFK_GNT =BIT (12 ),/* not used, use mailbox to inform BT */ +BTC_WSCB_BT_HILNA =BIT (13 ), +BTC_WSCB_BTLOG =BIT (14 ), +BTC_WSCB_ALL =GENMASK (23 ,0 ), +}; + +enum btc_wl_link_mode { +BTC_WLINK_NOLINK =0x0 , +BTC_WLINK_2G_STA , +BTC_WLINK_2G_AP , +BTC_WLINK_2G_GO , +BTC_WLINK_2G_GC , +BTC_WLINK_2G_SCC , +BTC_WLINK_2G_MCC , +BTC_WLINK_25G_MCC , +BTC_WLINK_25G_DBCC , +BTC_WLINK_5G , +BTC_WLINK_2G_NAN , +BTC_WLINK_OTHER , +BTC_WLINK_MAX +}; + +enum btc_bt_hid_type { +BTC_HID_218 =BIT (0 ), +BTC_HID_418 =BIT (1 ), +BTC_HID_BLE =BIT (2 ), +BTC_HID_RCU =BIT (3 ), +BTC_HID_RCU_VOICE =BIT (4 ), +BTC_HID_OTHER_LEGACY =BIT (5 ) +}; + +enum btc_reset_module { +BTC_RESET_CX =BIT (0 ), +BTC_RESET_DM =BIT (1 ), +BTC_RESET_CTRL =BIT (2 ), +BTC_RESET_CXDM =BIT (0 ) |BIT (1 ), +BTC_RESET_BTINFO =BIT (3 ), +BTC_RESET_MDINFO =BIT (4 ), +BTC_RESET_ALL =GENMASK (7 ,0 ), +}; + +enum btc_gnt_state { +BTC_GNT_HW =0 , +BTC_GNT_SW_LO , +BTC_GNT_SW_HI , +BTC_GNT_MAX +}; + +enum btc_wl_max_tx_time { +BTC_MAX_TX_TIME_L1 =500 , +BTC_MAX_TX_TIME_L2 =1000 , +BTC_MAX_TX_TIME_L3 =2000 , +BTC_MAX_TX_TIME_DEF =5280 +}; + +enum btc_wl_max_tx_retry { +BTC_MAX_TX_RETRY_L1 =7 , +BTC_MAX_TX_RETRY_L2 =15 , +BTC_MAX_TX_RETRY_DEF =31 , +}; + +enum btc_reason_and_action { +BTC_RSN_NONE , +BTC_RSN_NTFY_INIT , +BTC_RSN_NTFY_SWBAND , +BTC_RSN_NTFY_WL_STA , +BTC_RSN_NTFY_RADIO_STATE , +BTC_RSN_UPDATE_BT_SCBD , +BTC_RSN_NTFY_WL_RFK , +BTC_RSN_UPDATE_BT_INFO , +BTC_RSN_NTFY_SCAN_START , +BTC_RSN_NTFY_SCAN_FINISH , +BTC_RSN_NTFY_SPECIFIC_PACKET , +BTC_RSN_NTFY_POWEROFF , +BTC_RSN_NTFY_ROLE_INFO , +BTC_RSN_CMD_SET_COEX , +BTC_RSN_ACT1_WORK , +BTC_RSN_BT_DEVINFO_WORK , +BTC_RSN_RFK_CHK_WORK , +BTC_RSN_NUM , +BTC_ACT_NONE =100 , +BTC_ACT_WL_ONLY , +BTC_ACT_WL_5G , +BTC_ACT_WL_OTHER , +BTC_ACT_WL_IDLE , +BTC_ACT_WL_NC , +BTC_ACT_WL_RFK , +BTC_ACT_WL_INIT , +BTC_ACT_WL_OFF , +BTC_ACT_FREERUN , +BTC_ACT_BT_WHQL , +BTC_ACT_BT_RFK , +BTC_ACT_BT_OFF , +BTC_ACT_BT_IDLE , +BTC_ACT_BT_HFP , +BTC_ACT_BT_HID , +BTC_ACT_BT_A2DP , +BTC_ACT_BT_A2DPSINK , +BTC_ACT_BT_PAN , +BTC_ACT_BT_A2DP_HID , +BTC_ACT_BT_A2DP_PAN , +BTC_ACT_BT_PAN_HID , +BTC_ACT_BT_A2DP_PAN_HID , +BTC_ACT_WL_25G_MCC , +BTC_ACT_WL_2G_MCC , +BTC_ACT_WL_2G_SCC , +BTC_ACT_WL_2G_AP , +BTC_ACT_WL_2G_GO , +BTC_ACT_WL_2G_GC , +BTC_ACT_WL_2G_NAN , +BTC_ACT_LAST , +BTC_ACT_NUM =BTC_ACT_LAST -BTC_ACT_NONE , +BTC_ACT_EXT_BIT =BIT (14 ), +BTC_POLICY_EXT_BIT =BIT (15 ), +}; + +#define BTC_FREERUN_ANTISO_MIN 30 +#define BTC_TDMA_BTHID_MAX 2 +#define BTC_BLINK_NOCONNECT 0 + +static void _run_coex (struct rtw89_dev *rtwdev, +enum btc_reason_and_action reason); +static void _write_scbd (struct rtw89_dev *rtwdev,u32 val,bool state); +static void _update_bt_scbd (struct rtw89_dev *rtwdev,bool only_update); + +static void _send_fw_cmd (struct rtw89_dev *rtwdev,u8 h2c_class,u8 h2c_func, +void *param,u16 len) +{ +rtw89_fw_h2c_raw_with_hdr (rtwdev, h2c_class, h2c_func, param, len, + false, true); +} + +static void _reset_btc_var (struct rtw89_dev *rtwdev,u8 type) +{ +struct rtw89_btc *btc = &rtwdev->btc; +struct rtw89_btc_cx *cx = &btc->cx; +struct rtw89_btc_wl_info *wl = &btc->cx.wl; +struct rtw89_btc_bt_info *bt = &btc->cx.bt; +struct rtw89_btc_bt_link_info *bt_linfo = &bt->link_info; +struct rtw89_btc_wl_link_info *wl_linfo = wl->link_info; +u8 i; + +rtw89_debug (rtwdev,RTW89_DBG_BTC ,"[BTC], %s\n" , __func__); + +if (type &BTC_RESET_CX ) +memset (cx,0 ,sizeof (*cx)); +else if (type &BTC_RESET_BTINFO )/* only for BT enable */ +memset (bt,0 ,sizeof (*bt)); + +if (type &BTC_RESET_CTRL ) { +memset (&btc->ctrl,0 ,sizeof (btc->ctrl)); + btc->ctrl.trace_step =FCXDEF_STEP ; + } + +/* Init Coex variables that are not zero */ +if (type &BTC_RESET_DM ) { +memset (&btc->dm,0 ,sizeof (btc->dm)); +memset (bt_linfo->rssi_state,0 ,sizeof (bt_linfo->rssi_state)); + +for (i =0 ; i <RTW89_MAX_HW_PORT_NUM ; i++) +memset (wl_linfo[i].rssi_state,0 , +sizeof (wl_linfo[i].rssi_state)); + +/* set the slot_now table to original */ + btc->dm.tdma_now = t_def[CXTD_OFF ]; + btc->dm.tdma = t_def[CXTD_OFF ]; +memcpy (&btc->dm.slot_now, s_def,sizeof (btc->dm.slot_now)); +memcpy (&btc->dm.slot, s_def,sizeof (btc->dm.slot)); + + btc->policy_len =0 ; + btc->bt_req_len =0 ; + + btc->dm.coex_info_map =BTC_COEX_INFO_ALL ; + btc->dm.wl_tx_limit.tx_time =BTC_MAX_TX_TIME_DEF ; + btc->dm.wl_tx_limit.tx_retry =BTC_MAX_TX_RETRY_DEF ; + } + +if (type &BTC_RESET_MDINFO ) +memset (&btc->mdinfo,0 ,sizeof (btc->mdinfo)); +} + +#define BTC_FWINFO_BUF 1024 + +#define BTC_RPT_HDR_SIZE 3 +#define BTC_CHK_WLSLOT_DRIFT_MAX 15 +#define BTC_CHK_HANG_MAX 3 + +static void _chk_btc_err (struct rtw89_dev *rtwdev,u8 type,u32 cnt) +{ +struct rtw89_btc *btc = &rtwdev->btc; +struct rtw89_btc_cx *cx = &btc->cx; +struct rtw89_btc_dm *dm = &btc->dm; +struct rtw89_btc_bt_info *bt = &cx->bt; + +rtw89_debug (rtwdev,RTW89_DBG_BTC , +"[BTC], %s(): type:%d cnt:%d\n" , + __func__, type, cnt); + +switch (type) { +case BTC_DCNT_RPT_FREEZE : +if (dm->cnt_dm[BTC_DCNT_RPT ] == cnt && btc->fwinfo.rpt_en_map) + dm->cnt_dm[BTC_DCNT_RPT_FREEZE ]++; +else + dm->cnt_dm[BTC_DCNT_RPT_FREEZE ] =0 ; + +if (dm->cnt_dm[BTC_DCNT_RPT_FREEZE ] >=BTC_CHK_HANG_MAX ) + dm->error.map.wl_fw_hang = true; +else + dm->error.map.wl_fw_hang = false; + + dm->cnt_dm[BTC_DCNT_RPT ] = cnt; +break ; +case BTC_DCNT_CYCLE_FREEZE : +if (dm->cnt_dm[BTC_DCNT_CYCLE ] == cnt && + (dm->tdma_now.type !=CXTDMA_OFF || + dm->tdma_now.ext_ctrl ==CXECTL_EXT )) + dm->cnt_dm[BTC_DCNT_CYCLE_FREEZE ]++; +else + dm->cnt_dm[BTC_DCNT_CYCLE_FREEZE ] =0 ; + +if (dm->cnt_dm[BTC_DCNT_CYCLE_FREEZE ] >=BTC_CHK_HANG_MAX ) + dm->error.map.cycle_hang = true; +else + dm->error.map.cycle_hang = false; + + dm->cnt_dm[BTC_DCNT_CYCLE ] = cnt; +break ; +case BTC_DCNT_W1_FREEZE : +if (dm->cnt_dm[BTC_DCNT_W1 ] == cnt && + dm->tdma_now.type !=CXTDMA_OFF ) + dm->cnt_dm[BTC_DCNT_W1_FREEZE ]++; +else + dm->cnt_dm[BTC_DCNT_W1_FREEZE ] =0 ; + +if (dm->cnt_dm[BTC_DCNT_W1_FREEZE ] >=BTC_CHK_HANG_MAX ) + dm->error.map.w1_hang = true; +else + dm->error.map.w1_hang = false; + + dm->cnt_dm[BTC_DCNT_W1 ] = cnt; +break ; +case BTC_DCNT_B1_FREEZE : +if (dm->cnt_dm[BTC_DCNT_B1 ] == cnt && + dm->tdma_now.type !=CXTDMA_OFF ) + dm->cnt_dm[BTC_DCNT_B1_FREEZE ]++; +else + dm->cnt_dm[BTC_DCNT_B1_FREEZE ] =0 ; + +if (dm->cnt_dm[BTC_DCNT_B1_FREEZE ] >=BTC_CHK_HANG_MAX ) + dm->error.map.b1_hang = true; +else + dm->error.map.b1_hang = false; + + dm->cnt_dm[BTC_DCNT_B1 ] = cnt; +break ; +case BTC_DCNT_TDMA_NONSYNC : +if (cnt !=0 )/* if tdma not sync between drv/fw */ + dm->cnt_dm[BTC_DCNT_TDMA_NONSYNC ]++; +else + dm->cnt_dm[BTC_DCNT_TDMA_NONSYNC ] =0 ; + +if (dm->cnt_dm[BTC_DCNT_TDMA_NONSYNC ] >=BTC_CHK_HANG_MAX ) + dm->error.map.tdma_no_sync = true; +else + dm->error.map.tdma_no_sync = false; +break ; +case BTC_DCNT_SLOT_NONSYNC : +if (cnt !=0 )/* if slot not sync between drv/fw */ + dm->cnt_dm[BTC_DCNT_SLOT_NONSYNC ]++; +else + dm->cnt_dm[BTC_DCNT_SLOT_NONSYNC ] =0 ; + +if (dm->cnt_dm[BTC_DCNT_SLOT_NONSYNC ] >=BTC_CHK_HANG_MAX ) + dm->error.map.tdma_no_sync = true; +else + dm->error.map.tdma_no_sync = false; +break ; +case BTC_DCNT_BTCNT_FREEZE : + cnt = cx->cnt_bt[BTC_BCNT_HIPRI_RX ] + + cx->cnt_bt[BTC_BCNT_HIPRI_TX ] + + cx->cnt_bt[BTC_BCNT_LOPRI_RX ] + + cx->cnt_bt[BTC_BCNT_LOPRI_TX ]; + +if (cnt ==0 ) + dm->cnt_dm[BTC_DCNT_BTCNT_FREEZE ]++; +else + dm->cnt_dm[BTC_DCNT_BTCNT_FREEZE ] =0 ; + +if ((dm->cnt_dm[BTC_DCNT_BTCNT_FREEZE ] >=BTC_CHK_HANG_MAX && + bt->enable.now) || (!dm->cnt_dm[BTC_DCNT_BTCNT_FREEZE ] && + !bt->enable.now)) +_update_bt_scbd (rtwdev, false); +break ; +case BTC_DCNT_WL_SLOT_DRIFT : +if (cnt >=BTC_CHK_WLSLOT_DRIFT_MAX ) + dm->cnt_dm[BTC_DCNT_WL_SLOT_DRIFT ]++; +else + dm->cnt_dm[BTC_DCNT_WL_SLOT_DRIFT ] =0 ; + +if (dm->cnt_dm[BTC_DCNT_WL_SLOT_DRIFT ] >=BTC_CHK_HANG_MAX ) + dm->error.map.wl_slot_drift = true; +else + dm->error.map.wl_slot_drift = false; +break ; + } +} + +static void _update_bt_report (struct rtw89_dev *rtwdev,u8 rpt_type,u8 *pfinfo) +{ +struct rtw89_btc *btc = &rtwdev->btc; +struct rtw89_btc_bt_info *bt = &btc->cx.bt; +struct rtw89_btc_bt_link_info *bt_linfo = &bt->link_info; +struct rtw89_btc_bt_a2dp_desc *a2dp = &bt_linfo->a2dp_desc; +struct rtw89_btc_fbtc_btver *pver =NULL ; +struct rtw89_btc_fbtc_btscan *pscan =NULL ; +struct rtw89_btc_fbtc_btafh *pafh =NULL ; +struct rtw89_btc_fbtc_btdevinfo *pdev =NULL ; + + pver = (struct rtw89_btc_fbtc_btver *)pfinfo; + pscan = (struct rtw89_btc_fbtc_btscan *)pfinfo; + pafh = (struct rtw89_btc_fbtc_btafh *)pfinfo; + pdev = (struct rtw89_btc_fbtc_btdevinfo *)pfinfo; + +rtw89_debug (rtwdev,RTW89_DBG_BTC , +"[BTC], %s(): rpt_type:%d\n" , + __func__, rpt_type); + +switch (rpt_type) { +case BTC_RPT_TYPE_BT_VER : + bt->ver_info.fw =le32_to_cpu (pver->fw_ver); + bt->ver_info.fw_coex =le32_get_bits (pver->coex_ver,GENMASK (7 ,0 )); + bt->feature =le32_to_cpu (pver->feature); +break ; +case BTC_RPT_TYPE_BT_SCAN : +memcpy (bt->scan_info, pscan->scan,BTC_SCAN_MAX1 ); +break ; +case BTC_RPT_TYPE_BT_AFH : +memcpy (&bt_linfo->afh_map[0 ], pafh->afh_l,4 ); +memcpy (&bt_linfo->afh_map[4 ], pafh->afh_m,4 ); +memcpy (&bt_linfo->afh_map[8 ], pafh->afh_h,2 ); +break ; +case BTC_RPT_TYPE_BT_DEVICE : + a2dp->device_name =le32_to_cpu (pdev->dev_name); + a2dp->vendor_id =le16_to_cpu (pdev->vendor_id); + a2dp->flush_time =le32_to_cpu (pdev->flush_time); +break ; +default : +break ; + } +} + +struct rtw89_btc_fbtc_cysta_cpu { +u8 fver; +u8 rsvd; +u16 cycles; +u16 cycles_a2dp[CXT_FLCTRL_MAX ]; +u16 a2dpept; +u16 a2dpeptto; +u16 tavg_cycle[CXT_MAX ]; +u16 tmax_cycle[CXT_MAX ]; +u16 tmaxdiff_cycle[CXT_MAX ]; +u16 tavg_a2dp[CXT_FLCTRL_MAX ]; +u16 tmax_a2dp[CXT_FLCTRL_MAX ]; +u16 tavg_a2dpept; +u16 tmax_a2dpept; +u16 tavg_lk; +u16 tmax_lk; +u32 slot_cnt[CXST_MAX ]; +u32 bcn_cnt[CXBCN_MAX ]; +u32 leakrx_cnt; +u32 collision_cnt; +u32 skip_cnt; +u32 exception; +u32 except_cnt; +#if (FCXCYSTA_VER >1 ) +u16 tslot_cycle[BTC_CYCLE_SLOT_MAX ]; +#endif +}; + +static void rtw89_btc_fbtc_cysta_to_cpu (const struct rtw89_btc_fbtc_cysta *src, +struct rtw89_btc_fbtc_cysta_cpu *dst) +{ +static_assert (sizeof (*src) ==sizeof (*dst)); + +#define __CPY_U8 (_x) ({dst->_x = src->_x; }) +#define __CPY_LE16 (_x) ({dst->_x = le16_to_cpu(src->_x); }) +#define __CPY_LE16S (_x) ({int _i; for (_i = 0; _i < ARRAY_SIZE(dst->_x); _i++) \ + dst->_x[_i] = le16_to_cpu(src->_x[_i]); }) +#define __CPY_LE32 (_x) ({dst->_x = le32_to_cpu(src->_x); }) +#define __CPY_LE32S (_x) ({int _i; for (_i = 0; _i < ARRAY_SIZE(dst->_x); _i++) \ + dst->_x[_i] = le32_to_cpu(src->_x[_i]); }) + +__CPY_U8 (fver); +__CPY_U8 (rsvd); +__CPY_LE16 (cycles); +__CPY_LE16S (cycles_a2dp); +__CPY_LE16 (a2dpept); +__CPY_LE16 (a2dpeptto); +__CPY_LE16S (tavg_cycle); +__CPY_LE16S (tmax_cycle); +__CPY_LE16S (tmaxdiff_cycle); +__CPY_LE16S (tavg_a2dp); +__CPY_LE16S (tmax_a2dp); +__CPY_LE16 (tavg_a2dpept); +__CPY_LE16 (tmax_a2dpept); +__CPY_LE16 (tavg_lk); +__CPY_LE16 (tmax_lk); +__CPY_LE32S (slot_cnt); +__CPY_LE32S (bcn_cnt); +__CPY_LE32 (leakrx_cnt); +__CPY_LE32 (collision_cnt); +__CPY_LE32 (skip_cnt); +__CPY_LE32 (exception); +__CPY_LE32 (except_cnt); +#if (FCXCYSTA_VER >1 ) +__CPY_LE16S (tslot_cycle); +#endif + +#undef __CPY_U8 +#undef __CPY_LE16 +#undef __CPY_LE16S +#undef __CPY_LE32 +#undef __CPY_LE32S +} + +#define BTC_LEAK_AP_TH 10 +#define BTC_CYSTA_CHK_PERIOD 100 + +struct rtw89_btc_prpt { +u8 type; +__le16 len; +u8 content[]; +} __packed; + +static u32 _chk_btc_report (struct rtw89_dev *rtwdev, +struct rtw89_btc_btf_fwinfo *pfwinfo, +u8 *prptbuf,u32 index) +{ +struct rtw89_btc *btc = &rtwdev->btc; +struct rtw89_btc_dm *dm = &btc->dm; +struct rtw89_btc_rpt_cmn_info *pcinfo =NULL ; +struct rtw89_btc_wl_info *wl = &btc->cx.wl; +struct rtw89_btc_fbtc_rpt_ctrl *prpt =NULL ; +struct rtw89_btc_fbtc_cysta *pcysta_le32 =NULL ; +struct rtw89_btc_fbtc_cysta_cpu pcysta[1 ]; +struct rtw89_btc_prpt *btc_prpt =NULL ; +struct rtw89_btc_fbtc_slot *rtp_slot =NULL ; +u8 rpt_type =0 , *rpt_content =NULL , *pfinfo =NULL ; +u16 wl_slot_set =0 ; +u32 trace_step = btc->ctrl.trace_step, rpt_len =0 , diff_t; +u8 i; + +rtw89_debug (rtwdev,RTW89_DBG_BTC , +"[BTC], %s(): index:%d\n" , + __func__, index); + +if (!prptbuf) { + pfwinfo->err[BTFRE_INVALID_INPUT ]++; +return 0 ; + } + + btc_prpt = (struct rtw89_btc_prpt *)&prptbuf[index]; + rpt_type = btc_prpt->type; + rpt_len =le16_to_cpu (btc_prpt->len); + rpt_content = btc_prpt->content; + +rtw89_debug (rtwdev,RTW89_DBG_BTC , +"[BTC], %s(): rpt_type:%d\n" , + __func__, rpt_type); + +switch (rpt_type) { +case BTC_RPT_TYPE_CTRL : + pcinfo = &pfwinfo->rpt_ctrl.cinfo; + pfinfo = (u8 *)(&pfwinfo->rpt_ctrl.finfo); + pcinfo->req_len =sizeof (pfwinfo->rpt_ctrl.finfo); + pcinfo->req_fver =BTCRPT_VER ; + pcinfo->rx_len = rpt_len; + pcinfo->rx_cnt++; +break ; +case BTC_RPT_TYPE_TDMA : + pcinfo = &pfwinfo->rpt_fbtc_tdma.cinfo; + pfinfo = (u8 *)(&pfwinfo->rpt_fbtc_tdma.finfo); + pcinfo->req_len =sizeof (pfwinfo->rpt_fbtc_tdma.finfo); + pcinfo->req_fver =FCXTDMA_VER ; + pcinfo->rx_len = rpt_len; + pcinfo->rx_cnt++; +break ; +case BTC_RPT_TYPE_SLOT : + pcinfo = &pfwinfo->rpt_fbtc_slots.cinfo; + pfinfo = (u8 *)(&pfwinfo->rpt_fbtc_slots.finfo); + pcinfo->req_len =sizeof (pfwinfo->rpt_fbtc_slots.finfo); + pcinfo->req_fver =FCXSLOTS_VER ; + pcinfo->rx_len = rpt_len; + pcinfo->rx_cnt++; +break ; +case BTC_RPT_TYPE_CYSTA : + pcinfo = &pfwinfo->rpt_fbtc_cysta.cinfo; + pfinfo = (u8 *)(&pfwinfo->rpt_fbtc_cysta.finfo); + pcysta_le32 = &pfwinfo->rpt_fbtc_cysta.finfo; +rtw89_btc_fbtc_cysta_to_cpu (pcysta_le32, pcysta); + pcinfo->req_len =sizeof (pfwinfo->rpt_fbtc_cysta.finfo); + pcinfo->req_fver =FCXCYSTA_VER ; + pcinfo->rx_len = rpt_len; + pcinfo->rx_cnt++; +break ; +case BTC_RPT_TYPE_STEP : + pcinfo = &pfwinfo->rpt_fbtc_step.cinfo; + pfinfo = (u8 *)(&pfwinfo->rpt_fbtc_step.finfo); + pcinfo->req_len =sizeof (pfwinfo->rpt_fbtc_step.finfo.step[0 ]) * + trace_step +8 ; + pcinfo->req_fver =FCXSTEP_VER ; + pcinfo->rx_len = rpt_len; + pcinfo->rx_cnt++; +break ; +case BTC_RPT_TYPE_NULLSTA : + pcinfo = &pfwinfo->rpt_fbtc_nullsta.cinfo; + pfinfo = (u8 *)(&pfwinfo->rpt_fbtc_nullsta.finfo); + pcinfo->req_len =sizeof (pfwinfo->rpt_fbtc_nullsta.finfo); + pcinfo->req_fver =FCXNULLSTA_VER ; + pcinfo->rx_len = rpt_len; + pcinfo->rx_cnt++; +break ; +case BTC_RPT_TYPE_MREG : + pcinfo = &pfwinfo->rpt_fbtc_mregval.cinfo; + pfinfo = (u8 *)(&pfwinfo->rpt_fbtc_mregval.finfo); + pcinfo->req_len =sizeof (pfwinfo->rpt_fbtc_mregval.finfo); + pcinfo->req_fver =FCXMREG_VER ; + pcinfo->rx_len = rpt_len; + pcinfo->rx_cnt++; +break ; +case BTC_RPT_TYPE_GPIO_DBG : + pcinfo = &pfwinfo->rpt_fbtc_gpio_dbg.cinfo; + pfinfo = (u8 *)(&pfwinfo->rpt_fbtc_gpio_dbg.finfo); + pcinfo->req_len =sizeof (pfwinfo->rpt_fbtc_gpio_dbg.finfo); + pcinfo->req_fver =FCXGPIODBG_VER ; + pcinfo->rx_len = rpt_len; + pcinfo->rx_cnt++; +break ; +case BTC_RPT_TYPE_BT_VER : + pcinfo = &pfwinfo->rpt_fbtc_btver.cinfo; + pfinfo = (u8 *)(&pfwinfo->rpt_fbtc_btver.finfo); + pcinfo->req_len =sizeof (pfwinfo->rpt_fbtc_btver.finfo); + pcinfo->req_fver =FCX_BTVER_VER ; + pcinfo->rx_len = rpt_len; + pcinfo->rx_cnt++; +break ; +case BTC_RPT_TYPE_BT_SCAN : + pcinfo = &pfwinfo->rpt_fbtc_btscan.cinfo; + pfinfo = (u8 *)(&pfwinfo->rpt_fbtc_btscan.finfo); + pcinfo->req_len =sizeof (pfwinfo->rpt_fbtc_btscan.finfo); + pcinfo->req_fver =FCX_BTSCAN_VER ; + pcinfo->rx_len = rpt_len; + pcinfo->rx_cnt++; +break ; +case BTC_RPT_TYPE_BT_AFH : + pcinfo = &pfwinfo->rpt_fbtc_btafh.cinfo; + pfinfo = (u8 *)(&pfwinfo->rpt_fbtc_btafh.finfo); + pcinfo->req_len =sizeof (pfwinfo->rpt_fbtc_btafh.finfo); + pcinfo->req_fver =FCX_BTAFH_VER ; + pcinfo->rx_len = rpt_len; + pcinfo->rx_cnt++; +break ; +case BTC_RPT_TYPE_BT_DEVICE : + pcinfo = &pfwinfo->rpt_fbtc_btdev.cinfo; + pfinfo = (u8 *)(&pfwinfo->rpt_fbtc_btdev.finfo); + pcinfo->req_len =sizeof (pfwinfo->rpt_fbtc_btdev.finfo); + pcinfo->req_fver =FCX_BTDEVINFO_VER ; + pcinfo->rx_len = rpt_len; + pcinfo->rx_cnt++; +break ; +default : + pfwinfo->err[BTFRE_UNDEF_TYPE ]++; +return 0 ; + } + +if (rpt_len != pcinfo->req_len) { +if (rpt_type <BTC_RPT_TYPE_MAX ) + pfwinfo->len_mismch |= (0x1 << rpt_type); +else + pfwinfo->len_mismch |=BIT (31 ); +rtw89_debug (rtwdev,RTW89_DBG_BTC , +"[BTC], %s(): %d rpt_len:%d!=req_len:%d\n" , + __func__, rpt_type, rpt_len, pcinfo->req_len); + + pcinfo->valid =0 ; +return 0 ; + }else if (!pfinfo || !rpt_content || !pcinfo->req_len) { + pfwinfo->err[BTFRE_EXCEPTION ]++; + pcinfo->valid =0 ; +return 0 ; + } + +memcpy ((void *)pfinfo, (void *)rpt_content, pcinfo->req_len); + pcinfo->valid =1 ; + +if (rpt_type ==BTC_RPT_TYPE_TDMA ) { +rtw89_debug (rtwdev,RTW89_DBG_BTC , +"[BTC], %s(): check %d %ld\n" , __func__, +BTC_DCNT_TDMA_NONSYNC ,sizeof (dm->tdma_now)); + +if (memcmp (&dm->tdma_now, &pfwinfo->rpt_fbtc_tdma.finfo, +sizeof (dm->tdma_now)) !=0 ) { +rtw89_debug (rtwdev,RTW89_DBG_BTC , +"[BTC], %s(): %d tdma_now %x %x %x %x %x %x %x %x\n" , + __func__,BTC_DCNT_TDMA_NONSYNC , + dm->tdma_now.type, dm->tdma_now.rxflctrl, + dm->tdma_now.txpause, dm->tdma_now.wtgle_n, + dm->tdma_now.leak_n, dm->tdma_now.ext_ctrl, + dm->tdma_now.rsvd0, dm->tdma_now.rsvd1); + +rtw89_debug (rtwdev,RTW89_DBG_BTC , +"[BTC], %s(): %d rpt_fbtc_tdma %x %x %x %x %x %x %x %x\n" , + __func__,BTC_DCNT_TDMA_NONSYNC , + pfwinfo->rpt_fbtc_tdma.finfo.type, + pfwinfo->rpt_fbtc_tdma.finfo.rxflctrl, + pfwinfo->rpt_fbtc_tdma.finfo.txpause, + pfwinfo->rpt_fbtc_tdma.finfo.wtgle_n, + pfwinfo->rpt_fbtc_tdma.finfo.leak_n, + pfwinfo->rpt_fbtc_tdma.finfo.ext_ctrl, + pfwinfo->rpt_fbtc_tdma.finfo.rsvd0, + pfwinfo->rpt_fbtc_tdma.finfo.rsvd1); + } + +_chk_btc_err (rtwdev,BTC_DCNT_TDMA_NONSYNC , +memcmp (&dm->tdma_now, + &pfwinfo->rpt_fbtc_tdma.finfo, +sizeof (dm->tdma_now))); + } + +if (rpt_type ==BTC_RPT_TYPE_SLOT ) { +rtw89_debug (rtwdev,RTW89_DBG_BTC , +"[BTC], %s(): check %d %ld\n" , + __func__,BTC_DCNT_SLOT_NONSYNC , +sizeof (dm->slot_now)); + +if (memcmp (dm->slot_now, pfwinfo->rpt_fbtc_slots.finfo.slot, +sizeof (dm->slot_now)) !=0 ) { +for (i =0 ; i <CXST_MAX ; i++) { + rtp_slot = + &pfwinfo->rpt_fbtc_slots.finfo.slot[i]; +if (memcmp (&dm->slot_now[i], rtp_slot, +sizeof (dm->slot_now[i])) !=0 ) { +rtw89_debug (rtwdev,RTW89_DBG_BTC , +"[BTC], %s(): %d slot_now[%d] dur=0x%04x tbl=%08x type=0x%04x\n" , + __func__, +BTC_DCNT_SLOT_NONSYNC , i, + dm->slot_now[i].dur, + dm->slot_now[i].cxtbl, + dm->slot_now[i].cxtype); + +rtw89_debug (rtwdev,RTW89_DBG_BTC , +"[BTC], %s(): %d rpt_fbtc_slots[%d] dur=0x%04x tbl=%08x type=0x%04x\n" , + __func__, +BTC_DCNT_SLOT_NONSYNC , i, + rtp_slot->dur, + rtp_slot->cxtbl, + rtp_slot->cxtype); + } + } + } +_chk_btc_err (rtwdev,BTC_DCNT_SLOT_NONSYNC , +memcmp (dm->slot_now, + pfwinfo->rpt_fbtc_slots.finfo.slot, +sizeof (dm->slot_now))); + } + +if (rpt_type ==BTC_RPT_TYPE_CYSTA && + pcysta->cycles >=BTC_CYSTA_CHK_PERIOD ) { +/* Check Leak-AP */ +if (pcysta->slot_cnt[CXST_LK ] !=0 && + pcysta->leakrx_cnt !=0 && dm->tdma_now.rxflctrl) { +if (pcysta->slot_cnt[CXST_LK ] < +BTC_LEAK_AP_TH * pcysta->leakrx_cnt) + dm->leak_ap =1 ; + } + +/* Check diff time between WL slot and W1/E2G slot */ +if (dm->tdma_now.type ==CXTDMA_OFF && + dm->tdma_now.ext_ctrl ==CXECTL_EXT ) + wl_slot_set =le16_to_cpu (dm->slot_now[CXST_E2G ].dur); +else + wl_slot_set =le16_to_cpu (dm->slot_now[CXST_W1 ].dur); + +if (pcysta->tavg_cycle[CXT_WL ] > wl_slot_set) { + diff_t = pcysta->tavg_cycle[CXT_WL ] - wl_slot_set; +_chk_btc_err (rtwdev,BTC_DCNT_WL_SLOT_DRIFT , diff_t); + } + } + +if (rpt_type ==BTC_RPT_TYPE_CTRL ) { + prpt = &pfwinfo->rpt_ctrl.finfo; + btc->fwinfo.rpt_en_map = prpt->rpt_enable; + wl->ver_info.fw_coex = prpt->wl_fw_coex_ver; + wl->ver_info.fw = prpt->wl_fw_ver; + dm->wl_fw_cx_offload = !!(prpt->wl_fw_cx_offload); + } + +if (rpt_type >=BTC_RPT_TYPE_BT_VER && + rpt_type <=BTC_RPT_TYPE_BT_DEVICE ) +_update_bt_report (rtwdev, rpt_type, pfinfo); + +return (rpt_len +BTC_RPT_HDR_SIZE ); +} + +static void _parse_btc_report (struct rtw89_dev *rtwdev, +struct rtw89_btc_btf_fwinfo *pfwinfo, +u8 *pbuf,u32 buf_len) +{ +struct rtw89_btc_prpt *btc_prpt =NULL ; +u32 index =0 , rpt_len =0 ; + +rtw89_debug (rtwdev,RTW89_DBG_BTC , +"[BTC], %s(): buf_len:%d\n" , + __func__, buf_len); + +while (pbuf) { + btc_prpt = (struct rtw89_btc_prpt *)&pbuf[index]; +if (index +2 >=BTC_FWINFO_BUF ) +break ; +/* At least 3 bytes: type(1) & len(2) */ + rpt_len =le16_to_cpu (btc_prpt->len); +if ((index + rpt_len +BTC_RPT_HDR_SIZE ) > buf_len) +break ; + + rpt_len =_chk_btc_report (rtwdev, pfwinfo, pbuf, index); +if (!rpt_len) +break ; + index += rpt_len; + } +} + +#define BTC_TLV_HDR_LEN 2 + +static void _append_tdma (struct rtw89_dev *rtwdev) +{ +struct rtw89_btc *btc = &rtwdev->btc; +struct rtw89_btc_dm *dm = &btc->dm; +struct rtw89_btc_btf_tlv *tlv =NULL ; +struct rtw89_btc_fbtc_tdma *v =NULL ; +u16 len = btc->policy_len; + +if (!btc->update_policy_force && + !memcmp (&dm->tdma, &dm->tdma_now,sizeof (dm->tdma))) { +rtw89_debug (rtwdev, +RTW89_DBG_BTC ,"[BTC], %s(): tdma no change!\n" , + __func__); +return ; + } + + tlv = (struct rtw89_btc_btf_tlv *)&btc->policy[len]; + v = (struct rtw89_btc_fbtc_tdma *)&tlv->val[0 ]; + tlv->type =CXPOLICY_TDMA ; + tlv->len =sizeof (*v); + +memcpy (v, &dm->tdma,sizeof (*v)); + btc->policy_len +=BTC_TLV_HDR_LEN +sizeof (*v); + +rtw89_debug (rtwdev,RTW89_DBG_BTC , +"[BTC], %s(): type:%d, rxflctrl=%d, txpause=%d, wtgle_n=%d, leak_n=%d, ext_ctrl=%d\n" , + __func__, dm->tdma.type, dm->tdma.rxflctrl, + dm->tdma.txpause, dm->tdma.wtgle_n, dm->tdma.leak_n, + dm->tdma.ext_ctrl); +} + +static void _append_slot (struct rtw89_dev *rtwdev) +{ +struct rtw89_btc *btc = &rtwdev->btc; +struct rtw89_btc_dm *dm = &btc->dm; +struct rtw89_btc_btf_tlv *tlv =NULL ; +struct btc_fbtc_1slot *v =NULL ; +u16 len =0 ; +u8 i, cnt =0 ; + +rtw89_debug (rtwdev,RTW89_DBG_BTC , +"[BTC], %s(): A:btc->policy_len = %d\n" , + __func__, btc->policy_len); + +for (i =0 ; i <CXST_MAX ; i++) { +if (!btc->update_policy_force && + !memcmp (&dm->slot[i], &dm->slot_now[i], +sizeof (dm->slot[i]))) +continue ; + + len = btc->policy_len; + + tlv = (struct rtw89_btc_btf_tlv *)&btc->policy[len]; + v = (struct btc_fbtc_1slot *)&tlv->val[0 ]; + tlv->type =CXPOLICY_SLOT ; + tlv->len =sizeof (*v); + + v->fver =FCXONESLOT_VER ; + v->sid = i; + v->slot = dm->slot[i]; + +rtw89_debug (rtwdev,RTW89_DBG_BTC , +"[BTC], %s(): slot-%d: dur=%d, table=0x%08x, type=%d\n" , + __func__, i, dm->slot[i].dur, dm->slot[i].cxtbl, + dm->slot[i].cxtype); + cnt++; + + btc->policy_len +=BTC_TLV_HDR_LEN +sizeof (*v); + } + +if (cnt >0 ) +rtw89_debug (rtwdev,RTW89_DBG_BTC , +"[BTC], %s(): slot update (cnt=%d)!!\n" , + __func__, cnt); +} + +static void rtw89_btc_fw_en_rpt (struct rtw89_dev *rtwdev, +u32 rpt_map,bool rpt_state) +{ +struct rtw89_btc *btc = &rtwdev->btc; +struct rtw89_btc_btf_fwinfo *fwinfo = &btc->fwinfo; +struct rtw89_btc_btf_set_report r = {0 }; +u32 val =0 ; + +rtw89_debug (rtwdev,RTW89_DBG_BTC , +"[BTC], %s(): rpt_map=%x, rpt_state=%x\n" , + __func__, rpt_map, rpt_state); + +if (rpt_state) + val = fwinfo->rpt_en_map | rpt_map; +else + val = fwinfo->rpt_en_map & ~rpt_map; + +if (val == fwinfo->rpt_en_map) +return ; + + fwinfo->rpt_en_map = val; + + r.fver =BTF_SET_REPORT_VER ; + r.enable =cpu_to_le32 (val); + r.para =cpu_to_le32 (rpt_state); + +_send_fw_cmd (rtwdev,BTFC_SET ,SET_REPORT_EN , &r,sizeof (r)); +} + +static void rtw89_btc_fw_set_slots (struct rtw89_dev *rtwdev,u8 num, +struct rtw89_btc_fbtc_slot *s) +{ +struct rtw89_btc_btf_set_slot_table *tbl =NULL ; +u8 *ptr =NULL ; +u16 n =0 ; + + n =sizeof (*s) * num +sizeof (*tbl); + tbl =kmalloc (n,GFP_KERNEL ); +if (!tbl) +return ; + + tbl->fver =BTF_SET_SLOT_TABLE_VER ; + tbl->tbl_num = num; + ptr = &tbl->buf[0 ]; +memcpy (ptr, s, num *sizeof (*s)); + +_send_fw_cmd (rtwdev,BTFC_SET ,SET_SLOT_TABLE , tbl, n); + +kfree (tbl); +} + +static void btc_fw_set_monreg (struct rtw89_dev *rtwdev) +{ +const struct rtw89_chip_info *chip = rtwdev->chip; +struct rtw89_btc_btf_set_mon_reg *monreg =NULL ; +u8 n, *ptr =NULL , ulen; +u16 sz =0 ; + + n = chip->mon_reg_num; + +rtw89_debug (rtwdev,RTW89_DBG_BTC , +"[BTC], %s(): mon_reg_num=%d\n" , __func__, n); +if (n >CXMREG_MAX ) { +rtw89_debug (rtwdev,RTW89_DBG_BTC , +"[BTC], %s(): mon reg count %d > %d\n" , + __func__, n,CXMREG_MAX ); +return ; + } + + ulen =sizeof (struct rtw89_btc_fbtc_mreg ); + sz = (ulen * n) +sizeof (*monreg); + monreg =kmalloc (sz,GFP_KERNEL ); +if (!monreg) +return ; + + monreg->fver =BTF_SET_MON_REG_VER ; + monreg->reg_num = n; + ptr = &monreg->buf[0 ]; +memcpy ((void *)ptr, chip->mon_reg, n * ulen); +rtw89_debug (rtwdev,RTW89_DBG_BTC , +"[BTC], %s(): sz=%d ulen=%d n=%d\n" , + __func__, sz, ulen, n); + +_send_fw_cmd (rtwdev,BTFC_SET ,SET_MREG_TABLE , (u8 *)monreg, sz); +kfree (monreg); +rtw89_btc_fw_en_rpt (rtwdev,RPT_EN_MREG ,1 ); +} + +static void _update_dm_step (struct rtw89_dev *rtwdev, +enum btc_reason_and_action reason_or_action) +{ +struct rtw89_btc *btc = &rtwdev->btc; +struct rtw89_btc_dm *dm = &btc->dm; + +/* use ring-structure to store dm step */ + dm->dm_step.step[dm->dm_step.step_pos] = reason_or_action; + dm->dm_step.step_pos++; + +if (dm->dm_step.step_pos >=ARRAY_SIZE (dm->dm_step.step)) { + dm->dm_step.step_pos =0 ; + dm->dm_step.step_ov = true; + } +} + +static void _fw_set_policy (struct rtw89_dev *rtwdev,u16 policy_type, +enum btc_reason_and_action action) +{ +struct rtw89_btc *btc = &rtwdev->btc; +struct rtw89_btc_dm *dm = &btc->dm; + + dm->run_action = action; + +_update_dm_step (rtwdev, action |BTC_ACT_EXT_BIT ); +_update_dm_step (rtwdev, policy_type |BTC_POLICY_EXT_BIT ); + + btc->policy_len =0 ; + btc->policy_type = policy_type; + +_append_tdma (rtwdev); +_append_slot (rtwdev); + +if (btc->policy_len ==0 || btc->policy_len >RTW89_BTC_POLICY_MAXLEN ) +return ; + +rtw89_debug (rtwdev,RTW89_DBG_BTC , +"[BTC], %s(): action = %d -> policy type/len: 0x%04x/%d\n" , + __func__, action, policy_type, btc->policy_len); + +if (dm->tdma.rxflctrl ==CXFLC_NULLP || + dm->tdma.rxflctrl ==CXFLC_QOSNULL ) + btc->lps =1 ; +else + btc->lps =0 ; + +if (btc->lps ==1 ) +rtw89_set_coex_ctrl_lps (rtwdev, btc->lps); + +_send_fw_cmd (rtwdev,BTFC_SET ,SET_CX_POLICY , + btc->policy, btc->policy_len); + +memcpy (&dm->tdma_now, &dm->tdma,sizeof (dm->tdma_now)); +memcpy (&dm->slot_now, &dm->slot,sizeof (dm->slot_now)); + +if (btc->update_policy_force) + btc->update_policy_force = false; + +if (btc->lps ==0 ) +rtw89_set_coex_ctrl_lps (rtwdev, btc->lps); +} + +static void _fw_set_drv_info (struct rtw89_dev *rtwdev,u8 type) +{ +switch (type) { +case CXDRVINFO_INIT : +rtw89_fw_h2c_cxdrv_init (rtwdev); +break ; +case CXDRVINFO_ROLE : +rtw89_fw_h2c_cxdrv_role (rtwdev); +break ; +case CXDRVINFO_CTRL : +rtw89_fw_h2c_cxdrv_ctrl (rtwdev); +break ; +case CXDRVINFO_RFK : +rtw89_fw_h2c_cxdrv_rfk (rtwdev); +break ; +default : +break ; + } +} + +static +void btc_fw_event (struct rtw89_dev *rtwdev,u8 evt_id,void *data,u32 len) +{ +struct rtw89_btc *btc = &rtwdev->btc; +struct rtw89_btc_btf_fwinfo *pfwinfo = &btc->fwinfo; + +rtw89_debug (rtwdev,RTW89_DBG_BTC , +"[BTC], %s(): evt_id:%d len:%d\n" , + __func__, evt_id, len); + +if (!len || !data) +return ; + +switch (evt_id) { +case BTF_EVNT_RPT : +_parse_btc_report (rtwdev, pfwinfo, data, len); +break ; +default : +break ; + } +} + +static void _set_gnt_wl (struct rtw89_dev *rtwdev,u8 phy_map,u8 state) +{ +struct rtw89_btc *btc = &rtwdev->btc; +struct rtw89_btc_dm *dm = &btc->dm; +struct rtw89_mac_ax_gnt *g = dm->gnt.band; +u8 i; + +if (phy_map >BTC_PHY_ALL ) +return ; + +for (i =0 ; i <RTW89_PHY_MAX ; i++) { +if (!(phy_map &BIT (i))) +continue ; + +switch (state) { +case BTC_GNT_HW : + g[i].gnt_wl_sw_en =0 ; + g[i].gnt_wl =0 ; +break ; +case BTC_GNT_SW_LO : + g[i].gnt_wl_sw_en =1 ; + g[i].gnt_wl =0 ; +break ; +case BTC_GNT_SW_HI : + g[i].gnt_wl_sw_en =1 ; + g[i].gnt_wl =1 ; +break ; + } + } + +rtw89_mac_cfg_gnt (rtwdev, &dm->gnt); +} + +#define BTC_TDMA_WLROLE_MAX 2 + +static void _set_bt_ignore_wlan_act (struct rtw89_dev *rtwdev,u8 enable) +{ +rtw89_debug (rtwdev,RTW89_DBG_BTC , +"[BTC], %s(): set bt %s wlan_act\n" , __func__, + enable ?"ignore" :"do not ignore" ); + +_send_fw_cmd (rtwdev,BTFC_SET ,SET_BT_IGNORE_WLAN_ACT , &enable,1 ); +} + +#define WL_TX_POWER_NO_BTC_CTRL GENMASK(31, 0) +#define WL_TX_POWER_ALL_TIME GENMASK(15, 0) +#define WL_TX_POWER_WITH_BT GENMASK(31, 16) +#define WL_TX_POWER_INT_PART GENMASK(8, 2) +#define WL_TX_POWER_FRA_PART GENMASK(1, 0) +#define B_BTC_WL_TX_POWER_SIGN BIT(7) +#define B_TSSI_WL_TX_POWER_SIGN BIT(8) + +static void _set_wl_tx_power (struct rtw89_dev *rtwdev,u32 level) +{ +const struct rtw89_chip_info *chip = rtwdev->chip; +struct rtw89_btc *btc = &rtwdev->btc; +struct rtw89_btc_wl_info *wl = &btc->cx.wl; +u32 pwr_val; + +if (wl->rf_para.tx_pwr_freerun == level) +return ; + + wl->rf_para.tx_pwr_freerun = level; + btc->dm.rf_trx_para.wl_tx_power = level; + +rtw89_debug (rtwdev,RTW89_DBG_BTC , +"[BTC], %s(): level = %d\n" , + __func__, level); + +if (level ==RTW89_BTC_WL_DEF_TX_PWR ) { + pwr_val =WL_TX_POWER_NO_BTC_CTRL ; + }else {/* only apply "force tx power" */ + pwr_val =FIELD_PREP (WL_TX_POWER_INT_PART , level); +if (pwr_val >RTW89_BTC_WL_DEF_TX_PWR ) + pwr_val =RTW89_BTC_WL_DEF_TX_PWR ; + +if (level &B_BTC_WL_TX_POWER_SIGN ) + pwr_val |=B_TSSI_WL_TX_POWER_SIGN ; + pwr_val |=WL_TX_POWER_WITH_BT ; + } + + chip->ops->btc_set_wl_txpwr_ctrl (rtwdev, pwr_val); +} + +#define rtw89_btc_wl_rx_gain (rtwdev, level) do {} while (0) + +static void _set_wl_rx_gain (struct rtw89_dev *rtwdev,u32 level) +{ +struct rtw89_btc *btc = &rtwdev->btc; +struct rtw89_btc_wl_info *wl = &btc->cx.wl; + +if (wl->rf_para.rx_gain_freerun == level) +return ; + + wl->rf_para.rx_gain_freerun = level; + btc->dm.rf_trx_para.wl_rx_gain = level; + +rtw89_debug (rtwdev,RTW89_DBG_BTC , +"[BTC], %s(): level = %d\n" , + __func__, level); + +rtw89_btc_wl_rx_gain (rtwdev, level); +} + +static void _set_bt_tx_power (struct rtw89_dev *rtwdev,u8 level) +{ +struct rtw89_btc *btc = &rtwdev->btc; +struct rtw89_btc_bt_info *bt = &btc->cx.bt; +u8 buf =0 ; + +if (bt->rf_para.tx_pwr_freerun == level) +return ; + + bt->rf_para.tx_pwr_freerun = level; + btc->dm.rf_trx_para.bt_tx_power = level; + +rtw89_debug (rtwdev,RTW89_DBG_BTC , +"[BTC], %s(): level = %d\n" , + __func__, level); + + buf = (s8)(-level); +_send_fw_cmd (rtwdev,BTFC_SET ,SET_BT_TX_PWR , &buf,1 ); +} + +#define BTC_BT_RX_NORMAL_LVL 7 + +static void _set_bt_rx_gain (struct rtw89_dev *rtwdev,u8 level) +{ +struct rtw89_btc *btc = &rtwdev->btc; +struct rtw89_btc_bt_info *bt = &btc->cx.bt; + +if (bt->rf_para.rx_gain_freerun == level || + level >BTC_BT_RX_NORMAL_LVL ) +return ; + + bt->rf_para.rx_gain_freerun = level; + btc->dm.rf_trx_para.bt_rx_gain = level; + +rtw89_debug (rtwdev,RTW89_DBG_BTC , +"[BTC], %s(): level = %d\n" , + __func__, level); + +if (level ==BTC_BT_RX_NORMAL_LVL ) +_write_scbd (rtwdev,BTC_WSCB_RXGAIN , false); +else +_write_scbd (rtwdev,BTC_WSCB_RXGAIN , true); + +_send_fw_cmd (rtwdev,BTFC_SET ,SET_BT_LNA_CONSTRAIN , &level,1 ); +} + +static void _set_rf_trx_para (struct rtw89_dev *rtwdev) +{ +const struct rtw89_chip_info *chip = rtwdev->chip; +struct rtw89_btc *btc = &rtwdev->btc; +struct rtw89_btc_dm *dm = &btc->dm; +struct rtw89_btc_wl_info *wl = &btc->cx.wl; +struct rtw89_btc_bt_info *bt = &btc->cx.bt; +struct rtw89_btc_rf_trx_para para; +u32 wl_stb_chg =0 ; +u8 level_id =0 ; + +if (!dm->freerun) { + dm->trx_para_level =0 ; + chip->ops->btc_bt_aci_imp (rtwdev); + } + + level_id = (u8 )dm->trx_para_level; + +if (level_id >= chip->rf_para_dlink_num || + level_id >= chip->rf_para_ulink_num) { +rtw89_debug (rtwdev,RTW89_DBG_BTC , +"[BTC], %s(): invalid level_id: %d\n" , + __func__, level_id); +return ; + } + +if (wl->status.map.traffic_dir &BIT (RTW89_TFC_UL )) + para = chip->rf_para_ulink[level_id]; +else + para = chip->rf_para_dlink[level_id]; + +if (para.wl_tx_power !=RTW89_BTC_WL_DEF_TX_PWR ) +rtw89_debug (rtwdev,RTW89_DBG_BTC , +"[BTC], %s(): wl_tx_power=%d\n" , + __func__, para.wl_tx_power); +_set_wl_tx_power (rtwdev, para.wl_tx_power); +_set_wl_rx_gain (rtwdev, para.wl_rx_gain); +_set_bt_tx_power (rtwdev, para.bt_tx_power); +_set_bt_rx_gain (rtwdev, para.bt_rx_gain); + +if (bt->enable.now ==0 || wl->status.map.rf_off ==1 || + wl->status.map.lps ==1 ) + wl_stb_chg =0 ; +else + wl_stb_chg =1 ; + +if (wl_stb_chg != dm->wl_stb_chg) { +rtw89_debug (rtwdev,RTW89_DBG_BTC , +"[BTC], %s(): wl_stb_chg=%d\n" , + __func__, wl_stb_chg); + dm->wl_stb_chg = wl_stb_chg; + chip->ops->btc_wl_s1_standby (rtwdev, dm->wl_stb_chg); + } +} + +static void _update_btc_state_map (struct rtw89_dev *rtwdev) +{ +struct rtw89_btc *btc = &rtwdev->btc; +struct rtw89_btc_cx *cx = &btc->cx; +struct rtw89_btc_wl_info *wl = &cx->wl; +struct rtw89_btc_bt_info *bt = &cx->bt; +struct rtw89_btc_bt_link_info *bt_linfo = &bt->link_info; + +if (wl->status.map.connecting || wl->status.map._4way || + wl->status.map.roaming) { + cx->state_map =BTC_WLINKING ; + }else if (wl->status.map.scan) {/* wl scan */ +if (bt_linfo->status.map.inq_pag) + cx->state_map =BTC_WSCAN_BSCAN ; +else + cx->state_map =BTC_WSCAN_BNOSCAN ; + }else if (wl->status.map.busy) {/* only busy */ +if (bt_linfo->status.map.inq_pag) + cx->state_map =BTC_WBUSY_BSCAN ; +else + cx->state_map =BTC_WBUSY_BNOSCAN ; + }else {/* wl idle */ + cx->state_map =BTC_WIDLE ; + } +} + +static void _set_bt_afh_info (struct rtw89_dev *rtwdev) +{ +const struct rtw89_chip_info *chip = rtwdev->chip; +struct rtw89_btc *btc = &rtwdev->btc; +struct rtw89_btc_wl_info *wl = &btc->cx.wl; +struct rtw89_btc_bt_info *bt = &btc->cx.bt; +struct rtw89_btc_bt_link_info *b = &bt->link_info; +struct rtw89_btc_wl_role_info *wl_rinfo = &wl->role_info; +u8 en =0 , i, ch =0 , bw =0 ; + +if (btc->ctrl.manual || wl->status.map.scan) +return ; + +/* TODO if include module->ant.type == BTC_ANT_SHARED */ +if (wl->status.map.rf_off || bt->whql_test || + wl_rinfo->link_mode ==BTC_WLINK_NOLINK || + wl_rinfo->link_mode ==BTC_WLINK_5G || + wl_rinfo->connect_cnt >BTC_TDMA_WLROLE_MAX ) { + en = false; + }else if (wl_rinfo->link_mode ==BTC_WLINK_2G_MCC || + wl_rinfo->link_mode ==BTC_WLINK_2G_SCC ) { + en = true; +/* get p2p channel */ +for (i =0 ; i <RTW89_MAX_HW_PORT_NUM ; i++) { +if (wl_rinfo->active_role[i].role == +RTW89_WIFI_ROLE_P2P_GO || + wl_rinfo->active_role[i].role == +RTW89_WIFI_ROLE_P2P_CLIENT ) { + ch = wl_rinfo->active_role[i].ch; + bw = wl_rinfo->active_role[i].bw; +break ; + } + } + }else { + en = true; +/* get 2g channel */ +for (i =0 ; i <RTW89_MAX_HW_PORT_NUM ; i++) { +if (wl_rinfo->active_role[i].connected && + wl_rinfo->active_role[i].band ==RTW89_BAND_2G ) { + ch = wl_rinfo->active_role[i].ch; + bw = wl_rinfo->active_role[i].bw; +break ; + } + } + } + +switch (bw) { +case RTW89_CHANNEL_WIDTH_20 : +#ifdef BTC_NON_SHARED_ANT_FREERUN + bw =48 ; +#else + bw =20 + chip->afh_guard_ch *2 ; +#endif +break ; +case RTW89_CHANNEL_WIDTH_40 : + bw =40 + chip->afh_guard_ch *2 ; +break ; +case RTW89_CHANNEL_WIDTH_5 : + bw =5 + chip->afh_guard_ch *2 ; +break ; +case RTW89_CHANNEL_WIDTH_10 : + bw =10 + chip->afh_guard_ch *2 ; +break ; +default : + bw =0 ; + en = false;/* turn off AFH info if BW > 40 */ +break ; + } + +if (wl->afh_info.en == en && + wl->afh_info.ch == ch && + wl->afh_info.bw == bw && + b->profile_cnt.last == b->profile_cnt.now) { +rtw89_debug (rtwdev,RTW89_DBG_BTC , +"[BTC], %s(): return because no change!\n" , + __func__); +return ; + } + + wl->afh_info.en = en; + wl->afh_info.ch = ch; + wl->afh_info.bw = bw; + +_send_fw_cmd (rtwdev,BTFC_SET ,SET_BT_WL_CH_INFO , &wl->afh_info,3 ); + +rtw89_debug (rtwdev,RTW89_DBG_BTC , +"[BTC], %s(): en=%d, ch=%d, bw=%d\n" , + __func__, en, ch, bw); + btc->cx.cnt_wl[BTC_WCNT_CH_UPDATE ]++; +} + +static bool _check_freerun (struct rtw89_dev *rtwdev) +{ +struct rtw89_btc *btc = &rtwdev->btc; +struct rtw89_btc_wl_info *wl = &btc->cx.wl; +struct rtw89_btc_bt_info *bt = &btc->cx.bt; +struct rtw89_btc_wl_role_info *wl_rinfo = &wl->role_info; +struct rtw89_btc_bt_link_info *bt_linfo = &bt->link_info; +struct rtw89_btc_bt_hid_desc *hid = &bt_linfo->hid_desc; + +if (btc->mdinfo.ant.type ==BTC_ANT_SHARED ) { + btc->dm.trx_para_level =0 ; +return false; + } + +/* The below is dedicated antenna case */ +if (wl_rinfo->connect_cnt >BTC_TDMA_WLROLE_MAX ) { + btc->dm.trx_para_level =5 ; +return true; + } + +if (bt_linfo->profile_cnt.now ==0 ) { + btc->dm.trx_para_level =5 ; +return true; + } + +if (hid->pair_cnt >BTC_TDMA_BTHID_MAX ) { + btc->dm.trx_para_level =5 ; +return true; + } + +/* TODO get isolation by BT psd */ +if (btc->mdinfo.ant.isolation >=BTC_FREERUN_ANTISO_MIN ) { + btc->dm.trx_para_level =5 ; +return true; + } + +if (!wl->status.map.busy) {/* wl idle -> freerun */ + btc->dm.trx_para_level =5 ; +return true; + }else if (wl->rssi_level >1 ) {/* WL rssi < 50% (-60dBm) */ + btc->dm.trx_para_level =0 ; +return false; + }else if (wl->status.map.traffic_dir &BIT (RTW89_TFC_UL )) { +if (wl->rssi_level ==0 && bt_linfo->rssi >31 ) { + btc->dm.trx_para_level =6 ; +return true; + }else if (wl->rssi_level ==1 && bt_linfo->rssi >36 ) { + btc->dm.trx_para_level =7 ; +return true; + } + btc->dm.trx_para_level =0 ; +return false; + }else if (wl->status.map.traffic_dir &BIT (RTW89_TFC_DL )) { +if (bt_linfo->rssi >28 ) { + btc->dm.trx_para_level =6 ; +return true; + } + } + + btc->dm.trx_para_level =0 ; +return false; +} + +#define _tdma_set_flctrl (btc, flc) ({(btc)->dm.tdma.rxflctrl = flc; }) +#define _tdma_set_tog (btc, wtg) ({(btc)->dm.tdma.wtgle_n = wtg; }) +#define _tdma_set_lek (btc, lek) ({(btc)->dm.tdma.leak_n = lek; }) + +#define _slot_set (btc, sid, dura, tbl, type) \ + do { \ + typeof(sid) _sid = (sid); \ + typeof(btc) _btc = (btc); \ + _btc->dm.slot[_sid].dur = cpu_to_le16(dura);\ + _btc->dm.slot[_sid].cxtbl = cpu_to_le32(tbl); \ + _btc->dm.slot[_sid].cxtype = cpu_to_le16(type); \ + } while (0) + +#define _slot_set_dur (btc, sid, dura) (btc)->dm.slot[sid].dur = cpu_to_le16(dura) +#define _slot_set_tbl (btc, sid, tbl) (btc)->dm.slot[sid].cxtbl = cpu_to_le32(tbl) +#define _slot_set_type (btc, sid, type) (btc)->dm.slot[sid].cxtype = cpu_to_le16(type) + +struct btc_btinfo_lb2 { +u8 connect:1 ; +u8 sco_busy:1 ; +u8 inq_pag:1 ; +u8 acl_busy:1 ; +u8 hfp:1 ; +u8 hid:1 ; +u8 a2dp:1 ; +u8 pan:1 ; +}; + +struct btc_btinfo_lb3 { +u8 retry:4 ; +u8 cqddr:1 ; +u8 inq:1 ; +u8 mesh_busy:1 ; +u8 pag:1 ; +}; + +struct btc_btinfo_hb0 { +s8 rssi; +}; + +struct btc_btinfo_hb1 { +u8 ble_connect:1 ; +u8 reinit:1 ; +u8 relink:1 ; +u8 igno_wl:1 ; +u8 voice:1 ; +u8 ble_scan:1 ; +u8 role_sw:1 ; +u8 multi_link:1 ; +}; + +struct btc_btinfo_hb2 { +u8 pan_active:1 ; +u8 afh_update:1 ; +u8 a2dp_active:1 ; +u8 slave:1 ; +u8 hid_slot:2 ; +u8 hid_cnt:2 ; +}; + +struct btc_btinfo_hb3 { +u8 a2dp_bitpool:6 ; +u8 tx_3m:1 ; +u8 a2dp_sink:1 ; +}; + +union btc_btinfo { +u8 val; +struct btc_btinfo_lb2 lb2; +struct btc_btinfo_lb3 lb3; +struct btc_btinfo_hb0 hb0; +struct btc_btinfo_hb1 hb1; +struct btc_btinfo_hb2 hb2; +struct btc_btinfo_hb3 hb3; +}; + +static void _set_policy (struct rtw89_dev *rtwdev,u16 policy_type, +enum btc_reason_and_action action) +{ +struct rtw89_btc *btc = &rtwdev->btc; +struct rtw89_btc_dm *dm = &btc->dm; +struct rtw89_btc_fbtc_tdma *t = &dm->tdma; +struct rtw89_btc_fbtc_slot *s = dm->slot; +u8 type; +u32 tbl_w1, tbl_b1, tbl_b4; + +if (btc->mdinfo.ant.type ==BTC_ANT_SHARED ) { +if (btc->cx.wl.status.map._4way) + tbl_w1 = cxtbl[1 ]; +else + tbl_w1 = cxtbl[8 ]; + tbl_b1 = cxtbl[3 ]; + tbl_b4 = cxtbl[3 ]; + }else { + tbl_w1 = cxtbl[16 ]; + tbl_b1 = cxtbl[17 ]; + tbl_b4 = cxtbl[17 ]; + } + + type = (u8)((policy_type &BTC_CXP_MASK ) >>8 ); + btc->bt_req_en = false; + +switch (type) { +case BTC_CXP_USERDEF0 : + *t = t_def[CXTD_OFF ]; + s[CXST_OFF ] = s_def[CXST_OFF ]; +_slot_set_tbl (btc,CXST_OFF , cxtbl[2 ]); + btc->update_policy_force = true; +break ; +case BTC_CXP_OFF :/* TDMA off */ +_write_scbd (rtwdev,BTC_WSCB_TDMA , false); + *t = t_def[CXTD_OFF ]; + s[CXST_OFF ] = s_def[CXST_OFF ]; + +switch (policy_type) { +case BTC_CXP_OFF_BT : +_slot_set_tbl (btc,CXST_OFF , cxtbl[2 ]); +break ; +case BTC_CXP_OFF_WL : +_slot_set_tbl (btc,CXST_OFF , cxtbl[1 ]); +break ; +case BTC_CXP_OFF_EQ0 : +_slot_set_tbl (btc,CXST_OFF , cxtbl[0 ]); +break ; +case BTC_CXP_OFF_EQ1 : +_slot_set_tbl (btc,CXST_OFF , cxtbl[16 ]); +break ; +case BTC_CXP_OFF_EQ2 : +_slot_set_tbl (btc,CXST_OFF , cxtbl[17 ]); +break ; +case BTC_CXP_OFF_EQ3 : +_slot_set_tbl (btc,CXST_OFF , cxtbl[18 ]); +break ; +case BTC_CXP_OFF_BWB0 : +_slot_set_tbl (btc,CXST_OFF , cxtbl[5 ]); +break ; +case BTC_CXP_OFF_BWB1 : +_slot_set_tbl (btc,CXST_OFF , cxtbl[8 ]); +break ; + } +break ; +case BTC_CXP_OFFB :/* TDMA off + beacon protect */ +_write_scbd (rtwdev,BTC_WSCB_TDMA , false); + *t = t_def[CXTD_OFF_B2 ]; + s[CXST_OFF ] = s_def[CXST_OFF ]; +switch (policy_type) { +case BTC_CXP_OFFB_BWB0 : +_slot_set_tbl (btc,CXST_OFF , cxtbl[8 ]); +break ; + } +break ; +case BTC_CXP_OFFE :/* TDMA off + beacon protect + Ext_control */ + btc->bt_req_en = true; +_write_scbd (rtwdev,BTC_WSCB_TDMA , true); + *t = t_def[CXTD_OFF_EXT ]; +switch (policy_type) { +case BTC_CXP_OFFE_DEF : + s[CXST_E2G ] = s_def[CXST_E2G ]; + s[CXST_E5G ] = s_def[CXST_E5G ]; + s[CXST_EBT ] = s_def[CXST_EBT ]; + s[CXST_ENULL ] = s_def[CXST_ENULL ]; +break ; +case BTC_CXP_OFFE_DEF2 : +_slot_set (btc,CXST_E2G ,20 , cxtbl[1 ],SLOT_ISO ); + s[CXST_E5G ] = s_def[CXST_E5G ]; + s[CXST_EBT ] = s_def[CXST_EBT ]; + s[CXST_ENULL ] = s_def[CXST_ENULL ]; +break ; + } +break ; +case BTC_CXP_FIX :/* TDMA Fix-Slot */ +_write_scbd (rtwdev,BTC_WSCB_TDMA , true); + *t = t_def[CXTD_FIX ]; +switch (policy_type) { +case BTC_CXP_FIX_TD3030 : +_slot_set (btc,CXST_W1 ,30 , tbl_w1,SLOT_ISO ); +_slot_set (btc,CXST_B1 ,30 , tbl_b1,SLOT_MIX ); +break ; +case BTC_CXP_FIX_TD5050 : +_slot_set (btc,CXST_W1 ,50 , tbl_w1,SLOT_ISO ); +_slot_set (btc,CXST_B1 ,50 , tbl_b1,SLOT_MIX ); +break ; +case BTC_CXP_FIX_TD2030 : +_slot_set (btc,CXST_W1 ,20 , tbl_w1,SLOT_ISO ); +_slot_set (btc,CXST_B1 ,30 , tbl_b1,SLOT_MIX ); +break ; +case BTC_CXP_FIX_TD4010 : +_slot_set (btc,CXST_W1 ,40 , tbl_w1,SLOT_ISO ); +_slot_set (btc,CXST_B1 ,10 , tbl_b1,SLOT_MIX ); +break ; +case BTC_CXP_FIX_TD4020 : +_slot_set (btc,CXST_W1 ,40 , cxtbl[1 ],SLOT_MIX ); +_slot_set (btc,CXST_B1 ,20 , tbl_b1,SLOT_MIX ); +break ; +case BTC_CXP_FIX_TD7010 : +_slot_set (btc,CXST_W1 ,70 , tbl_w1,SLOT_ISO ); +_slot_set (btc,CXST_B1 ,10 , tbl_b1,SLOT_MIX ); +break ; +case BTC_CXP_FIX_TD2060 : +_slot_set (btc,CXST_W1 ,20 , tbl_w1,SLOT_ISO ); +_slot_set (btc,CXST_B1 ,60 , tbl_b1,SLOT_MIX ); +break ; +case BTC_CXP_FIX_TD3060 : +_slot_set (btc,CXST_W1 ,30 , tbl_w1,SLOT_ISO ); +_slot_set (btc,CXST_B1 ,60 , tbl_b1,SLOT_MIX ); +break ; +case BTC_CXP_FIX_TD2080 : +_slot_set (btc,CXST_W1 ,20 , tbl_w1,SLOT_ISO ); +_slot_set (btc,CXST_B1 ,80 , tbl_b1,SLOT_MIX ); +break ; +case BTC_CXP_FIX_TDW1B1 :/* W1:B1 = user-define */ +_slot_set (btc,CXST_W1 , dm->slot_dur[CXST_W1 ], + tbl_w1,SLOT_ISO ); +_slot_set (btc,CXST_B1 , dm->slot_dur[CXST_B1 ], + tbl_b1,SLOT_MIX ); +break ; + } +break ; +case BTC_CXP_PFIX :/* PS-TDMA Fix-Slot */ +_write_scbd (rtwdev,BTC_WSCB_TDMA , true); + *t = t_def[CXTD_PFIX ]; +if (btc->cx.wl.role_info.role_map.role.ap) +_tdma_set_flctrl (btc,CXFLC_QOSNULL ); + +switch (policy_type) { +case BTC_CXP_PFIX_TD3030 : +_slot_set (btc,CXST_W1 ,30 , tbl_w1,SLOT_ISO ); +_slot_set (btc,CXST_B1 ,30 , tbl_b1,SLOT_MIX ); +break ; +case BTC_CXP_PFIX_TD5050 : +_slot_set (btc,CXST_W1 ,50 , tbl_w1,SLOT_ISO ); +_slot_set (btc,CXST_B1 ,50 , tbl_b1,SLOT_MIX ); +break ; +case BTC_CXP_PFIX_TD2030 : +_slot_set (btc,CXST_W1 ,20 , tbl_w1,SLOT_ISO ); +_slot_set (btc,CXST_B1 ,30 , tbl_b1,SLOT_MIX ); +break ; +case BTC_CXP_PFIX_TD2060 : +_slot_set (btc,CXST_W1 ,20 , tbl_w1,SLOT_ISO ); +_slot_set (btc,CXST_B1 ,60 , tbl_b1,SLOT_MIX ); +break ; +case BTC_CXP_PFIX_TD3070 : +_slot_set (btc,CXST_W1 ,30 , tbl_w1,SLOT_ISO ); +_slot_set (btc,CXST_B1 ,60 , tbl_b1,SLOT_MIX ); +break ; +case BTC_CXP_PFIX_TD2080 : +_slot_set (btc,CXST_W1 ,20 , tbl_w1,SLOT_ISO ); +_slot_set (btc,CXST_B1 ,80 , tbl_b1,SLOT_MIX ); +break ; + } +break ; +case BTC_CXP_AUTO :/* TDMA Auto-Slot */ +_write_scbd (rtwdev,BTC_WSCB_TDMA , true); + *t = t_def[CXTD_AUTO ]; +switch (policy_type) { +case BTC_CXP_AUTO_TD50200 : +_slot_set (btc,CXST_W1 ,50 , tbl_w1,SLOT_ISO ); +_slot_set (btc,CXST_B1 ,200 , tbl_b1,SLOT_MIX ); +break ; +case BTC_CXP_AUTO_TD60200 : +_slot_set (btc,CXST_W1 ,60 , tbl_w1,SLOT_ISO ); +_slot_set (btc,CXST_B1 ,200 , tbl_b1,SLOT_MIX ); +break ; +case BTC_CXP_AUTO_TD20200 : +_slot_set (btc,CXST_W1 ,20 , tbl_w1,SLOT_ISO ); +_slot_set (btc,CXST_B1 ,200 , tbl_b1,SLOT_MIX ); +break ; +case BTC_CXP_AUTO_TDW1B1 :/* W1:B1 = user-define */ +_slot_set (btc,CXST_W1 , dm->slot_dur[CXST_W1 ], + tbl_w1,SLOT_ISO ); +_slot_set (btc,CXST_B1 , dm->slot_dur[CXST_B1 ], + tbl_b1,SLOT_MIX ); +break ; + } +break ; +case BTC_CXP_PAUTO :/* PS-TDMA Auto-Slot */ +_write_scbd (rtwdev,BTC_WSCB_TDMA , true); + *t = t_def[CXTD_PAUTO ]; +switch (policy_type) { +case BTC_CXP_PAUTO_TD50200 : +_slot_set (btc,CXST_W1 ,50 , tbl_w1,SLOT_ISO ); +_slot_set (btc,CXST_B1 ,200 , tbl_b1,SLOT_MIX ); +break ; +case BTC_CXP_PAUTO_TD60200 : +_slot_set (btc,CXST_W1 ,60 , tbl_w1,SLOT_ISO ); +_slot_set (btc,CXST_B1 ,200 , tbl_b1,SLOT_MIX ); +break ; +case BTC_CXP_PAUTO_TD20200 : +_slot_set (btc,CXST_W1 ,20 , tbl_w1,SLOT_ISO ); +_slot_set (btc,CXST_B1 ,200 , tbl_b1,SLOT_MIX ); +break ; +case BTC_CXP_PAUTO_TDW1B1 : +_slot_set (btc,CXST_W1 , dm->slot_dur[CXST_W1 ], + tbl_w1,SLOT_ISO ); +_slot_set (btc,CXST_B1 , dm->slot_dur[CXST_B1 ], + tbl_b1,SLOT_MIX ); +break ; + } +break ; +case BTC_CXP_AUTO2 :/* TDMA Auto-Slot2 */ +_write_scbd (rtwdev,BTC_WSCB_TDMA , true); + *t = t_def[CXTD_AUTO2 ]; +switch (policy_type) { +case BTC_CXP_AUTO2_TD3050 : +_slot_set (btc,CXST_W1 ,30 , tbl_w1,SLOT_ISO ); +_slot_set (btc,CXST_B1 ,200 , tbl_b1,SLOT_MIX ); +_slot_set (btc,CXST_B4 ,50 , tbl_b4,SLOT_MIX ); +break ; +case BTC_CXP_AUTO2_TD3070 : +_slot_set (btc,CXST_W1 ,30 , tbl_w1,SLOT_ISO ); +_slot_set (btc,CXST_B1 ,200 , tbl_b1,SLOT_MIX ); +_slot_set (btc,CXST_B4 ,70 , tbl_b4,SLOT_MIX ); +break ; +case BTC_CXP_AUTO2_TD5050 : +_slot_set (btc,CXST_W1 ,50 , tbl_w1,SLOT_ISO ); +_slot_set (btc,CXST_B1 ,200 , tbl_b1,SLOT_MIX ); +_slot_set (btc,CXST_B4 ,50 , tbl_b4,SLOT_MIX ); +break ; +case BTC_CXP_AUTO2_TD6060 : +_slot_set (btc,CXST_W1 ,60 , tbl_w1,SLOT_ISO ); +_slot_set (btc,CXST_B1 ,200 , tbl_b1,SLOT_MIX ); +_slot_set (btc,CXST_B4 ,60 , tbl_b4,SLOT_MIX ); +break ; +case BTC_CXP_AUTO2_TD2080 : +_slot_set (btc,CXST_W1 ,20 , tbl_w1,SLOT_ISO ); +_slot_set (btc,CXST_B1 ,200 , tbl_b1,SLOT_MIX ); +_slot_set (btc,CXST_B4 ,80 , tbl_b4,SLOT_MIX ); +break ; +case BTC_CXP_AUTO2_TDW1B4 :/* W1:B1 = user-define */ +_slot_set (btc,CXST_W1 , dm->slot_dur[CXST_W1 ], + tbl_w1,SLOT_ISO ); +_slot_set (btc,CXST_B4 , dm->slot_dur[CXST_B4 ], + tbl_b4,SLOT_MIX ); +break ; + } +break ; +case BTC_CXP_PAUTO2 :/* PS-TDMA Auto-Slot2 */ +_write_scbd (rtwdev,BTC_WSCB_TDMA , true); + *t = t_def[CXTD_PAUTO2 ]; +switch (policy_type) { +case BTC_CXP_PAUTO2_TD3050 : +_slot_set (btc,CXST_W1 ,30 , tbl_w1,SLOT_ISO ); +_slot_set (btc,CXST_B1 ,200 , tbl_b1,SLOT_MIX ); +_slot_set (btc,CXST_B4 ,50 , tbl_b4,SLOT_MIX ); +break ; +case BTC_CXP_PAUTO2_TD3070 : +_slot_set (btc,CXST_W1 ,30 , tbl_w1,SLOT_ISO ); +_slot_set (btc,CXST_B1 ,200 , tbl_b1,SLOT_MIX ); +_slot_set (btc,CXST_B4 ,70 , tbl_b4,SLOT_MIX ); +break ; +case BTC_CXP_PAUTO2_TD5050 : +_slot_set (btc,CXST_W1 ,50 , tbl_w1,SLOT_ISO ); +_slot_set (btc,CXST_B1 ,200 , tbl_b1,SLOT_MIX ); +_slot_set (btc,CXST_B4 ,50 , tbl_b4,SLOT_MIX ); +break ; +case BTC_CXP_PAUTO2_TD6060 : +_slot_set (btc,CXST_W1 ,60 , tbl_w1,SLOT_ISO ); +_slot_set (btc,CXST_B1 ,200 , tbl_b1,SLOT_MIX ); +_slot_set (btc,CXST_B4 ,60 , tbl_b4,SLOT_MIX ); +break ; +case BTC_CXP_PAUTO2_TD2080 : +_slot_set (btc,CXST_W1 ,20 , tbl_w1,SLOT_ISO ); +_slot_set (btc,CXST_B1 ,200 , tbl_b1,SLOT_MIX ); +_slot_set (btc,CXST_B4 ,80 , tbl_b4,SLOT_MIX ); +break ; +case BTC_CXP_PAUTO2_TDW1B4 :/* W1:B1 = user-define */ +_slot_set (btc,CXST_W1 , dm->slot_dur[CXST_W1 ], + tbl_w1,SLOT_ISO ); +_slot_set (btc,CXST_B4 , dm->slot_dur[CXST_B4 ], + tbl_b4,SLOT_MIX ); +break ; + } +break ; + } + +_fw_set_policy (rtwdev, policy_type, action); +} + +static void _set_gnt_bt (struct rtw89_dev *rtwdev,u8 phy_map,u8 state) +{ +struct rtw89_btc *btc = &rtwdev->btc; +struct rtw89_btc_dm *dm = &btc->dm; +struct rtw89_mac_ax_gnt *g = dm->gnt.band; +u8 i; + +if (phy_map >BTC_PHY_ALL ) +return ; + +for (i =0 ; i <RTW89_PHY_MAX ; i++) { +if (!(phy_map &BIT (i))) +continue ; + +switch (state) { +case BTC_GNT_HW : + g[i].gnt_bt_sw_en =0 ; + g[i].gnt_bt =0 ; +break ; +case BTC_GNT_SW_LO : + g[i].gnt_bt_sw_en =1 ; + g[i].gnt_bt =0 ; +break ; +case BTC_GNT_SW_HI : + g[i].gnt_bt_sw_en =1 ; + g[i].gnt_bt =1 ; +break ; + } + } + +rtw89_mac_cfg_gnt (rtwdev, &dm->gnt); +} + +static void _set_bt_plut (struct rtw89_dev *rtwdev,u8 phy_map, +u8 tx_val,u8 rx_val) +{ +struct rtw89_mac_ax_plt plt; + + plt.band =RTW89_MAC_0 ; + plt.tx = tx_val; + plt.rx = rx_val; + +if (phy_map &BTC_PHY_0 ) +rtw89_mac_cfg_plt (rtwdev, &plt); + +if (!rtwdev->dbcc_en) +return ; + + plt.band =RTW89_MAC_1 ; +if (phy_map &BTC_PHY_1 ) +rtw89_mac_cfg_plt (rtwdev, &plt); +} + +static void _set_ant (struct rtw89_dev *rtwdev,bool force_exec, +u8 phy_map,u8 type) +{ +struct rtw89_btc *btc = &rtwdev->btc; +struct rtw89_btc_dm *dm = &btc->dm; +struct rtw89_btc_cx *cx = &btc->cx; +struct rtw89_btc_wl_info *wl = &btc->cx.wl; +struct rtw89_btc_bt_info *bt = &cx->bt; +struct rtw89_btc_wl_dbcc_info *wl_dinfo = &wl->dbcc_info; +u8 gnt_wl_ctrl, gnt_bt_ctrl, plt_ctrl, i, b2g =0 ; +u32 ant_path_type; + + ant_path_type = ((phy_map <<8 ) + type); + +if (btc->dm.run_reason ==BTC_RSN_NTFY_POWEROFF || + btc->dm.run_reason ==BTC_RSN_NTFY_RADIO_STATE || + btc->dm.run_reason ==BTC_RSN_CMD_SET_COEX ) + force_exec =FC_EXEC ; + +if (!force_exec && ant_path_type == dm->set_ant_path) { +rtw89_debug (rtwdev,RTW89_DBG_BTC , +"[BTC], %s(): return by no change!!\n" , + __func__); +return ; + }else if (bt->rfk_info.map.run) { +rtw89_debug (rtwdev,RTW89_DBG_BTC , +"[BTC], %s(): return by bt rfk!!\n" , __func__); +return ; + }else if (btc->dm.run_reason !=BTC_RSN_NTFY_WL_RFK && + wl->rfk_info.state !=BTC_WRFK_STOP ) { +rtw89_debug (rtwdev,RTW89_DBG_BTC , +"[BTC], %s(): return by wl rfk!!\n" , __func__); +return ; + } + + dm->set_ant_path = ant_path_type; + +rtw89_debug (rtwdev, +RTW89_DBG_BTC , +"[BTC], %s(): path=0x%x, set_type=0x%x\n" , + __func__, phy_map, dm->set_ant_path &0xff ); + +switch (type) { +case BTC_ANT_WPOWERON : +rtw89_mac_cfg_ctrl_path (rtwdev, false); +break ; +case BTC_ANT_WINIT : +if (bt->enable.now) { +_set_gnt_wl (rtwdev, phy_map,BTC_GNT_SW_LO ); +_set_gnt_bt (rtwdev, phy_map,BTC_GNT_SW_HI ); + }else { +_set_gnt_wl (rtwdev, phy_map,BTC_GNT_SW_HI ); +_set_gnt_bt (rtwdev, phy_map,BTC_GNT_SW_LO ); + } +rtw89_mac_cfg_ctrl_path (rtwdev, true); +_set_bt_plut (rtwdev,BTC_PHY_ALL ,BTC_PLT_BT ,BTC_PLT_BT ); +break ; +case BTC_ANT_WONLY : +_set_gnt_wl (rtwdev, phy_map,BTC_GNT_SW_HI ); +_set_gnt_bt (rtwdev, phy_map,BTC_GNT_SW_LO ); +rtw89_mac_cfg_ctrl_path (rtwdev, true); +_set_bt_plut (rtwdev,BTC_PHY_ALL ,BTC_PLT_NONE ,BTC_PLT_NONE ); +break ; +case BTC_ANT_WOFF : +rtw89_mac_cfg_ctrl_path (rtwdev, false); +_set_bt_plut (rtwdev,BTC_PHY_ALL ,BTC_PLT_NONE ,BTC_PLT_NONE ); +break ; +case BTC_ANT_W2G : +rtw89_mac_cfg_ctrl_path (rtwdev, true); +if (rtwdev->dbcc_en) { +for (i =0 ; i <RTW89_PHY_MAX ; i++) { + b2g = (wl_dinfo->real_band[i] ==RTW89_BAND_2G ); + + gnt_wl_ctrl = b2g ?BTC_GNT_HW :BTC_GNT_SW_HI ; +_set_gnt_wl (rtwdev,BIT (i), gnt_wl_ctrl); + + gnt_bt_ctrl = b2g ?BTC_GNT_HW :BTC_GNT_SW_HI ; +/* BT should control by GNT_BT if WL_2G at S0 */ +if (i ==1 && + wl_dinfo->real_band[0 ] ==RTW89_BAND_2G && + wl_dinfo->real_band[1 ] ==RTW89_BAND_5G ) + gnt_bt_ctrl =BTC_GNT_HW ; +_set_gnt_bt (rtwdev,BIT (i), gnt_bt_ctrl); + + plt_ctrl = b2g ?BTC_PLT_BT :BTC_PLT_NONE ; +_set_bt_plut (rtwdev,BIT (i), + plt_ctrl, plt_ctrl); + } + }else { +_set_gnt_wl (rtwdev, phy_map,BTC_GNT_HW ); +_set_gnt_bt (rtwdev, phy_map,BTC_GNT_HW ); +_set_bt_plut (rtwdev,BTC_PHY_ALL , +BTC_PLT_BT ,BTC_PLT_BT ); + } +break ; +case BTC_ANT_W5G : +rtw89_mac_cfg_ctrl_path (rtwdev, true); +_set_gnt_wl (rtwdev, phy_map,BTC_GNT_SW_HI ); +_set_gnt_bt (rtwdev, phy_map,BTC_GNT_HW ); +_set_bt_plut (rtwdev,BTC_PHY_ALL ,BTC_PLT_NONE ,BTC_PLT_NONE ); +break ; +case BTC_ANT_W25G : +rtw89_mac_cfg_ctrl_path (rtwdev, true); +_set_gnt_wl (rtwdev, phy_map,BTC_GNT_HW ); +_set_gnt_bt (rtwdev, phy_map,BTC_GNT_HW ); +_set_bt_plut (rtwdev,BTC_PHY_ALL , +BTC_PLT_GNT_WL ,BTC_PLT_GNT_WL ); +break ; +case BTC_ANT_FREERUN : +rtw89_mac_cfg_ctrl_path (rtwdev, true); +_set_gnt_wl (rtwdev, phy_map,BTC_GNT_SW_HI ); +_set_gnt_bt (rtwdev, phy_map,BTC_GNT_SW_HI ); +_set_bt_plut (rtwdev,BTC_PHY_ALL ,BTC_PLT_NONE ,BTC_PLT_NONE ); +break ; +case BTC_ANT_WRFK : +rtw89_mac_cfg_ctrl_path (rtwdev, true); +_set_gnt_wl (rtwdev, phy_map,BTC_GNT_SW_HI ); +_set_gnt_bt (rtwdev, phy_map,BTC_GNT_SW_LO ); +_set_bt_plut (rtwdev, phy_map,BTC_PLT_NONE ,BTC_PLT_NONE ); +break ; +case BTC_ANT_BRFK : +rtw89_mac_cfg_ctrl_path (rtwdev, false); +_set_gnt_wl (rtwdev, phy_map,BTC_GNT_SW_LO ); +_set_gnt_bt (rtwdev, phy_map,BTC_GNT_SW_HI ); +_set_bt_plut (rtwdev, phy_map,BTC_PLT_NONE ,BTC_PLT_NONE ); +break ; +default : +break ; + } +} + +static void _action_wl_only (struct rtw89_dev *rtwdev) +{ +_set_ant (rtwdev,FC_EXEC ,BTC_PHY_ALL ,BTC_ANT_WONLY ); +_set_policy (rtwdev,BTC_CXP_OFF_BT ,BTC_ACT_WL_ONLY ); +} + +static void _action_wl_init (struct rtw89_dev *rtwdev) +{ +rtw89_debug (rtwdev,RTW89_DBG_BTC ,"[BTC], %s(): !!\n" , __func__); + +_set_ant (rtwdev,FC_EXEC ,BTC_PHY_ALL ,BTC_ANT_WINIT ); +_set_policy (rtwdev,BTC_CXP_OFF_BT ,BTC_ACT_WL_INIT ); +} + +static void _action_wl_off (struct rtw89_dev *rtwdev) +{ +struct rtw89_btc *btc = &rtwdev->btc; +struct rtw89_btc_wl_info *wl = &btc->cx.wl; + +rtw89_debug (rtwdev,RTW89_DBG_BTC ,"[BTC], %s(): !!\n" , __func__); + +if (wl->status.map.rf_off || btc->dm.bt_only) +_set_ant (rtwdev,NM_EXEC ,BTC_PHY_ALL ,BTC_ANT_WOFF ); + +_set_policy (rtwdev,BTC_CXP_OFF_BT ,BTC_ACT_WL_OFF ); +} + +static void _action_freerun (struct rtw89_dev *rtwdev) +{ +struct rtw89_btc *btc = &rtwdev->btc; + +rtw89_debug (rtwdev,RTW89_DBG_BTC ,"[BTC], %s(): !!\n" , __func__); + +_set_ant (rtwdev,FC_EXEC ,BTC_PHY_ALL ,BTC_ANT_FREERUN ); +_set_policy (rtwdev,BTC_CXP_OFF_BT ,BTC_ACT_FREERUN ); + + btc->dm.freerun = true; +} + +static void _action_bt_whql (struct rtw89_dev *rtwdev) +{ +rtw89_debug (rtwdev,RTW89_DBG_BTC ,"[BTC], %s(): !!\n" , __func__); + +_set_ant (rtwdev,FC_EXEC ,BTC_PHY_ALL ,BTC_ANT_W2G ); +_set_policy (rtwdev,BTC_CXP_OFF_BT ,BTC_ACT_BT_WHQL ); +} + +static void _action_bt_off (struct rtw89_dev *rtwdev) +{ +rtw89_debug (rtwdev,RTW89_DBG_BTC ,"[BTC], %s(): !!\n" , __func__); + +_set_ant (rtwdev,FC_EXEC ,BTC_PHY_ALL ,BTC_ANT_WONLY ); +_set_policy (rtwdev,BTC_CXP_OFF_BT ,BTC_ACT_BT_OFF ); +} + +static void _action_bt_idle (struct rtw89_dev *rtwdev) +{ +struct rtw89_btc *btc = &rtwdev->btc; +struct rtw89_btc_bt_link_info *b = &btc->cx.bt.link_info; + +_set_ant (rtwdev,NM_EXEC ,BTC_PHY_ALL ,BTC_ANT_W2G ); + +if (btc->mdinfo.ant.type ==BTC_ANT_SHARED ) {/* shared-antenna */ +switch (btc->cx.state_map) { +case BTC_WBUSY_BNOSCAN :/*wl-busy + bt idle*/ +if (b->profile_cnt.now >0 ) +_set_policy (rtwdev,BTC_CXP_FIX_TD4010 , +BTC_ACT_BT_IDLE ); +else +_set_policy (rtwdev,BTC_CXP_FIX_TD4020 , +BTC_ACT_BT_IDLE ); +break ; +case BTC_WBUSY_BSCAN :/*wl-busy + bt-inq */ +_set_policy (rtwdev,BTC_CXP_PFIX_TD5050 , +BTC_ACT_BT_IDLE ); +break ; +case BTC_WSCAN_BNOSCAN :/* wl-scan + bt-idle */ +if (b->profile_cnt.now >0 ) +_set_policy (rtwdev,BTC_CXP_FIX_TD4010 , +BTC_ACT_BT_IDLE ); +else +_set_policy (rtwdev,BTC_CXP_FIX_TD4020 , +BTC_ACT_BT_IDLE ); +break ; +case BTC_WSCAN_BSCAN :/* wl-scan + bt-inq */ +_set_policy (rtwdev,BTC_CXP_FIX_TD5050 , +BTC_ACT_BT_IDLE ); +break ; +case BTC_WLINKING :/* wl-connecting + bt-inq or bt-idle */ +_set_policy (rtwdev,BTC_CXP_FIX_TD7010 , +BTC_ACT_BT_IDLE ); +break ; +case BTC_WIDLE :/* wl-idle + bt-idle */ +_set_policy (rtwdev,BTC_CXP_OFF_BWB1 ,BTC_ACT_BT_IDLE ); +break ; + } + }else {/* dedicated-antenna */ +_set_policy (rtwdev,BTC_CXP_OFF_EQ0 ,BTC_ACT_BT_IDLE ); + } +} + +static void _action_bt_hfp (struct rtw89_dev *rtwdev) +{ +struct rtw89_btc *btc = &rtwdev->btc; + +_set_ant (rtwdev,NM_EXEC ,BTC_PHY_ALL ,BTC_ANT_W2G ); + +if (btc->mdinfo.ant.type ==BTC_ANT_SHARED ) { +if (btc->cx.wl.status.map._4way) +_set_policy (rtwdev,BTC_CXP_OFF_WL ,BTC_ACT_BT_HFP ); +else +_set_policy (rtwdev,BTC_CXP_OFF_BWB0 ,BTC_ACT_BT_HFP ); + }else { +_set_policy (rtwdev,BTC_CXP_OFF_EQ2 ,BTC_ACT_BT_HFP ); + } +} + +static void _action_bt_hid (struct rtw89_dev *rtwdev) +{ +struct rtw89_btc *btc = &rtwdev->btc; + +_set_ant (rtwdev,NM_EXEC ,BTC_PHY_ALL ,BTC_ANT_W2G ); + +if (btc->mdinfo.ant.type ==BTC_ANT_SHARED )/* shared-antenna */ +if (btc->cx.wl.status.map._4way) +_set_policy (rtwdev,BTC_CXP_OFF_WL ,BTC_ACT_BT_HID ); +else +_set_policy (rtwdev,BTC_CXP_OFF_BWB0 ,BTC_ACT_BT_HID ); +else /* dedicated-antenna */ +_set_policy (rtwdev,BTC_CXP_OFF_EQ3 ,BTC_ACT_BT_HID ); +} + +static void _action_bt_a2dp (struct rtw89_dev *rtwdev) +{ +struct rtw89_btc *btc = &rtwdev->btc; +struct rtw89_btc_bt_link_info *bt_linfo = &btc->cx.bt.link_info; +struct rtw89_btc_bt_a2dp_desc a2dp = bt_linfo->a2dp_desc; +struct rtw89_btc_dm *dm = &btc->dm; + +_set_ant (rtwdev,NM_EXEC ,BTC_PHY_ALL ,BTC_ANT_W2G ); + +switch (btc->cx.state_map) { +case BTC_WBUSY_BNOSCAN :/* wl-busy + bt-A2DP */ +if (a2dp.vendor_id ==0x4c || dm->leak_ap) { + dm->slot_dur[CXST_W1 ] =40 ; + dm->slot_dur[CXST_B1 ] =200 ; +_set_policy (rtwdev, +BTC_CXP_PAUTO_TDW1B1 ,BTC_ACT_BT_A2DP ); + }else { +_set_policy (rtwdev, +BTC_CXP_PAUTO_TD50200 ,BTC_ACT_BT_A2DP ); + } +break ; +case BTC_WBUSY_BSCAN :/* wl-busy + bt-inq + bt-A2DP */ +_set_policy (rtwdev,BTC_CXP_PAUTO2_TD3050 ,BTC_ACT_BT_A2DP ); +break ; +case BTC_WSCAN_BSCAN :/* wl-scan + bt-inq + bt-A2DP */ +_set_policy (rtwdev,BTC_CXP_AUTO2_TD3050 ,BTC_ACT_BT_A2DP ); +break ; +case BTC_WSCAN_BNOSCAN :/* wl-scan + bt-A2DP */ +case BTC_WLINKING :/* wl-connecting + bt-A2DP */ +if (a2dp.vendor_id ==0x4c || dm->leak_ap) { + dm->slot_dur[CXST_W1 ] =40 ; + dm->slot_dur[CXST_B1 ] =200 ; +_set_policy (rtwdev,BTC_CXP_AUTO_TDW1B1 , +BTC_ACT_BT_A2DP ); + }else { +_set_policy (rtwdev,BTC_CXP_AUTO_TD50200 , +BTC_ACT_BT_A2DP ); + } +break ; +case BTC_WIDLE :/* wl-idle + bt-A2DP */ +_set_policy (rtwdev,BTC_CXP_AUTO_TD20200 ,BTC_ACT_BT_A2DP ); +break ; + } +} + +static void _action_bt_a2dpsink (struct rtw89_dev *rtwdev) +{ +struct rtw89_btc *btc = &rtwdev->btc; + +_set_ant (rtwdev,NM_EXEC ,BTC_PHY_ALL ,BTC_ANT_W2G ); + +switch (btc->cx.state_map) { +case BTC_WBUSY_BNOSCAN :/* wl-busy + bt-A2dp_Sink */ +_set_policy (rtwdev,BTC_CXP_PFIX_TD2030 ,BTC_ACT_BT_A2DPSINK ); +break ; +case BTC_WBUSY_BSCAN :/* wl-busy + bt-inq + bt-A2dp_Sink */ +_set_policy (rtwdev,BTC_CXP_PFIX_TD2060 ,BTC_ACT_BT_A2DPSINK ); +break ; +case BTC_WSCAN_BNOSCAN :/* wl-scan + bt-A2dp_Sink */ +_set_policy (rtwdev,BTC_CXP_FIX_TD2030 ,BTC_ACT_BT_A2DPSINK ); +break ; +case BTC_WSCAN_BSCAN :/* wl-scan + bt-inq + bt-A2dp_Sink */ +_set_policy (rtwdev,BTC_CXP_FIX_TD2060 ,BTC_ACT_BT_A2DPSINK ); +break ; +case BTC_WLINKING :/* wl-connecting + bt-A2dp_Sink */ +_set_policy (rtwdev,BTC_CXP_FIX_TD3030 ,BTC_ACT_BT_A2DPSINK ); +break ; +case BTC_WIDLE :/* wl-idle + bt-A2dp_Sink */ +_set_policy (rtwdev,BTC_CXP_FIX_TD2080 ,BTC_ACT_BT_A2DPSINK ); +break ; + } +} + +static void _action_bt_pan (struct rtw89_dev *rtwdev) +{ +struct rtw89_btc *btc = &rtwdev->btc; + +_set_ant (rtwdev,NM_EXEC ,BTC_PHY_ALL ,BTC_ANT_W2G ); + +switch (btc->cx.state_map) { +case BTC_WBUSY_BNOSCAN :/* wl-busy + bt-PAN */ +_set_policy (rtwdev,BTC_CXP_PFIX_TD5050 ,BTC_ACT_BT_PAN ); +break ; +case BTC_WBUSY_BSCAN :/* wl-busy + bt-inq + bt-PAN */ +_set_policy (rtwdev,BTC_CXP_PFIX_TD3070 ,BTC_ACT_BT_PAN ); +break ; +case BTC_WSCAN_BNOSCAN :/* wl-scan + bt-PAN */ +_set_policy (rtwdev,BTC_CXP_FIX_TD3030 ,BTC_ACT_BT_PAN ); +break ; +case BTC_WSCAN_BSCAN :/* wl-scan + bt-inq + bt-PAN */ +_set_policy (rtwdev,BTC_CXP_FIX_TD3060 ,BTC_ACT_BT_PAN ); +break ; +case BTC_WLINKING :/* wl-connecting + bt-PAN */ +_set_policy (rtwdev,BTC_CXP_FIX_TD4020 ,BTC_ACT_BT_PAN ); +break ; +case BTC_WIDLE :/* wl-idle + bt-pan */ +_set_policy (rtwdev,BTC_CXP_PFIX_TD2080 ,BTC_ACT_BT_PAN ); +break ; + } +} + +static void _action_bt_a2dp_hid (struct rtw89_dev *rtwdev) +{ +struct rtw89_btc *btc = &rtwdev->btc; +struct rtw89_btc_bt_link_info *bt_linfo = &btc->cx.bt.link_info; +struct rtw89_btc_bt_a2dp_desc a2dp = bt_linfo->a2dp_desc; +struct rtw89_btc_dm *dm = &btc->dm; + +_set_ant (rtwdev,NM_EXEC ,BTC_PHY_ALL ,BTC_ANT_W2G ); + +switch (btc->cx.state_map) { +case BTC_WBUSY_BNOSCAN :/* wl-busy + bt-A2DP+HID */ +case BTC_WIDLE :/* wl-idle + bt-A2DP */ +if (a2dp.vendor_id ==0x4c || dm->leak_ap) { + dm->slot_dur[CXST_W1 ] =40 ; + dm->slot_dur[CXST_B1 ] =200 ; +_set_policy (rtwdev, +BTC_CXP_PAUTO_TDW1B1 ,BTC_ACT_BT_A2DP_HID ); + }else { +_set_policy (rtwdev, +BTC_CXP_PAUTO_TD50200 ,BTC_ACT_BT_A2DP_HID ); + } +break ; +case BTC_WBUSY_BSCAN :/* wl-busy + bt-inq + bt-A2DP+HID */ +_set_policy (rtwdev,BTC_CXP_PAUTO2_TD3050 ,BTC_ACT_BT_A2DP_HID ); +break ; + +case BTC_WSCAN_BSCAN :/* wl-scan + bt-inq + bt-A2DP+HID */ +_set_policy (rtwdev,BTC_CXP_AUTO2_TD3050 ,BTC_ACT_BT_A2DP_HID ); +break ; +case BTC_WSCAN_BNOSCAN :/* wl-scan + bt-A2DP+HID */ +case BTC_WLINKING :/* wl-connecting + bt-A2DP+HID */ +if (a2dp.vendor_id ==0x4c || dm->leak_ap) { + dm->slot_dur[CXST_W1 ] =40 ; + dm->slot_dur[CXST_B1 ] =200 ; +_set_policy (rtwdev,BTC_CXP_AUTO_TDW1B1 , +BTC_ACT_BT_A2DP_HID ); + }else { +_set_policy (rtwdev,BTC_CXP_AUTO_TD50200 , +BTC_ACT_BT_A2DP_HID ); + } +break ; + } +} + +static void _action_bt_a2dp_pan (struct rtw89_dev *rtwdev) +{ +struct rtw89_btc *btc = &rtwdev->btc; + +_set_ant (rtwdev,NM_EXEC ,BTC_PHY_ALL ,BTC_ANT_W2G ); + +switch (btc->cx.state_map) { +case BTC_WBUSY_BNOSCAN :/* wl-busy + bt-A2DP+PAN */ +_set_policy (rtwdev,BTC_CXP_PAUTO2_TD3070 ,BTC_ACT_BT_A2DP_PAN ); +break ; +case BTC_WBUSY_BSCAN :/* wl-busy + bt-inq + bt-A2DP+PAN */ +_set_policy (rtwdev,BTC_CXP_PAUTO2_TD3070 ,BTC_ACT_BT_A2DP_PAN ); +break ; +case BTC_WSCAN_BNOSCAN :/* wl-scan + bt-A2DP+PAN */ +_set_policy (rtwdev,BTC_CXP_AUTO2_TD5050 ,BTC_ACT_BT_A2DP_PAN ); +break ; +case BTC_WSCAN_BSCAN :/* wl-scan + bt-inq + bt-A2DP+PAN */ +_set_policy (rtwdev,BTC_CXP_AUTO2_TD3070 ,BTC_ACT_BT_A2DP_PAN ); +break ; +case BTC_WLINKING :/* wl-connecting + bt-A2DP+PAN */ +_set_policy (rtwdev,BTC_CXP_AUTO2_TD3050 ,BTC_ACT_BT_A2DP_PAN ); +break ; +case BTC_WIDLE :/* wl-idle + bt-A2DP+PAN */ +_set_policy (rtwdev,BTC_CXP_PAUTO2_TD2080 ,BTC_ACT_BT_A2DP_PAN ); +break ; + } +} + +static void _action_bt_pan_hid (struct rtw89_dev *rtwdev) +{ +struct rtw89_btc *btc = &rtwdev->btc; + +_set_ant (rtwdev,NM_EXEC ,BTC_PHY_ALL ,BTC_ANT_W2G ); + +switch (btc->cx.state_map) { +case BTC_WBUSY_BNOSCAN :/* wl-busy + bt-PAN+HID */ +_set_policy (rtwdev,BTC_CXP_PFIX_TD3030 ,BTC_ACT_BT_PAN_HID ); +break ; +case BTC_WBUSY_BSCAN :/* wl-busy + bt-inq + bt-PAN+HID */ +_set_policy (rtwdev,BTC_CXP_PFIX_TD3070 ,BTC_ACT_BT_PAN_HID ); +break ; +case BTC_WSCAN_BNOSCAN :/* wl-scan + bt-PAN+HID */ +_set_policy (rtwdev,BTC_CXP_FIX_TD3030 ,BTC_ACT_BT_PAN_HID ); +break ; +case BTC_WSCAN_BSCAN :/* wl-scan + bt-inq + bt-PAN+HID */ +_set_policy (rtwdev,BTC_CXP_FIX_TD3060 ,BTC_ACT_BT_PAN_HID ); +break ; +case BTC_WLINKING :/* wl-connecting + bt-PAN+HID */ +_set_policy (rtwdev,BTC_CXP_FIX_TD4010 ,BTC_ACT_BT_PAN_HID ); +break ; +case BTC_WIDLE :/* wl-idle + bt-PAN+HID */ +_set_policy (rtwdev,BTC_CXP_PFIX_TD2080 ,BTC_ACT_BT_PAN_HID ); +break ; + } +} + +static void _action_bt_a2dp_pan_hid (struct rtw89_dev *rtwdev) +{ +struct rtw89_btc *btc = &rtwdev->btc; + +_set_ant (rtwdev,NM_EXEC ,BTC_PHY_ALL ,BTC_ANT_W2G ); + +switch (btc->cx.state_map) { +case BTC_WBUSY_BNOSCAN :/* wl-busy + bt-A2DP+PAN+HID */ +_set_policy (rtwdev,BTC_CXP_PAUTO2_TD3070 , +BTC_ACT_BT_A2DP_PAN_HID ); +break ; +case BTC_WBUSY_BSCAN :/* wl-busy + bt-inq + bt-A2DP+PAN+HID */ +_set_policy (rtwdev,BTC_CXP_PAUTO2_TD3070 , +BTC_ACT_BT_A2DP_PAN_HID ); +break ; +case BTC_WSCAN_BSCAN :/* wl-scan + bt-inq + bt-A2DP+PAN+HID */ +_set_policy (rtwdev,BTC_CXP_AUTO2_TD3070 , +BTC_ACT_BT_A2DP_PAN_HID ); +break ; +case BTC_WSCAN_BNOSCAN :/* wl-scan + bt-A2DP+PAN+HID */ +case BTC_WLINKING :/* wl-connecting + bt-A2DP+PAN+HID */ +_set_policy (rtwdev,BTC_CXP_AUTO2_TD3050 , +BTC_ACT_BT_A2DP_PAN_HID ); +break ; +case BTC_WIDLE :/* wl-idle + bt-A2DP+PAN+HID */ +_set_policy (rtwdev,BTC_CXP_PAUTO2_TD2080 , +BTC_ACT_BT_A2DP_PAN_HID ); +break ; + } +} + +static void _action_wl_5g (struct rtw89_dev *rtwdev) +{ +_set_ant (rtwdev,NM_EXEC ,BTC_PHY_ALL ,BTC_ANT_W5G ); +_set_policy (rtwdev,BTC_CXP_OFF_EQ0 ,BTC_ACT_WL_5G ); +} + +static void _action_wl_other (struct rtw89_dev *rtwdev) +{ +struct rtw89_btc *btc = &rtwdev->btc; + +_set_ant (rtwdev,NM_EXEC ,BTC_PHY_ALL ,BTC_ANT_W2G ); + +if (btc->mdinfo.ant.type ==BTC_ANT_SHARED ) +_set_policy (rtwdev,BTC_CXP_OFFB_BWB0 ,BTC_ACT_WL_OTHER ); +else +_set_policy (rtwdev,BTC_CXP_OFF_EQ0 ,BTC_ACT_WL_OTHER ); +} + +static void _action_wl_nc (struct rtw89_dev *rtwdev) +{ +_set_ant (rtwdev,NM_EXEC ,BTC_PHY_ALL ,BTC_ANT_W2G ); +_set_policy (rtwdev,BTC_CXP_OFF_BT ,BTC_ACT_WL_NC ); +} + +static void _action_wl_rfk (struct rtw89_dev *rtwdev) +{ +struct rtw89_btc *btc = &rtwdev->btc; +struct rtw89_btc_wl_rfk_info rfk = btc->cx.wl.rfk_info; + +if (rfk.state !=BTC_WRFK_START ) +return ; + +rtw89_debug (rtwdev,RTW89_DBG_BTC ,"[BTC], %s(): band = %d\n" , + __func__, rfk.band); + +_set_ant (rtwdev,FC_EXEC ,BTC_PHY_ALL ,BTC_ANT_WRFK ); +_set_policy (rtwdev,BTC_CXP_OFF_WL ,BTC_ACT_WL_RFK ); +} + +static void _set_btg_ctrl (struct rtw89_dev *rtwdev) +{ +struct rtw89_btc *btc = &rtwdev->btc; +struct rtw89_btc_wl_info *wl = &btc->cx.wl; +struct rtw89_btc_wl_role_info *wl_rinfo = &wl->role_info; +struct rtw89_btc_wl_dbcc_info *wl_dinfo = &wl->dbcc_info; +bool is_btg = false; + +if (btc->ctrl.manual) +return ; + +/* notify halbb ignore GNT_BT or not for WL BB Rx-AGC control */ +if (wl_rinfo->link_mode ==BTC_WLINK_5G )/* always 0 if 5G */ + is_btg = false; +else if (wl_rinfo->link_mode ==BTC_WLINK_25G_DBCC && + wl_dinfo->real_band[RTW89_PHY_1 ] !=RTW89_BAND_2G ) + is_btg = false; +else + is_btg = true; + +if (btc->dm.run_reason !=BTC_RSN_NTFY_INIT && + is_btg == btc->dm.wl_btg_rx) +return ; + + btc->dm.wl_btg_rx = is_btg; + +if (wl_rinfo->link_mode ==BTC_WLINK_25G_MCC ) +return ; + +rtw89_ctrl_btg (rtwdev, is_btg); +} + +struct rtw89_txtime_data { +struct rtw89_dev *rtwdev; +int type; +u32 tx_time; +u8 tx_retry; +u16 enable; +bool reenable; +}; + +static void rtw89_tx_time_iter (void *data,struct ieee80211_sta *sta) +{ +struct rtw89_sta *rtwsta = (struct rtw89_sta *)sta->drv_priv; +struct rtw89_txtime_data *iter_data = + (struct rtw89_txtime_data *)data; +struct rtw89_dev *rtwdev = iter_data->rtwdev; +struct rtw89_vif *rtwvif = rtwsta->rtwvif; +struct rtw89_btc *btc = &rtwdev->btc; +struct rtw89_btc_cx *cx = &btc->cx; +struct rtw89_btc_wl_info *wl = &cx->wl; +struct rtw89_btc_wl_link_info *plink =NULL ; +u8 port = rtwvif->port; +u32 tx_time = iter_data->tx_time; +u8 tx_retry = iter_data->tx_retry; +u16 enable = iter_data->enable; +bool reenable = iter_data->reenable; + + plink = &wl->link_info[port]; + +rtw89_debug (rtwdev,RTW89_DBG_BTC , +"[BTC], %s(): port = %d\n" , __func__, port); + +if (!plink->connected) { +rtw89_debug (rtwdev,RTW89_DBG_BTC , +"[BTC], %s(): connected = %d\n" , + __func__, plink->connected); +return ; + } + +/* backup the original tx time before tx-limit on */ +if (reenable) { +rtw89_mac_get_tx_time (rtwdev, rtwsta, &plink->tx_time); +rtw89_mac_get_tx_retry_limit (rtwdev, rtwsta, &plink->tx_retry); +rtw89_debug (rtwdev,RTW89_DBG_BTC , +"[BTC], %s(): reenable, tx_time=%d tx_retry= %d\n" , + __func__, plink->tx_time, plink->tx_retry); + } + +/* restore the original tx time if no tx-limit */ +if (!enable) { +rtw89_mac_set_tx_time (rtwdev, rtwsta, true, plink->tx_time); +rtw89_mac_set_tx_retry_limit (rtwdev, rtwsta, true, + plink->tx_retry); +rtw89_debug (rtwdev,RTW89_DBG_BTC , +"[BTC], %s(): restore, tx_time=%d tx_retry= %d\n" , + __func__, plink->tx_time, plink->tx_retry); + + }else { +rtw89_mac_set_tx_time (rtwdev, rtwsta, false, tx_time); +rtw89_mac_set_tx_retry_limit (rtwdev, rtwsta, false, tx_retry); +rtw89_debug (rtwdev,RTW89_DBG_BTC , +"[BTC], %s(): set, tx_time=%d tx_retry= %d\n" , + __func__, tx_time, tx_retry); + } +} + +static void _set_wl_tx_limit (struct rtw89_dev *rtwdev) +{ +struct rtw89_btc *btc = &rtwdev->btc; +struct rtw89_btc_cx *cx = &btc->cx; +struct rtw89_btc_dm *dm = &btc->dm; +struct rtw89_btc_wl_info *wl = &cx->wl; +struct rtw89_btc_bt_info *bt = &cx->bt; +struct rtw89_btc_bt_link_info *b = &bt->link_info; +struct rtw89_btc_bt_hfp_desc *hfp = &b->hfp_desc; +struct rtw89_btc_bt_hid_desc *hid = &b->hid_desc; +struct rtw89_btc_wl_role_info *wl_rinfo = &wl->role_info; +struct rtw89_txtime_data data = {.rtwdev = rtwdev}; +u8 mode = wl_rinfo->link_mode; +u8 tx_retry =0 ; +u32 tx_time =0 ; +u16 enable =0 ; +bool reenable = false; + +if (btc->ctrl.manual) +return ; + +if (btc->dm.freerun || btc->ctrl.igno_bt || b->profile_cnt.now ==0 || + mode ==BTC_WLINK_5G || mode ==BTC_WLINK_NOLINK ) { + enable =0 ; + tx_time =BTC_MAX_TX_TIME_DEF ; + tx_retry =BTC_MAX_TX_RETRY_DEF ; + }else if ((hfp->exist && hid->exist) || hid->pair_cnt >1 ) { + enable =1 ; + tx_time =BTC_MAX_TX_TIME_L2 ; + tx_retry =BTC_MAX_TX_RETRY_L1 ; + }else if (hfp->exist || hid->exist) { + enable =1 ; + tx_time =BTC_MAX_TX_TIME_L3 ; + tx_retry =BTC_MAX_TX_RETRY_L1 ; + }else { + enable =0 ; + tx_time =BTC_MAX_TX_TIME_DEF ; + tx_retry =BTC_MAX_TX_RETRY_DEF ; + } + +if (dm->wl_tx_limit.enable == enable && + dm->wl_tx_limit.tx_time == tx_time && + dm->wl_tx_limit.tx_retry == tx_retry) +return ; + +if (!dm->wl_tx_limit.enable && enable) + reenable = true; + + dm->wl_tx_limit.enable = enable; + dm->wl_tx_limit.tx_time = tx_time; + dm->wl_tx_limit.tx_retry = tx_retry; + + data.enable = enable; + data.tx_time = tx_time; + data.tx_retry = tx_retry; + data.reenable = reenable; + +ieee80211_iterate_stations_atomic (rtwdev->hw, + rtw89_tx_time_iter, + &data); +} + +#define _set_bt_slot_req (rtwdev) do {} while (0) + +static void _set_bt_rx_agc (struct rtw89_dev *rtwdev) +{ +struct rtw89_btc *btc = &rtwdev->btc; +struct rtw89_btc_wl_info *wl = &btc->cx.wl; +struct rtw89_btc_wl_role_info *wl_rinfo = &wl->role_info; +struct rtw89_btc_bt_info *bt = &btc->cx.bt; +bool bt_hi_lna_rx = false; + +if (wl_rinfo->link_mode !=BTC_WLINK_NOLINK && btc->dm.wl_btg_rx) + bt_hi_lna_rx = true; + +if (bt_hi_lna_rx == bt->hi_lna_rx) +return ; + +_write_scbd (rtwdev,BTC_WSCB_BT_HILNA , bt_hi_lna_rx); +} + +/* TODO add these functions */ +static void _action_common (struct rtw89_dev *rtwdev) +{ +_set_btg_ctrl (rtwdev); +_set_wl_tx_limit (rtwdev); +_set_bt_afh_info (rtwdev); +_set_bt_rx_agc (rtwdev); +_set_bt_slot_req (rtwdev); +_set_rf_trx_para (rtwdev); +} + +static void _action_by_bt (struct rtw89_dev *rtwdev) +{ +struct rtw89_btc *btc = &rtwdev->btc; +struct rtw89_btc_bt_info *bt = &btc->cx.bt; +struct rtw89_btc_bt_link_info *bt_linfo = &bt->link_info; +struct rtw89_btc_bt_hid_desc hid = bt_linfo->hid_desc; +struct rtw89_btc_bt_a2dp_desc a2dp = bt_linfo->a2dp_desc; +struct rtw89_btc_bt_pan_desc pan = bt_linfo->pan_desc; +u8 profile_map =0 ; + +if (bt_linfo->hfp_desc.exist) + profile_map |=BTC_BT_HFP ; + +if (bt_linfo->hid_desc.exist) + profile_map |=BTC_BT_HID ; + +if (bt_linfo->a2dp_desc.exist) + profile_map |=BTC_BT_A2DP ; + +if (bt_linfo->pan_desc.exist) + profile_map |=BTC_BT_PAN ; + +switch (profile_map) { +case BTC_BT_NOPROFILE : +if (_check_freerun (rtwdev)) +_action_freerun (rtwdev); +else if (a2dp.active || pan.active) +_action_bt_pan (rtwdev); +else +_action_bt_idle (rtwdev); +break ; +case BTC_BT_HFP : +if (_check_freerun (rtwdev)) +_action_freerun (rtwdev); +else +_action_bt_hfp (rtwdev); +break ; +case BTC_BT_HFP |BTC_BT_HID : +case BTC_BT_HID : +if (_check_freerun (rtwdev)) +_action_freerun (rtwdev); +else +_action_bt_hid (rtwdev); +break ; +case BTC_BT_A2DP : +if (_check_freerun (rtwdev))
Lines of this file are not shown. View the raw message.
drivers/net/wireless/realtek/rtw89/coex.h new file +179 -0
179 lines not on this page · raw message
The rest of this diff is not shown. View the raw message.
Signature · 1 line
2.25.1
Part of the diff is not shown here. View the raw message.
Re: [PATCH v6 02/24] rtw89: add BT coexistence files
Quoted 5 lines · Ping-Ke Shih
BT coexistence uses TDMA-based mechanism to coordinate with WiFi and BT. Now, we implement basic coexistence features for wide use cases, such as HID and A2DP. More will be supported later.
Signed-off-by Ping-Ke Shih <[email protected]>
A small tip for future drivers, try to remove all the optional features from the driver as much possible and keep only the absolutely needed features to get ping working. For example this file was pain to review and I suspect coex support could have been submitted separately.
But don't change anything for this driver, this comment is for rtw90 :)
Quoted 17 lines
drivers/net/wireless/realtek/rtw89/coex.c new file +17
-0+struct rtw89_btc_fbtc_cysta_cpu { +u8 fver; +u8 rsvd; +u16 cycles; +u16 cycles_a2dp[CXT_FLCTRL_MAX ]; +u16 a2dpept; +u16 a2dpeptto; +u16 tavg_cycle[CXT_MAX ]; +u16 tmax_cycle[CXT_MAX ]; +u16 tmaxdiff_cycle[CXT_MAX ]; +u16 tavg_a2dp[CXT_FLCTRL_MAX ]; +u16 tmax_a2dp[CXT_FLCTRL_MAX ]; +u16 tavg_a2dpept; +u16 tmax_a2dpept; +u16 tavg_lk; +u16 tmax_lk; +u32 slot_cnt[CXST_MAX ];
drivers/net/wireless/realtek/rtw89/coex.c line 792
Quoted 10 lines
drivers/net/wireless/realtek/rtw89/coex.c new file +10
-0+u32 bcn_cnt[CXBCN_MAX ]; +u32 leakrx_cnt; +u32 collision_cnt; +u32 skip_cnt; +u32 exception; +u32 except_cnt; +#if (FCXCYSTA_VER >1 ) +u16 tslot_cycle[BTC_CYCLE_SLOT_MAX ]; +#endif +};
Please remove the if check, in all cases:
coex.c.789:#if (FCXCYSTA_VER > 1)
coex.c.829:#if (FCXCYSTA_VER > 1)
core.h.1456:#if (FCXCYSTA_VER > 1)
drivers/net/wireless/realtek/rtw89/coex.c line 1007
Quoted 1 line
drivers/net/wireless/realtek/rtw89/coex.c new file +1
-0+memcpy ((void *)pfinfo, (void *)rpt_content, pcinfo->req_len);
Please remove the casts.
drivers/net/wireless/realtek/rtw89/coex.c line 1302
Quoted 1 line
drivers/net/wireless/realtek/rtw89/coex.c new file +1
-0+memcpy ((void *)ptr, chip->mon_reg, n * ulen);
Here too.
Quoted 1 line
+#define
rtw89_btc_wl_rx_gain(rtwdev, level)do {} while (0)
What's the use of this? Please remove.
drivers/net/wireless/realtek/rtw89/coex.c line 1524
Quoted 5 lines
drivers/net/wireless/realtek/rtw89/coex.c new file +5
-0+static void _set_bt_tx_power (struct rtw89_dev *rtwdev,u8 level) +{ +struct rtw89_btc *btc = &rtwdev->btc; +struct rtw89_btc_bt_info *bt = &btc->cx.bt; +u8 buf =0 ;
Nitpicking, but no need to initialise buf here.
drivers/net/wireless/realtek/rtw89/coex.c line 1699
Quoted 7 lines
drivers/net/wireless/realtek/rtw89/coex.c new file +7
-0+switch (bw) { +case RTW89_CHANNEL_WIDTH_20 : +#ifdef BTC_NON_SHARED_ANT_FREERUN + bw =48 ; +#else + bw =20 + chip->afh_guard_ch *2 ; +#endif
No ifdefs like this. Please remove.
Quoted 1 line
+#define
_set_bt_slot_req(rtwdev)do {} while (0)
I can't see why this is needed, please remove.
Quoted 1 line
+#define
_get_wl_nhm_dbm(rtwdev)do {} while (0)
This too.
drivers/net/wireless/realtek/rtw89/coex.c line 4027
Quoted 4 lines
drivers/net/wireless/realtek/rtw89/coex.c new file +4
-0+/* ======= parse raw info low-Byte2 ======= */ +/* ======= parse raw info low-Byte3 ======= */ +/* ======= parse raw info high-Byte0 ======= */ +/* ======= parse raw info high-Byte1 ======= */
No need to use '=' characters in comments.
drivers/net/wireless/realtek/rtw89/coex.c line 4506
Quoted 2 lines
drivers/net/wireless/realtek/rtw89/coex.c new file +2
-0+#define _update_bt_psd (rtwdev, buf, len) do {} while (0) +#define _update_offload_runinfo (rtwdev, buf, len) do {} while (0)
And more of these, remove.
Quoted 8 lines
drivers/net/wireless/realtek/rtw89/coex.c new file +8
-0+void rtw89_btc_c2h_handle (struct rtw89_dev *rtwdev,struct sk_buff *skb, +u32 len,u8 class,u8 func) +{ +/* The below is just sample code. Don't use magic number in your release +* version. +*/ +struct rtw89_btc *btc = &rtwdev->btc; +struct rtw89_btc_btf_fwinfo *pfwinfo = &btc->fwinfo;
drivers/net/wireless/realtek/rtw89/coex.c line 4525
Quoted 10 lines
drivers/net/wireless/realtek/rtw89/coex.c new file +10
-0+ +/* note: 'len' includes header, so 'buf' length is 'len - 8' */ +u8 *buf = &skb->data[8 ];/* size of C2H header is 8 */ + +rtw89_debug (rtwdev,RTW89_DBG_BTC , +"[BTC], %s(): C2H BT len:%d class:%d fun:%d\n" , + __func__, len, class, func); + +if (class !=0x12 ) +return ;
A magic number, a proper define would document better what's happening.
Quoted 19 lines
drivers/net/wireless/realtek/rtw89/coex.c new file +19
-0+ +switch (func) { +case BTF_EVNT_RPT : +case BTF_EVNT_BUF_OVERFLOW : + pfwinfo->event[func]++; +/* Don't need rtw89_leave_ps_mode() */ +btc_fw_event (rtwdev, func, buf, len); +break ; +case BTF_EVNT_BT_INFO : +rtw89_debug (rtwdev,RTW89_DBG_BTC , +"[BTC], handle C2H BT INFO with data %8ph\n" , buf); + btc->cx.cnt_bt[BTC_BCNT_INFOUPDATE ]++; +rtw89_leave_ps_mode (rtwdev); +_update_bt_info (rtwdev, buf, len); +break ; +case BTF_EVNT_BT_SCBD : +rtw89_debug (rtwdev,RTW89_DBG_BTC , +"[BTC], handle C2H BT SCBD with data %8ph\n" , buf); + btc->cx.cnt_bt[BTC_BCNT_SCBDUPDATE ]++;
drivers/net/wireless/realtek/rtw89/coex.c line 4554
Quoted 10 lines
drivers/net/wireless/realtek/rtw89/coex.c new file +10
-0+rtw89_leave_ps_mode (rtwdev); +_update_bt_scbd (rtwdev, false); +break ; +case BTF_EVNT_BT_PSD : +_update_bt_psd (rtwdev, buf, len); +break ; +case BTF_EVNT_BT_REG : + btc->dbg.rb_done = true; + btc->dbg.rb_val = ((buf[3 ] <<24 ) | (buf[2 ] <<16 ) | + (buf[1 ] <<8 ) | (buf[0 ]));
So are just changing endian or what? le_to_cpu() etc?
Quoted 1 line
+#define
_get_bt_polt_cnt(rtwdev, phy, polt_cnt)do {} while (0)
And again, remove.
Signature · 3 lines
https://patchwork.kernel.org/project/linux-wireless/list/ https://wireless.wiki.kernel.org/en/developers/documentation/submittingpatches
Small driver submissions and long feedback cycles
[[ changing subject, as this is less about rtw89; it used to be:
Re: [PATCH v6 02/24] rtw89: add BT coexistence files ]]
Quoted 4 lines · Kalle Valo
A small tip for future drivers, try to remove all the optional features from the driver as much possible and keep only the absolutely needed features to get ping working. For example this file was pain to review and I suspect coex support could have been submitted separately.
I find that a bit of a tall order. You haven't looked at this driver for 9-10 months:
https://lore.kernel.org/linux-wireless/[email protected]/
Do you expect people to just live with a feature-less driver (read:
unusable) for all that time? As noted in other parts of this thread, quite a few people are already using this driver; so your suggestion implies people should submit a completely different driver to you (i.e., more or less non-working) than the one everybody else wants to use -- or else suffer for those 9-10 months.
For the record, Realtek _did_ try this for rtw88, where they partitioned their driver work into stages, submitting you only the first stuff over the period of 9 months before you merged it:
rtw88 RFC v2; I couldn't find v1:
https://lore.kernel.org/linux-wireless/[email protected]/
Committed about 9 months later:
https://git.kernel.org/linus/e3037485c68ec1a299ff41160d8fedbd4abc29b9
But it was several months after that before the rest of the really-usable features were submitted and actually landed properly.
So it sounds like with your suggested approach (like rtw88), it can take over a year to get a usable driver merged. For this approach, I guess it's 9+ months (TBD; but you seemed more or less happy with this version, minus some small comments). I can see why folks would choose the latter.
Or maybe, those timelines sound altogether bad [1], and we should consider some alternative. If the review and feedback (or merge) cycle was quicker, I'm sure people would be happier to split work into bite-sized chunks. But when it's slow and lacking in transparency, people are instead incentivized to just publish everything at once. Do you need additional help? A reviewer team? I do occasionally try to help things a lot with review on-list, but it's not clear that it has any bearing on time-to-acceptance, so I don't exactly stretch myself.
Brian
[1] They do to me.
Ping-Ke Shih
maintainerPATCH v6 3/24 rtw89: add core and trx files
In reply to Ping-Ke ShihImplement main flows that contains register/unregister mac80211 hw with hardware capability, power on/off sequence, STA state actions, and TX/RX path.
The chip info is read from efuse while probing PCI, and then it can be used to induce supported channel, band, bitrate, ht/vht/he capability, and etc. Then, we register hardware with these capabilities.
When network interface is up, driver does power-on sequence to enable MAC, BB and RF function blocks. Oppositely, do power-off sequence when interface is going to down.
To maintain STA state, five callbacks are implemented -- add, assoc, disassoc, disconnect and remove. In which state, driver tells firmware STA info via H2C.
TX flow:
When a SKB is going to be transmitted, we must know its type first. If the type is mgmt or fwcmd made by driver, SKB is queued into corresponding DMA channel and PCI ring. The other type is data frame that is more complex, because it needs to establish BA session to have better throughput with AMPDU and AMSDU.
In order to have better PCI DMA efficiency, we don't kick off DMA every SKB. With wake TX queue, kick off DMA after a bunch of SKBs are written. To achieve this, we have two HCI ops -- tx_write and tx_kick_off.
BA establishment work:
For data frames, we start to establish BA session if the STA is associated with APMDU capability and the TID session isn't established, and then the BA work is used to ask mac80211 to start AMPDU actions. Driver implements AMPDU action callbacks to know the session is established, so that we can set AGG_EN bit in TX descriptor to enable AMPDU.
RX flow:
When a RX SKB is delivered from PCI, rtw89_core_rx() process it depneds on its type -- WIFI, C2H or PPDU. If type is C2H, it's queued into a C2H queue, and wake a work to handle the C2H packet. If type is WIFI, it's a normal RX packet. When mgmt or data frame is received, it is queued into pending RX SKB queue to wait for corresponding PPDU packet (another RX packet with PPDU type) to fill its rx_status, like RSSI. And, then indicate this packet to mac80211. When control frame is received, indicate it to mac80211 immediately.
Track work:
Use track work to monitor PHY status to know the changes of environment, and then update RA status or do RFK accordingly.
--- drivers/net/wireless/realtek/rtw89/core.c | 2428 +++++++++++++++ drivers/net/wireless/realtek/rtw89/core.h | 3360 +++++++++++++++++++++ drivers/net/wireless/realtek/rtw89/txrx.h | 393 +++ drivers/net/wireless/realtek/rtw89/util.h | 17 + 4 files changed, 6198 insertions(+) create mode 100644 drivers/net/wireless/realtek/rtw89/core.c create mode 100644 drivers/net/wireless/realtek/rtw89/core.h create mode 100644 drivers/net/wireless/realtek/rtw89/txrx.h create mode 100644 drivers/net/wireless/realtek/rtw89/util.h
drivers/net/wireless/realtek/rtw89/core.c new file +2428 -0
@@ -0,0 +1,2428 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* Copyright(c) 2019-2020 Realtek Corporation +*/ + +#include "coex.h" +#include "core.h" +#include "efuse.h" +#include "fw.h" +#include "mac.h" +#include "phy.h" +#include "ps.h" +#include "reg.h" +#include "sar.h" +#include "ser.h" +#include "txrx.h" +#include "util.h" + +static bool rtw89_disable_ps_mode; +module_param_named (disable_ps_mode, rtw89_disable_ps_mode, bool,0644 ); +MODULE_PARM_DESC (disable_ps_mode,"Set Y to disable low power mode" ); + +static struct ieee80211_channel rtw89_channels_2ghz[] = { + { .center_freq =2412 , .hw_value =1 , }, + { .center_freq =2417 , .hw_value =2 , }, + { .center_freq =2422 , .hw_value =3 , }, + { .center_freq =2427 , .hw_value =4 , }, + { .center_freq =2432 , .hw_value =5 , }, + { .center_freq =2437 , .hw_value =6 , }, + { .center_freq =2442 , .hw_value =7 , }, + { .center_freq =2447 , .hw_value =8 , }, + { .center_freq =2452 , .hw_value =9 , }, + { .center_freq =2457 , .hw_value =10 , }, + { .center_freq =2462 , .hw_value =11 , }, + { .center_freq =2467 , .hw_value =12 , }, + { .center_freq =2472 , .hw_value =13 , }, + { .center_freq =2484 , .hw_value =14 , }, +}; + +static struct ieee80211_channel rtw89_channels_5ghz[] = { + {.center_freq =5180 , .hw_value =36 ,}, + {.center_freq =5200 , .hw_value =40 ,}, + {.center_freq =5220 , .hw_value =44 ,}, + {.center_freq =5240 , .hw_value =48 ,}, + {.center_freq =5260 , .hw_value =52 ,}, + {.center_freq =5280 , .hw_value =56 ,}, + {.center_freq =5300 , .hw_value =60 ,}, + {.center_freq =5320 , .hw_value =64 ,}, + {.center_freq =5500 , .hw_value =100 ,}, + {.center_freq =5520 , .hw_value =104 ,}, + {.center_freq =5540 , .hw_value =108 ,}, + {.center_freq =5560 , .hw_value =112 ,}, + {.center_freq =5580 , .hw_value =116 ,}, + {.center_freq =5600 , .hw_value =120 ,}, + {.center_freq =5620 , .hw_value =124 ,}, + {.center_freq =5640 , .hw_value =128 ,}, + {.center_freq =5660 , .hw_value =132 ,}, + {.center_freq =5680 , .hw_value =136 ,}, + {.center_freq =5700 , .hw_value =140 ,}, + {.center_freq =5720 , .hw_value =144 ,}, + {.center_freq =5745 , .hw_value =149 ,}, + {.center_freq =5765 , .hw_value =153 ,}, + {.center_freq =5785 , .hw_value =157 ,}, + {.center_freq =5805 , .hw_value =161 ,}, + {.center_freq =5825 , .hw_value =165 , + .flags =IEEE80211_CHAN_NO_HT40MINUS }, +}; + +static struct ieee80211_rate rtw89_bitrates[] = { + { .bitrate =10 , .hw_value =0x00 , }, + { .bitrate =20 , .hw_value =0x01 , }, + { .bitrate =55 , .hw_value =0x02 , }, + { .bitrate =110 , .hw_value =0x03 , }, + { .bitrate =60 , .hw_value =0x04 , }, + { .bitrate =90 , .hw_value =0x05 , }, + { .bitrate =120 , .hw_value =0x06 , }, + { .bitrate =180 , .hw_value =0x07 , }, + { .bitrate =240 , .hw_value =0x08 , }, + { .bitrate =360 , .hw_value =0x09 , }, + { .bitrate =480 , .hw_value =0x0a , }, + { .bitrate =540 , .hw_value =0x0b , }, +}; + +u16 rtw89_ra_report_to_bitrate (struct rtw89_dev *rtwdev,u8 rpt_rate) +{ +struct ieee80211_rate rate; + +if (unlikely (rpt_rate >=ARRAY_SIZE (rtw89_bitrates))) { +rtw89_info (rtwdev,"invalid rpt rate %d\n" , rpt_rate); +return 0 ; + } + + rate = rtw89_bitrates[rpt_rate]; + +return rate.bitrate; +} + +static struct ieee80211_supported_band rtw89_sband_2ghz = { + .band =NL80211_BAND_2GHZ , + .channels = rtw89_channels_2ghz, + .n_channels =ARRAY_SIZE (rtw89_channels_2ghz), + .bitrates = rtw89_bitrates, + .n_bitrates =ARRAY_SIZE (rtw89_bitrates), + .ht_cap = {0 }, + .vht_cap = {0 }, +}; + +static struct ieee80211_supported_band rtw89_sband_5ghz = { + .band =NL80211_BAND_5GHZ , + .channels = rtw89_channels_5ghz, + .n_channels =ARRAY_SIZE (rtw89_channels_5ghz), + +/* 5G has no CCK rates, 1M/2M/5.5M/11M */ + .bitrates = rtw89_bitrates +4 , + .n_bitrates =ARRAY_SIZE (rtw89_bitrates) -4 , + .ht_cap = {0 }, + .vht_cap = {0 }, +}; + +static void rtw89_traffic_stats_accu (struct rtw89_dev *rtwdev, +struct rtw89_traffic_stats *stats, +struct sk_buff *skb,bool tx) +{ +struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + +if (!ieee80211_is_data (hdr->frame_control)) +return ; + +if (is_broadcast_ether_addr (hdr->addr1) || +is_multicast_ether_addr (hdr->addr1)) +return ; + +if (tx) { + stats->tx_cnt++; + stats->tx_unicast += skb->len; + }else { + stats->rx_cnt++; + stats->rx_unicast += skb->len; + } +} + +static void rtw89_get_channel_params (struct cfg80211_chan_def *chandef, +struct rtw89_channel_params *chan_param) +{ +struct ieee80211_channel *channel = chandef->chan; +enum nl80211_chan_width width = chandef->width; +u8 *cch_by_bw = chan_param->cch_by_bw; +u32 primary_freq, center_freq; +u8 center_chan; +u8 bandwidth =RTW89_CHANNEL_WIDTH_20 ; +u8 primary_chan_idx =0 ; +u8 i; + + center_chan = channel->hw_value; + primary_freq = channel->center_freq; + center_freq = chandef->center_freq1; + +/* assign the center channel used while 20M bw is selected */ + cch_by_bw[RTW89_CHANNEL_WIDTH_20 ] = channel->hw_value; + +switch (width) { +case NL80211_CHAN_WIDTH_20_NOHT : +case NL80211_CHAN_WIDTH_20 : + bandwidth =RTW89_CHANNEL_WIDTH_20 ; + primary_chan_idx =RTW89_SC_DONT_CARE ; +break ; +case NL80211_CHAN_WIDTH_40 : + bandwidth =RTW89_CHANNEL_WIDTH_40 ; +if (primary_freq > center_freq) { + primary_chan_idx =RTW89_SC_20_UPPER ; + center_chan -=2 ; + }else { + primary_chan_idx =RTW89_SC_20_LOWER ; + center_chan +=2 ; + } +break ; +case NL80211_CHAN_WIDTH_80 : + bandwidth =RTW89_CHANNEL_WIDTH_80 ; +if (primary_freq > center_freq) { +if (primary_freq - center_freq ==10 ) { + primary_chan_idx =RTW89_SC_20_UPPER ; + center_chan -=2 ; + }else { + primary_chan_idx =RTW89_SC_20_UPMOST ; + center_chan -=6 ; + } +/* assign the center channel used +* while 40M bw is selected +*/ + cch_by_bw[RTW89_CHANNEL_WIDTH_40 ] = center_chan +4 ; + }else { +if (center_freq - primary_freq ==10 ) { + primary_chan_idx =RTW89_SC_20_LOWER ; + center_chan +=2 ; + }else { + primary_chan_idx =RTW89_SC_20_LOWEST ; + center_chan +=6 ; + } +/* assign the center channel used +* while 40M bw is selected +*/ + cch_by_bw[RTW89_CHANNEL_WIDTH_40 ] = center_chan -4 ; + } +break ; +default : + center_chan =0 ; +break ; + } + + chan_param->center_chan = center_chan; + chan_param->primary_chan = channel->hw_value; + chan_param->bandwidth = bandwidth; + chan_param->pri_ch_idx = primary_chan_idx; + +/* assign the center channel used while current bw is selected */ + cch_by_bw[bandwidth] = center_chan; + +for (i = bandwidth +1 ; i <=RTW89_MAX_CHANNEL_WIDTH ; i++) + cch_by_bw[i] =0 ; +} + +void rtw89_set_channel (struct rtw89_dev *rtwdev) +{ +struct ieee80211_hw *hw = rtwdev->hw; +const struct rtw89_chip_info *chip = rtwdev->chip; +struct rtw89_hal *hal = &rtwdev->hal; +struct rtw89_channel_params ch_param; +struct rtw89_channel_help_params bak; +u8 center_chan, bandwidth; +u8 band_type; +bool band_changed; +u8 i; + +rtw89_get_channel_params (&hw->conf.chandef, &ch_param); +if (WARN (ch_param.center_chan ==0 ,"Invalid channel\n" )) +return ; + + center_chan = ch_param.center_chan; + bandwidth = ch_param.bandwidth; + band_type = center_chan >14 ?RTW89_BAND_5G :RTW89_BAND_2G ; + band_changed = hal->current_band_type != band_type || + hal->current_channel ==0 ; + + hal->current_band_width = bandwidth; + hal->current_channel = center_chan; + hal->current_primary_channel = ch_param.primary_chan; + hal->current_band_type = band_type; + +switch (center_chan) { + case 1 ... 14: + hal->current_subband =RTW89_CH_2G ; +break ; + case 36 ... 64: + hal->current_subband =RTW89_CH_5G_BAND_1 ; +break ; + case 100 ... 144: + hal->current_subband =RTW89_CH_5G_BAND_3 ; +break ; + case 149 ... 177: + hal->current_subband =RTW89_CH_5G_BAND_4 ; +break ; + } + +for (i =RTW89_CHANNEL_WIDTH_20 ; i <=RTW89_MAX_CHANNEL_WIDTH ; i++) + hal->cch_by_bw[i] = ch_param.cch_by_bw[i]; + +rtw89_chip_set_channel_prepare (rtwdev, &bak); + + chip->ops->set_channel (rtwdev, &ch_param); + +rtw89_chip_set_txpwr (rtwdev); + +rtw89_chip_set_channel_done (rtwdev, &bak); + +if (band_changed) { +rtw89_btc_ntfy_switch_band (rtwdev,RTW89_PHY_0 , hal->current_band_type); +rtw89_chip_rfk_band_changed (rtwdev); + } +} + +static enum rtw89_core_tx_type +rtw89_core_get_tx_type (struct rtw89_dev *rtwdev, +struct sk_buff *skb) +{ +struct ieee80211_hdr *hdr = (void *)skb->data; +__le16 fc = hdr->frame_control; + +if (ieee80211_is_mgmt (fc) ||ieee80211_is_nullfunc (fc)) +return RTW89_CORE_TX_TYPE_MGMT ; + +return RTW89_CORE_TX_TYPE_DATA ; +} + +static void +rtw89_core_tx_update_ampdu_info (struct rtw89_dev *rtwdev, +struct rtw89_core_tx_request *tx_req,u8 tid) +{ +struct ieee80211_sta *sta = tx_req->sta; +struct rtw89_tx_desc_info *desc_info = &tx_req->desc_info; +struct rtw89_sta *rtwsta; +u8 ampdu_num; + +if (!sta) { +rtw89_warn (rtwdev,"cannot set ampdu info without sta\n" ); +return ; + } + + rtwsta = (struct rtw89_sta *)sta->drv_priv; + + ampdu_num = (u8)((rtwsta->ampdu_params[tid].agg_num ? + rtwsta->ampdu_params[tid].agg_num : +4 << sta->ht_cap.ampdu_factor) -1 ); + + desc_info->agg_en = true; + desc_info->ampdu_density = sta->ht_cap.ampdu_density; + desc_info->ampdu_num = ampdu_num; +} + +static void +rtw89_core_tx_update_sec_key (struct rtw89_dev *rtwdev, +struct rtw89_core_tx_request *tx_req) +{ +struct ieee80211_vif *vif = tx_req->vif; +struct ieee80211_tx_info *info; +struct ieee80211_key_conf *key; +struct rtw89_vif *rtwvif; +struct rtw89_addr_cam_entry *addr_cam; +struct rtw89_sec_cam_entry *sec_cam; +struct rtw89_tx_desc_info *desc_info = &tx_req->desc_info; +struct sk_buff *skb = tx_req->skb; +u8 sec_type =RTW89_SEC_KEY_TYPE_NONE ; + +if (!vif) { +rtw89_warn (rtwdev,"cannot set sec key without vif\n" ); +return ; + } + + rtwvif = (struct rtw89_vif *)vif->drv_priv; + addr_cam = &rtwvif->addr_cam; + + info =IEEE80211_SKB_CB (skb); + key = info->control.hw_key; + sec_cam = addr_cam->sec_entries[key->hw_key_idx]; +if (!sec_cam) { +rtw89_warn (rtwdev,"sec cam entry is empty\n" ); +return ; + } + +switch (key->cipher) { +case WLAN_CIPHER_SUITE_WEP40 : + sec_type =RTW89_SEC_KEY_TYPE_WEP40 ; +break ; +case WLAN_CIPHER_SUITE_WEP104 : + sec_type =RTW89_SEC_KEY_TYPE_WEP104 ; +break ; +case WLAN_CIPHER_SUITE_TKIP : + sec_type =RTW89_SEC_KEY_TYPE_TKIP ; +break ; +case WLAN_CIPHER_SUITE_CCMP : + sec_type =RTW89_SEC_KEY_TYPE_CCMP128 ; +break ; +case WLAN_CIPHER_SUITE_CCMP_256 : + sec_type =RTW89_SEC_KEY_TYPE_CCMP256 ; +break ; +case WLAN_CIPHER_SUITE_GCMP : + sec_type =RTW89_SEC_KEY_TYPE_GCMP128 ; +break ; +case WLAN_CIPHER_SUITE_GCMP_256 : + sec_type =RTW89_SEC_KEY_TYPE_GCMP256 ; +break ; +default : +rtw89_warn (rtwdev,"key cipher not supported %d\n" , key->cipher); +return ; + } + + desc_info->sec_en = true; + desc_info->sec_type = sec_type; + desc_info->sec_cam_idx = sec_cam->sec_cam_idx; +} + +static u16 rtw89_core_get_mgmt_rate (struct rtw89_dev *rtwdev, +struct rtw89_core_tx_request *tx_req) +{ +struct sk_buff *skb = tx_req->skb; +struct ieee80211_tx_info *tx_info =IEEE80211_SKB_CB (skb); +struct ieee80211_vif *vif = tx_info->control.vif; +struct rtw89_hal *hal = &rtwdev->hal; +u16 lowest_rate = hal->current_band_type ==RTW89_BAND_2G ? +RTW89_HW_RATE_CCK1 :RTW89_HW_RATE_OFDM6 ; + +if (!vif || !vif->bss_conf.basic_rates || !tx_req->sta) +return lowest_rate; + +return __ffs (vif->bss_conf.basic_rates) + lowest_rate; +} + +static void +rtw89_core_tx_update_mgmt_info (struct rtw89_dev *rtwdev, +struct rtw89_core_tx_request *tx_req) +{ +struct rtw89_tx_desc_info *desc_info = &tx_req->desc_info; +u8 qsel, ch_dma; + + qsel =RTW89_TX_QSEL_B0_MGMT ; + ch_dma =rtw89_core_get_ch_dma (rtwdev, qsel); + + desc_info->qsel =RTW89_TX_QSEL_B0_MGMT ; + desc_info->ch_dma = ch_dma; + +/* fixed data rate for mgmt frames */ + desc_info->en_wd_info = true; + desc_info->use_rate = true; + desc_info->dis_data_fb = true; + desc_info->data_rate =rtw89_core_get_mgmt_rate (rtwdev, tx_req); + +rtw89_debug (rtwdev,RTW89_DBG_TXRX , +"tx mgmt frame with rate 0x%x on channel %d (bw %d)\n" , + desc_info->data_rate, rtwdev->hal.current_channel, + rtwdev->hal.current_band_width); +} + +static void +rtw89_core_tx_update_h2c_info (struct rtw89_dev *rtwdev, +struct rtw89_core_tx_request *tx_req) +{ +struct rtw89_tx_desc_info *desc_info = &tx_req->desc_info; + + desc_info->is_bmc = false; + desc_info->wd_page = false; + desc_info->ch_dma =RTW89_DMA_H2C ; +} + +static void rtw89_core_get_no_ul_ofdma_htc (struct rtw89_dev *rtwdev,__le32 *htc) +{ +static const u8 rtw89_bandwidth_to_om[] = { + [RTW89_CHANNEL_WIDTH_20 ] =HTC_OM_CHANNEL_WIDTH_20 , + [RTW89_CHANNEL_WIDTH_40 ] =HTC_OM_CHANNEL_WIDTH_40 , + [RTW89_CHANNEL_WIDTH_80 ] =HTC_OM_CHANNEL_WIDTH_80 , + [RTW89_CHANNEL_WIDTH_160 ] =HTC_OM_CHANNEL_WIDTH_160_OR_80_80 , + [RTW89_CHANNEL_WIDTH_80_80 ] =HTC_OM_CHANNEL_WIDTH_160_OR_80_80 , + }; +const struct rtw89_chip_info *chip = rtwdev->chip; +struct rtw89_hal *hal = &rtwdev->hal; +u8 om_bandwidth; + +if (!chip->dis_2g_40m_ul_ofdma || + hal->current_band_type !=RTW89_BAND_2G || + hal->current_band_width !=RTW89_CHANNEL_WIDTH_40 ) +return ; + + om_bandwidth = hal->current_band_width <ARRAY_SIZE (rtw89_bandwidth_to_om) ? + rtw89_bandwidth_to_om[hal->current_band_width] :0 ; + *htc =le32_encode_bits (RTW89_HTC_VARIANT_HE ,RTW89_HTC_MASK_VARIANT ) | +le32_encode_bits (RTW89_HTC_VARIANT_HE_CID_OM ,RTW89_HTC_MASK_CTL_ID ) | +le32_encode_bits (hal->rx_nss -1 ,RTW89_HTC_MASK_HTC_OM_RX_NSS ) | +le32_encode_bits (om_bandwidth,RTW89_HTC_MASK_HTC_OM_CH_WIDTH ) | +le32_encode_bits (1 ,RTW89_HTC_MASK_HTC_OM_UL_MU_DIS ) | +le32_encode_bits (hal->tx_nss -1 ,RTW89_HTC_MASK_HTC_OM_TX_NSTS ) | +le32_encode_bits (0 ,RTW89_HTC_MASK_HTC_OM_ER_SU_DIS ) | +le32_encode_bits (0 ,RTW89_HTC_MASK_HTC_OM_DL_MU_MIMO_RR ) | +le32_encode_bits (0 ,RTW89_HTC_MASK_HTC_OM_UL_MU_DATA_DIS ); +} + +static bool +__rtw89_core_tx_check_he_qos_htc (struct rtw89_dev *rtwdev, +struct rtw89_core_tx_request *tx_req, +enum btc_pkt_type pkt_type) +{ +struct ieee80211_sta *sta = tx_req->sta; +struct sk_buff *skb = tx_req->skb; +struct ieee80211_hdr *hdr = (void *)skb->data; +__le16 fc = hdr->frame_control; + +/* AP IOT issue with EAPoL, ARP and DHCP */ +if (pkt_type <PACKET_MAX ) +return false; + +if (!sta || !sta->he_cap.has_he) +return false; + +if (!ieee80211_is_data_qos (fc)) +return false; + +if (skb_headroom (skb) <IEEE80211_HT_CTL_LEN ) +return false; + +return true; +} + +static void +__rtw89_core_tx_adjust_he_qos_htc (struct rtw89_dev *rtwdev, +struct rtw89_core_tx_request *tx_req) +{ +struct ieee80211_sta *sta = tx_req->sta; +struct rtw89_sta *rtwsta = (struct rtw89_sta *)sta->drv_priv; +struct sk_buff *skb = tx_req->skb; +struct ieee80211_hdr *hdr = (void *)skb->data; +__le16 fc = hdr->frame_control; +void *data; +__le32 *htc; +u8 *qc; +int hdr_len; + + hdr_len =ieee80211_has_a4 (fc) ?32 :26 ; + data =skb_push (skb,IEEE80211_HT_CTL_LEN ); +memmove (data, data +IEEE80211_HT_CTL_LEN , hdr_len); + + hdr = data; + htc = data + hdr_len; + hdr->frame_control |=cpu_to_le16 (IEEE80211_FCTL_ORDER ); + *htc = rtwsta->htc_template ? rtwsta->htc_template : +le32_encode_bits (RTW89_HTC_VARIANT_HE ,RTW89_HTC_MASK_VARIANT ) | +le32_encode_bits (RTW89_HTC_VARIANT_HE_CID_CAS ,RTW89_HTC_MASK_CTL_ID ); + + qc = data + hdr_len -IEEE80211_QOS_CTL_LEN ; + qc[0 ] |=IEEE80211_QOS_CTL_EOSP ; +} + +static void +rtw89_core_tx_update_he_qos_htc (struct rtw89_dev *rtwdev, +struct rtw89_core_tx_request *tx_req, +enum btc_pkt_type pkt_type) +{ +struct rtw89_tx_desc_info *desc_info = &tx_req->desc_info; +struct ieee80211_vif *vif = tx_req->vif; +struct rtw89_vif *rtwvif = (struct rtw89_vif *)vif->drv_priv; + +if (!__rtw89_core_tx_check_he_qos_htc (rtwdev, tx_req, pkt_type)) + goto desc_bk; + +__rtw89_core_tx_adjust_he_qos_htc (rtwdev, tx_req); + + desc_info->pkt_size +=IEEE80211_HT_CTL_LEN ; + desc_info->a_ctrl_bsr = true; + +desc_bk: +if (!rtwvif || rtwvif->last_a_ctrl == desc_info->a_ctrl_bsr) +return ; + + rtwvif->last_a_ctrl = desc_info->a_ctrl_bsr; + desc_info->bk = true; +} + +static void +rtw89_core_tx_update_data_info (struct rtw89_dev *rtwdev, +struct rtw89_core_tx_request *tx_req) +{ +struct rtw89_tx_desc_info *desc_info = &tx_req->desc_info; +struct sk_buff *skb = tx_req->skb; +u8 tid, tid_indicate; +u8 qsel, ch_dma; + + tid = skb->priority &IEEE80211_QOS_CTL_TAG1D_MASK ; + tid_indicate =rtw89_core_get_tid_indicate (rtwdev, tid); + qsel =rtw89_core_get_qsel (rtwdev, tid); + ch_dma =rtw89_core_get_ch_dma (rtwdev, qsel); + + desc_info->ch_dma = ch_dma; + desc_info->tid_indicate = tid_indicate; + desc_info->qsel = qsel; + +/* enable wd_info for AMPDU */ + desc_info->en_wd_info = true; + +if (IEEE80211_SKB_CB (skb)->flags &IEEE80211_TX_CTL_AMPDU ) +rtw89_core_tx_update_ampdu_info (rtwdev, tx_req, tid); +if (IEEE80211_SKB_CB (skb)->control.hw_key) +rtw89_core_tx_update_sec_key (rtwdev, tx_req); +} + +static enum btc_pkt_type +rtw89_core_tx_btc_spec_pkt_notify (struct rtw89_dev *rtwdev, +struct rtw89_core_tx_request *tx_req) +{ +struct sk_buff *skb = tx_req->skb; +struct udphdr *udphdr; + +if (IEEE80211_SKB_CB (skb)->control.flags &IEEE80211_TX_CTRL_PORT_CTRL_PROTO ) { +ieee80211_queue_work (rtwdev->hw, &rtwdev->btc.eapol_notify_work); +return PACKET_EAPOL ; + } + +if (skb->protocol ==htons (ETH_P_ARP )) { +ieee80211_queue_work (rtwdev->hw, &rtwdev->btc.arp_notify_work); +return PACKET_ARP ; + } + +if (skb->protocol ==htons (ETH_P_IP ) && +ip_hdr (skb)->protocol ==IPPROTO_UDP ) { + udphdr =udp_hdr (skb); +if (((udphdr->source ==htons (67 ) && udphdr->dest ==htons (68 )) || + (udphdr->source ==htons (68 ) && udphdr->dest ==htons (67 ))) && + skb->len >282 ) { +ieee80211_queue_work (rtwdev->hw, &rtwdev->btc.dhcp_notify_work); +return PACKET_DHCP ; + } + } + +return PACKET_MAX ; +} + +static void +rtw89_core_tx_update_desc_info (struct rtw89_dev *rtwdev, +struct rtw89_core_tx_request *tx_req) +{ +struct rtw89_tx_desc_info *desc_info = &tx_req->desc_info; +struct sk_buff *skb = tx_req->skb; +struct ieee80211_hdr *hdr = (void *)skb->data; +enum rtw89_core_tx_type tx_type; +enum btc_pkt_type pkt_type; +bool is_bmc; +u16 seq; + + seq = (le16_to_cpu (hdr->seq_ctrl) &IEEE80211_SCTL_SEQ ) >>4 ; +if (tx_req->tx_type !=RTW89_CORE_TX_TYPE_FWCMD ) { + tx_type =rtw89_core_get_tx_type (rtwdev, skb); + tx_req->tx_type = tx_type; + } + is_bmc = (is_broadcast_ether_addr (hdr->addr1) || +is_multicast_ether_addr (hdr->addr1)); + + desc_info->seq = seq; + desc_info->pkt_size = skb->len; + desc_info->is_bmc = is_bmc; + desc_info->wd_page = true; + +switch (tx_req->tx_type) { +case RTW89_CORE_TX_TYPE_MGMT : +rtw89_core_tx_update_mgmt_info (rtwdev, tx_req); +break ; +case RTW89_CORE_TX_TYPE_DATA : +rtw89_core_tx_update_data_info (rtwdev, tx_req); + pkt_type =rtw89_core_tx_btc_spec_pkt_notify (rtwdev, tx_req); +rtw89_core_tx_update_he_qos_htc (rtwdev, tx_req, pkt_type); +break ; +case RTW89_CORE_TX_TYPE_FWCMD : +rtw89_core_tx_update_h2c_info (rtwdev, tx_req); +break ; + } +} + +void rtw89_core_tx_kick_off (struct rtw89_dev *rtwdev,u8 qsel) +{ +u8 ch_dma; + + ch_dma =rtw89_core_get_ch_dma (rtwdev, qsel); + +rtw89_hci_tx_kick_off (rtwdev, ch_dma); +} + +int rtw89_h2c_tx (struct rtw89_dev *rtwdev, +struct sk_buff *skb,bool fwdl) +{ +struct rtw89_core_tx_request tx_req = {0 }; +u32 cnt; +int ret; + + tx_req.skb = skb; + tx_req.tx_type =RTW89_CORE_TX_TYPE_FWCMD ; +if (fwdl) + tx_req.desc_info.fw_dl = true; + +rtw89_core_tx_update_desc_info (rtwdev, &tx_req); + +if (!fwdl) +rtw89_hex_dump (rtwdev,RTW89_DBG_FW ,"H2C: " , skb->data, skb->len); + + cnt =rtw89_hci_check_and_reclaim_tx_resource (rtwdev,RTW89_TXCH_CH12 ); +if (cnt ==0 ) { +rtw89_err (rtwdev,"no tx fwcmd resource\n" ); +return -ENOSPC ; + } + + ret =rtw89_hci_tx_write (rtwdev, &tx_req); +if (ret) { +rtw89_err (rtwdev,"failed to transmit skb to HCI\n" ); +return ret; + } +rtw89_hci_tx_kick_off (rtwdev,RTW89_TXCH_CH12 ); + +return 0 ; +} + +int rtw89_core_tx_write (struct rtw89_dev *rtwdev,struct ieee80211_vif *vif, +struct ieee80211_sta *sta,struct sk_buff *skb,int *qsel) +{ +struct rtw89_core_tx_request tx_req = {0 }; +struct rtw89_vif *rtwvif = (struct rtw89_vif *)vif->drv_priv; +int ret; + + tx_req.skb = skb; + tx_req.sta = sta; + tx_req.vif = vif; + +rtw89_traffic_stats_accu (rtwdev, &rtwdev->stats, skb, true); +rtw89_traffic_stats_accu (rtwdev, &rtwvif->stats, skb, true); +rtw89_core_tx_update_desc_info (rtwdev, &tx_req); + ret =rtw89_hci_tx_write (rtwdev, &tx_req); +if (ret) { +rtw89_err (rtwdev,"failed to transmit skb to HCI\n" ); +return ret; + } + +if (qsel) + *qsel = tx_req.desc_info.qsel; + +return 0 ; +} + +void rtw89_core_fill_txdesc (struct rtw89_dev *rtwdev, +struct rtw89_tx_desc_info *desc_info, +void *txdesc) +{ +RTW89_SET_TXWD_BODY_WP_OFFSET (txdesc, desc_info->wp_offset); +RTW89_SET_TXWD_BODY_WD_INFO_EN (txdesc, desc_info->en_wd_info); +RTW89_SET_TXWD_BODY_CHANNEL_DMA (txdesc, desc_info->ch_dma); +RTW89_SET_TXWD_BODY_HDR_LLC_LEN (txdesc, desc_info->hdr_llc_len); +RTW89_SET_TXWD_BODY_WD_PAGE (txdesc, desc_info->wd_page); +RTW89_SET_TXWD_BODY_FW_DL (txdesc, desc_info->fw_dl); +RTW89_SET_TXWD_BODY_SW_SEQ (txdesc, desc_info->seq); + +RTW89_SET_TXWD_BODY_TID_INDICATE (txdesc, desc_info->tid_indicate); +RTW89_SET_TXWD_BODY_QSEL (txdesc, desc_info->qsel); +RTW89_SET_TXWD_BODY_TXPKT_SIZE (txdesc, desc_info->pkt_size); +RTW89_SET_TXWD_BODY_AGG_EN (txdesc, desc_info->agg_en); +RTW89_SET_TXWD_BODY_BK (txdesc, desc_info->bk); + +if (!desc_info->en_wd_info) +return ; + +RTW89_SET_TXWD_INFO_USE_RATE (txdesc, desc_info->use_rate); +RTW89_SET_TXWD_INFO_DATA_RATE (txdesc, desc_info->data_rate); +RTW89_SET_TXWD_INFO_DISDATAFB (txdesc, desc_info->dis_data_fb); +RTW89_SET_TXWD_INFO_MAX_AGGNUM (txdesc, desc_info->ampdu_num); +RTW89_SET_TXWD_INFO_AMPDU_DENSITY (txdesc, desc_info->ampdu_density); +RTW89_SET_TXWD_INFO_SEC_TYPE (txdesc, desc_info->sec_type); +RTW89_SET_TXWD_INFO_SEC_HW_ENC (txdesc, desc_info->sec_en); +RTW89_SET_TXWD_INFO_SEC_CAM_IDX (txdesc, desc_info->sec_cam_idx); +RTW89_SET_TXWD_INFO_RTS_EN (txdesc,1 ); +RTW89_SET_TXWD_INFO_HW_RTS_EN (txdesc,1 ); +RTW89_SET_TXWD_INFO_A_CTRL_BSR (txdesc, desc_info->a_ctrl_bsr); +} +EXPORT_SYMBOL (rtw89_core_fill_txdesc); + +static int rtw89_core_rx_process_mac_ppdu (struct rtw89_dev *rtwdev, +struct sk_buff *skb, +struct rtw89_rx_phy_ppdu *phy_ppdu) +{ +bool rx_cnt_valid = false; +u8 plcp_size =0 ; +u8 usr_num =0 ; +u8 *phy_sts; + + rx_cnt_valid =RTW89_GET_RXINFO_RX_CNT_VLD (skb->data); + plcp_size =RTW89_GET_RXINFO_PLCP_LEN (skb->data) <<3 ; + usr_num =RTW89_GET_RXINFO_USR_NUM (skb->data); +if (usr_num >RTW89_PPDU_MAX_USR ) { +rtw89_warn (rtwdev,"Invalid user number in mac info\n" ); +return -EINVAL ; + } + + phy_sts = skb->data +RTW89_PPDU_MAC_INFO_SIZE ; + phy_sts += usr_num *RTW89_PPDU_MAC_INFO_USR_SIZE ; +/* 8-byte alignment */ +if (usr_num &BIT (0 )) + phy_sts +=RTW89_PPDU_MAC_INFO_USR_SIZE ; +if (rx_cnt_valid) + phy_sts +=RTW89_PPDU_MAC_RX_CNT_SIZE ; + phy_sts += plcp_size; + + phy_ppdu->buf = phy_sts; + phy_ppdu->len = skb->data + skb->len - phy_sts; + +return 0 ; +} + +static void rtw89_core_rx_process_phy_ppdu_iter (void *data, +struct ieee80211_sta *sta) +{ +struct rtw89_sta *rtwsta = (struct rtw89_sta *)sta->drv_priv; +struct rtw89_rx_phy_ppdu *phy_ppdu = (struct rtw89_rx_phy_ppdu *)data; + +if (rtwsta->mac_id == phy_ppdu->mac_id && phy_ppdu->to_self) +ewma_rssi_add (&rtwsta->avg_rssi, phy_ppdu->rssi_avg); +} + +#define VAR_LEN 0xff +#define VAR_LEN_UNIT 8 +static u16 rtw89_core_get_phy_status_ie_len (struct rtw89_dev *rtwdev,u8 *addr) +{ +static const u8 physts_ie_len_tab[32 ] = { +16 ,32 ,24 ,24 ,8 ,8 ,8 ,8 ,VAR_LEN ,8 ,VAR_LEN ,176 ,VAR_LEN , +VAR_LEN ,VAR_LEN ,VAR_LEN ,VAR_LEN ,VAR_LEN ,16 ,24 ,VAR_LEN , +VAR_LEN ,VAR_LEN ,0 ,24 ,24 ,24 ,24 ,32 ,32 ,32 ,32 + }; +u16 ie_len; +u8 ie; + + ie =RTW89_GET_PHY_STS_IE_TYPE (addr); +if (physts_ie_len_tab[ie] !=VAR_LEN ) + ie_len = physts_ie_len_tab[ie]; +else + ie_len =RTW89_GET_PHY_STS_IE_LEN (addr) *VAR_LEN_UNIT ; + +return ie_len; +} + +static void rtw89_core_parse_phy_status_ie01 (struct rtw89_dev *rtwdev,u8 *addr, +struct rtw89_rx_phy_ppdu *phy_ppdu) +{ +s16 cfo; + +/* sign conversion for S(12,2) */ + cfo =sign_extend32 (RTW89_GET_PHY_STS_IE0_CFO (addr),11 ); +rtw89_phy_cfo_parse (rtwdev, cfo, phy_ppdu); +} + +static int rtw89_core_process_phy_status_ie (struct rtw89_dev *rtwdev,u8 *addr, +struct rtw89_rx_phy_ppdu *phy_ppdu) +{ +u8 ie; + + ie =RTW89_GET_PHY_STS_IE_TYPE (addr); +switch (ie) { +case RTW89_PHYSTS_IE01_CMN_OFDM : +rtw89_core_parse_phy_status_ie01 (rtwdev, addr, phy_ppdu); +break ; +default : +break ; + } + +return 0 ; +} + +static void rtw89_core_update_phy_ppdu (struct rtw89_rx_phy_ppdu *phy_ppdu) +{ +s8 *rssi = phy_ppdu->rssi; +u8 *buf = phy_ppdu->buf; + + phy_ppdu->rssi_avg =RTW89_GET_PHY_STS_RSSI_AVG (buf); + rssi[RF_PATH_A ] =RTW89_RSSI_RAW_TO_DBM (RTW89_GET_PHY_STS_RSSI_A (buf)); + rssi[RF_PATH_B ] =RTW89_RSSI_RAW_TO_DBM (RTW89_GET_PHY_STS_RSSI_B (buf)); + rssi[RF_PATH_C ] =RTW89_RSSI_RAW_TO_DBM (RTW89_GET_PHY_STS_RSSI_C (buf)); + rssi[RF_PATH_D ] =RTW89_RSSI_RAW_TO_DBM (RTW89_GET_PHY_STS_RSSI_D (buf)); +} + +static int rtw89_core_rx_process_phy_ppdu (struct rtw89_dev *rtwdev, +struct rtw89_rx_phy_ppdu *phy_ppdu) +{ +if (RTW89_GET_PHY_STS_LEN (phy_ppdu->buf) <<3 != phy_ppdu->len) { +rtw89_warn (rtwdev,"phy ppdu len mismatch\n" ); +return -EINVAL ; + } +rtw89_core_update_phy_ppdu (phy_ppdu); +ieee80211_iterate_stations_atomic (rtwdev->hw, + rtw89_core_rx_process_phy_ppdu_iter, + phy_ppdu); + +return 0 ; +} + +static int rtw89_core_rx_parse_phy_sts (struct rtw89_dev *rtwdev, +struct rtw89_rx_phy_ppdu *phy_ppdu) +{ +u16 ie_len; +u8 *pos, *end; + +if (!phy_ppdu->to_self) +return 0 ; + + pos = (u8 *)phy_ppdu->buf +PHY_STS_HDR_LEN ; + end = (u8 *)phy_ppdu->buf + phy_ppdu->len; +while (pos < end) { + ie_len =rtw89_core_get_phy_status_ie_len (rtwdev, pos); +rtw89_core_process_phy_status_ie (rtwdev, pos, phy_ppdu); + pos += ie_len; +if (pos > end || ie_len ==0 ) { +rtw89_debug (rtwdev,RTW89_DBG_TXRX , +"phy status parse failed\n" ); +return -EINVAL ; + } + } + +return 0 ; +} + +static void rtw89_core_rx_process_phy_sts (struct rtw89_dev *rtwdev, +struct rtw89_rx_phy_ppdu *phy_ppdu) +{ +int ret; + + ret =rtw89_core_rx_parse_phy_sts (rtwdev, phy_ppdu); +if (ret) +rtw89_debug (rtwdev,RTW89_DBG_TXRX ,"parse phy sts failed\n" ); +else + phy_ppdu->valid = true; +} + +static u8 rtw89_rxdesc_to_nl_he_gi (struct rtw89_dev *rtwdev, +const struct rtw89_rx_desc_info *desc_info, +bool rx_status) +{ +switch (desc_info->gi_ltf) { +case RTW89_GILTF_SGI_4XHE08 : +case RTW89_GILTF_2XHE08 : +case RTW89_GILTF_1XHE08 : +return NL80211_RATE_INFO_HE_GI_0_8 ; +case RTW89_GILTF_2XHE16 : +case RTW89_GILTF_1XHE16 : +return NL80211_RATE_INFO_HE_GI_1_6 ; +case RTW89_GILTF_LGI_4XHE32 : +return NL80211_RATE_INFO_HE_GI_3_2 ; +default : +rtw89_warn (rtwdev,"invalid gi_ltf=%d" , desc_info->gi_ltf); +return rx_status ?NL80211_RATE_INFO_HE_GI_3_2 :U8_MAX ; + } +} + +static bool rtw89_core_rx_ppdu_match (struct rtw89_dev *rtwdev, +struct rtw89_rx_desc_info *desc_info, +struct ieee80211_rx_status *status) +{ +u8 band = desc_info->bb_sel ?RTW89_PHY_1 :RTW89_PHY_0 ; +u8 data_rate_mode, bw, rate_idx =MASKBYTE0 , gi_ltf; +u16 data_rate; +bool ret; + + data_rate = desc_info->data_rate; + data_rate_mode =GET_DATA_RATE_MODE (data_rate); +if (data_rate_mode ==DATA_RATE_MODE_NON_HT ) { + rate_idx =GET_DATA_RATE_NOT_HT_IDX (data_rate); +/* No 4 CCK rates for 5G */ +if (status->band ==NL80211_BAND_5GHZ ) + rate_idx -=4 ; + }else if (data_rate_mode ==DATA_RATE_MODE_HT ) { + rate_idx =GET_DATA_RATE_HT_IDX (data_rate); + }else if (data_rate_mode ==DATA_RATE_MODE_VHT ) { + rate_idx =GET_DATA_RATE_VHT_HE_IDX (data_rate); + }else if (data_rate_mode ==DATA_RATE_MODE_HE ) { + rate_idx =GET_DATA_RATE_VHT_HE_IDX (data_rate); + }else { +rtw89_warn (rtwdev,"invalid RX rate mode %d\n" , data_rate_mode); + } + +if (desc_info->bw ==RTW89_CHANNEL_WIDTH_80 ) + bw =RATE_INFO_BW_80 ; +else if (desc_info->bw ==RTW89_CHANNEL_WIDTH_40 ) + bw =RATE_INFO_BW_40 ; +else + bw =RATE_INFO_BW_20 ; + + gi_ltf =rtw89_rxdesc_to_nl_he_gi (rtwdev, desc_info, false); + ret = rtwdev->ppdu_sts.curr_rx_ppdu_cnt[band] == desc_info->ppdu_cnt && + status->rate_idx == rate_idx && + status->he_gi == gi_ltf && + status->bw == bw; + +return ret; +} + +struct rtw89_vif_rx_stats_iter_data { +struct rtw89_dev *rtwdev; +struct rtw89_rx_phy_ppdu *phy_ppdu; +struct rtw89_rx_desc_info *desc_info; +struct sk_buff *skb; +const u8 *bssid; +}; + +static void rtw89_vif_rx_stats_iter (void *data,u8 *mac, +struct ieee80211_vif *vif) +{ +struct rtw89_vif *rtwvif = (struct rtw89_vif *)vif->drv_priv; +struct rtw89_vif_rx_stats_iter_data *iter_data = data; +struct rtw89_dev *rtwdev = iter_data->rtwdev; +struct rtw89_pkt_stat *pkt_stat = &rtwdev->phystat.cur_pkt_stat; +struct rtw89_rx_desc_info *desc_info = iter_data->desc_info; +struct sk_buff *skb = iter_data->skb; +struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; +const u8 *bssid = iter_data->bssid; + +if (!ether_addr_equal (vif->bss_conf.bssid, bssid)) +return ; + +if (ieee80211_is_beacon (hdr->frame_control)) + pkt_stat->beacon_nr++; + +if (!ether_addr_equal (vif->addr, hdr->addr1)) +return ; + +if (desc_info->data_rate <RTW89_HW_RATE_NR ) + pkt_stat->rx_rate_cnt[desc_info->data_rate]++; + +rtw89_traffic_stats_accu (rtwdev, &rtwvif->stats, skb, false); +} + +static void rtw89_core_rx_stats (struct rtw89_dev *rtwdev, +struct rtw89_rx_phy_ppdu *phy_ppdu, +struct rtw89_rx_desc_info *desc_info, +struct sk_buff *skb) +{ +struct rtw89_vif_rx_stats_iter_data iter_data; + +rtw89_traffic_stats_accu (rtwdev, &rtwdev->stats, skb, false); + + iter_data.rtwdev = rtwdev; + iter_data.phy_ppdu = phy_ppdu; + iter_data.desc_info = desc_info; + iter_data.skb = skb; + iter_data.bssid =get_hdr_bssid ((struct ieee80211_hdr *)skb->data); +rtw89_iterate_vifs_bh (rtwdev, rtw89_vif_rx_stats_iter, &iter_data); +} + +static void rtw89_core_rx_pending_skb (struct rtw89_dev *rtwdev, +struct rtw89_rx_phy_ppdu *phy_ppdu, +struct rtw89_rx_desc_info *desc_info, +struct sk_buff *skb) +{ +u8 band = desc_info->bb_sel ?RTW89_PHY_1 :RTW89_PHY_0 ; +int curr = rtwdev->ppdu_sts.curr_rx_ppdu_cnt[band]; +struct sk_buff *skb_ppdu =NULL , *tmp; +struct ieee80211_rx_status *rx_status; + +if (curr >RTW89_MAX_PPDU_CNT ) +return ; + + skb_queue_walk_safe(&rtwdev->ppdu_sts.rx_queue[band], skb_ppdu, tmp) { +skb_unlink (skb_ppdu, &rtwdev->ppdu_sts.rx_queue[band]); + rx_status =IEEE80211_SKB_RXCB (skb_ppdu); +if (rtw89_core_rx_ppdu_match (rtwdev, desc_info, rx_status)) +rtw89_chip_query_ppdu (rtwdev, phy_ppdu, rx_status); +rtw89_core_rx_stats (rtwdev, phy_ppdu, desc_info, skb_ppdu); +ieee80211_rx_napi (rtwdev->hw,NULL , skb_ppdu, &rtwdev->napi); + rtwdev->napi_budget_countdown--; + } +} + +static void rtw89_core_rx_process_ppdu_sts (struct rtw89_dev *rtwdev, +struct rtw89_rx_desc_info *desc_info, +struct sk_buff *skb) +{ +struct rtw89_rx_phy_ppdu phy_ppdu = {.buf = skb->data, .valid = false, + .len = skb->len, + .to_self = desc_info->addr1_match, + .mac_id = desc_info->mac_id}; +int ret; + +if (desc_info->mac_info_valid) +rtw89_core_rx_process_mac_ppdu (rtwdev, skb, &phy_ppdu); + ret =rtw89_core_rx_process_phy_ppdu (rtwdev, &phy_ppdu); +if (ret) +rtw89_debug (rtwdev,RTW89_DBG_TXRX ,"process ppdu failed\n" ); + +rtw89_core_rx_process_phy_sts (rtwdev, &phy_ppdu); +rtw89_core_rx_pending_skb (rtwdev, &phy_ppdu, desc_info, skb); +dev_kfree_skb_any (skb); +} + +static void rtw89_core_rx_process_report (struct rtw89_dev *rtwdev, +struct rtw89_rx_desc_info *desc_info, +struct sk_buff *skb) +{ +switch (desc_info->pkt_type) { +case RTW89_CORE_RX_TYPE_C2H : +rtw89_fw_c2h_irqsafe (rtwdev, skb); +break ; +case RTW89_CORE_RX_TYPE_PPDU_STAT : +rtw89_core_rx_process_ppdu_sts (rtwdev, desc_info, skb); +break ; +default : +rtw89_debug (rtwdev,RTW89_DBG_TXRX ,"unhandled pkt_type=%d\n" , + desc_info->pkt_type); +dev_kfree_skb_any (skb); +break ; + } +} + +void rtw89_core_query_rxdesc (struct rtw89_dev *rtwdev, +struct rtw89_rx_desc_info *desc_info, +u8 *data,u32 data_offset) +{ +struct rtw89_rxdesc_short *rxd_s; +struct rtw89_rxdesc_long *rxd_l; +u8 shift_len, drv_info_len; + + rxd_s = (struct rtw89_rxdesc_short *)(data + data_offset); + desc_info->pkt_size =RTW89_GET_RXWD_PKT_SIZE (rxd_s); + desc_info->drv_info_size =RTW89_GET_RXWD_DRV_INFO_SIZE (rxd_s); + desc_info->long_rxdesc =RTW89_GET_RXWD_LONG_RXD (rxd_s); + desc_info->pkt_type =RTW89_GET_RXWD_RPKT_TYPE (rxd_s); + desc_info->mac_info_valid =RTW89_GET_RXWD_MAC_INFO_VALID (rxd_s); + desc_info->bw =RTW89_GET_RXWD_BW (rxd_s); + desc_info->data_rate =RTW89_GET_RXWD_DATA_RATE (rxd_s); + desc_info->gi_ltf =RTW89_GET_RXWD_GI_LTF (rxd_s); + desc_info->user_id =RTW89_GET_RXWD_USER_ID (rxd_s); + desc_info->sr_en =RTW89_GET_RXWD_SR_EN (rxd_s); + desc_info->ppdu_cnt =RTW89_GET_RXWD_PPDU_CNT (rxd_s); + desc_info->ppdu_type =RTW89_GET_RXWD_PPDU_TYPE (rxd_s); + desc_info->free_run_cnt =RTW89_GET_RXWD_FREE_RUN_CNT (rxd_s); + desc_info->icv_err =RTW89_GET_RXWD_ICV_ERR (rxd_s); + desc_info->crc32_err =RTW89_GET_RXWD_CRC32_ERR (rxd_s); + desc_info->hw_dec =RTW89_GET_RXWD_HW_DEC (rxd_s); + desc_info->sw_dec =RTW89_GET_RXWD_SW_DEC (rxd_s); + desc_info->addr1_match =RTW89_GET_RXWD_A1_MATCH (rxd_s); + + shift_len = desc_info->shift <<1 ;/* 2-byte unit */ + drv_info_len = desc_info->drv_info_size <<3 ;/* 8-byte unit */ + desc_info->offset = data_offset + shift_len + drv_info_len; + desc_info->ready = true; + +if (!desc_info->long_rxdesc) +return ; + + rxd_l = (struct rtw89_rxdesc_long *)(data + data_offset); + desc_info->frame_type =RTW89_GET_RXWD_TYPE (rxd_l); + desc_info->addr_cam_valid =RTW89_GET_RXWD_ADDR_CAM_VLD (rxd_l); + desc_info->addr_cam_id =RTW89_GET_RXWD_ADDR_CAM_ID (rxd_l); + desc_info->sec_cam_id =RTW89_GET_RXWD_SEC_CAM_ID (rxd_l); + desc_info->mac_id =RTW89_GET_RXWD_MAC_ID (rxd_l); + desc_info->rx_pl_id =RTW89_GET_RXWD_RX_PL_ID (rxd_l); +} +EXPORT_SYMBOL (rtw89_core_query_rxdesc); + +struct rtw89_core_iter_rx_status { +struct rtw89_dev *rtwdev; +struct ieee80211_rx_status *rx_status; +struct rtw89_rx_desc_info *desc_info; +u8 mac_id; +}; + +static +void rtw89_core_stats_sta_rx_status_iter (void *data,struct ieee80211_sta *sta) +{ +struct rtw89_core_iter_rx_status *iter_data = + (struct rtw89_core_iter_rx_status *)data; +struct ieee80211_rx_status *rx_status = iter_data->rx_status; +struct rtw89_sta *rtwsta = (struct rtw89_sta *)sta->drv_priv; +struct rtw89_rx_desc_info *desc_info = iter_data->desc_info; +u8 mac_id = iter_data->mac_id; + +if (mac_id != rtwsta->mac_id) +return ; + + rtwsta->rx_status = *rx_status; + rtwsta->rx_hw_rate = desc_info->data_rate; +} + +static void rtw89_core_stats_sta_rx_status (struct rtw89_dev *rtwdev, +struct rtw89_rx_desc_info *desc_info, +struct ieee80211_rx_status *rx_status) +{ +struct rtw89_core_iter_rx_status iter_data; + +if (!desc_info->addr1_match || !desc_info->long_rxdesc) +return ; + +if (desc_info->frame_type !=RTW89_RX_TYPE_DATA ) +return ; + + iter_data.rtwdev = rtwdev; + iter_data.rx_status = rx_status; + iter_data.desc_info = desc_info; + iter_data.mac_id = desc_info->mac_id; +ieee80211_iterate_stations_atomic (rtwdev->hw, + rtw89_core_stats_sta_rx_status_iter, + &iter_data); +} + +static void rtw89_core_update_rx_status (struct rtw89_dev *rtwdev, +struct rtw89_rx_desc_info *desc_info, +struct ieee80211_rx_status *rx_status) +{ +struct ieee80211_hw *hw = rtwdev->hw; +u16 data_rate; +u8 data_rate_mode; + +/* currently using single PHY */ + rx_status->freq = hw->conf.chandef.chan->center_freq; + rx_status->band = hw->conf.chandef.chan->band; + +if (desc_info->icv_err || desc_info->crc32_err) + rx_status->flag |=RX_FLAG_FAILED_FCS_CRC ; + +if (desc_info->hw_dec && + !(desc_info->sw_dec || desc_info->icv_err)) + rx_status->flag |=RX_FLAG_DECRYPTED ; + +if (desc_info->bw ==RTW89_CHANNEL_WIDTH_80 ) + rx_status->bw =RATE_INFO_BW_80 ; +else if (desc_info->bw ==RTW89_CHANNEL_WIDTH_40 ) + rx_status->bw =RATE_INFO_BW_40 ; +else + rx_status->bw =RATE_INFO_BW_20 ; + + data_rate = desc_info->data_rate; + data_rate_mode =GET_DATA_RATE_MODE (data_rate); +if (data_rate_mode ==DATA_RATE_MODE_NON_HT ) { + rx_status->encoding =RX_ENC_LEGACY ; + rx_status->rate_idx =GET_DATA_RATE_NOT_HT_IDX (data_rate); +/* No 4 CCK rates for 5G */ +if (rx_status->band ==NL80211_BAND_5GHZ ) + rx_status->rate_idx -=4 ; +if (rtwdev->scanning) + rx_status->rate_idx =min_t (u8, rx_status->rate_idx, +ARRAY_SIZE (rtw89_bitrates) -5 ); + }else if (data_rate_mode ==DATA_RATE_MODE_HT ) { + rx_status->encoding =RX_ENC_HT ; + rx_status->rate_idx =GET_DATA_RATE_HT_IDX (data_rate); +if (desc_info->gi_ltf) + rx_status->enc_flags |=RX_ENC_FLAG_SHORT_GI ; + }else if (data_rate_mode ==DATA_RATE_MODE_VHT ) { + rx_status->encoding =RX_ENC_VHT ; + rx_status->rate_idx =GET_DATA_RATE_VHT_HE_IDX (data_rate); + rx_status->nss =GET_DATA_RATE_NSS (data_rate) +1 ; +if (desc_info->gi_ltf) + rx_status->enc_flags |=RX_ENC_FLAG_SHORT_GI ; + }else if (data_rate_mode ==DATA_RATE_MODE_HE ) { + rx_status->encoding =RX_ENC_HE ; + rx_status->rate_idx =GET_DATA_RATE_VHT_HE_IDX (data_rate); + rx_status->nss =GET_DATA_RATE_NSS (data_rate) +1 ; + }else { +rtw89_warn (rtwdev,"invalid RX rate mode %d\n" , data_rate_mode); + } + +/* he_gi is used to match ppdu, so we always fill it. */ + rx_status->he_gi =rtw89_rxdesc_to_nl_he_gi (rtwdev, desc_info, true); + rx_status->flag |=RX_FLAG_MACTIME_START ; + rx_status->mactime = desc_info->free_run_cnt; + +rtw89_core_stats_sta_rx_status (rtwdev, desc_info, rx_status); +} + +static enum rtw89_ps_mode rtw89_update_ps_mode (struct rtw89_dev *rtwdev) +{ +const struct rtw89_chip_info *chip = rtwdev->chip; + +if (rtw89_disable_ps_mode || !chip->ps_mode_supported) +return RTW89_PS_MODE_NONE ; + +if (chip->ps_mode_supported &BIT (RTW89_PS_MODE_PWR_GATED )) +return RTW89_PS_MODE_PWR_GATED ; + +if (chip->ps_mode_supported &BIT (RTW89_PS_MODE_CLK_GATED )) +return RTW89_PS_MODE_CLK_GATED ; + +if (chip->ps_mode_supported &BIT (RTW89_PS_MODE_RFOFF )) +return RTW89_PS_MODE_RFOFF ; + +return RTW89_PS_MODE_NONE ; +} + +static void rtw89_core_flush_ppdu_rx_queue (struct rtw89_dev *rtwdev, +struct rtw89_rx_desc_info *desc_info) +{ +struct rtw89_ppdu_sts_info *ppdu_sts = &rtwdev->ppdu_sts; +u8 band = desc_info->bb_sel ?RTW89_PHY_1 :RTW89_PHY_0 ; +struct sk_buff *skb_ppdu, *tmp; + + skb_queue_walk_safe(&ppdu_sts->rx_queue[band], skb_ppdu, tmp) { +skb_unlink (skb_ppdu, &ppdu_sts->rx_queue[band]); +rtw89_core_rx_stats (rtwdev,NULL , desc_info, skb_ppdu); +ieee80211_rx_napi (rtwdev->hw,NULL , skb_ppdu, &rtwdev->napi); + rtwdev->napi_budget_countdown--; + } +} + +void rtw89_core_rx (struct rtw89_dev *rtwdev, +struct rtw89_rx_desc_info *desc_info, +struct sk_buff *skb) +{ +struct ieee80211_rx_status *rx_status; +struct rtw89_ppdu_sts_info *ppdu_sts = &rtwdev->ppdu_sts; +u8 ppdu_cnt = desc_info->ppdu_cnt; +u8 band = desc_info->bb_sel ?RTW89_PHY_1 :RTW89_PHY_0 ; + +if (desc_info->pkt_type !=RTW89_CORE_RX_TYPE_WIFI ) { +rtw89_core_rx_process_report (rtwdev, desc_info, skb); +return ; + } + +if (ppdu_sts->curr_rx_ppdu_cnt[band] != ppdu_cnt) { +rtw89_core_flush_ppdu_rx_queue (rtwdev, desc_info); + ppdu_sts->curr_rx_ppdu_cnt[band] = ppdu_cnt; + } + + rx_status =IEEE80211_SKB_RXCB (skb); +memset (rx_status,0 ,sizeof (*rx_status)); +rtw89_core_update_rx_status (rtwdev, desc_info, rx_status); +if (desc_info->long_rxdesc && +BIT (desc_info->frame_type) &PPDU_FILTER_BITMAP ) { +skb_queue_tail (&ppdu_sts->rx_queue[band], skb); + }else { +rtw89_core_rx_stats (rtwdev,NULL , desc_info, skb); +ieee80211_rx_napi (rtwdev->hw,NULL , skb, &rtwdev->napi); + rtwdev->napi_budget_countdown--; + } +} +EXPORT_SYMBOL (rtw89_core_rx); + +void rtw89_core_napi_start (struct rtw89_dev *rtwdev) +{ +if (test_and_set_bit (RTW89_FLAG_NAPI_RUNNING , rtwdev->flags)) +return ; + +napi_enable (&rtwdev->napi); +} +EXPORT_SYMBOL (rtw89_core_napi_start); + +void rtw89_core_napi_stop (struct rtw89_dev *rtwdev) +{ +if (!test_and_clear_bit (RTW89_FLAG_NAPI_RUNNING , rtwdev->flags)) +return ; + +napi_synchronize (&rtwdev->napi); +napi_disable (&rtwdev->napi); +} +EXPORT_SYMBOL (rtw89_core_napi_stop); + +void rtw89_core_napi_init (struct rtw89_dev *rtwdev) +{ +init_dummy_netdev (&rtwdev->netdev); +netif_napi_add (&rtwdev->netdev, &rtwdev->napi, + rtwdev->hci.ops->napi_poll,NAPI_POLL_WEIGHT ); +} +EXPORT_SYMBOL (rtw89_core_napi_init); + +void rtw89_core_napi_deinit (struct rtw89_dev *rtwdev) +{ +rtw89_core_napi_stop (rtwdev); +netif_napi_del (&rtwdev->napi); +} +EXPORT_SYMBOL (rtw89_core_napi_deinit); + +static void rtw89_core_ba_work (struct work_struct *work) +{ +struct rtw89_dev *rtwdev = + container_of(work, struct rtw89_dev, ba_work); +struct rtw89_txq *rtwtxq, *tmp; +int ret; + +spin_lock_bh (&rtwdev->ba_lock); +list_for_each_entry_safe (rtwtxq, tmp, &rtwdev->ba_list, list) { +struct ieee80211_txq *txq =rtw89_txq_to_txq (rtwtxq); +struct ieee80211_sta *sta = txq->sta; +struct rtw89_sta *rtwsta = (struct rtw89_sta *)sta->drv_priv; +u8 tid = txq->tid; + +if (!sta) { +rtw89_warn (rtwdev,"cannot start BA without sta\n" ); + goto skip_ba_work; + } + +if (rtwsta->disassoc) { +rtw89_debug (rtwdev,RTW89_DBG_TXRX , +"cannot start BA with disassoc sta\n" ); + goto skip_ba_work; + } + + ret =ieee80211_start_tx_ba_session (sta, tid,0 ); +if (ret) { +rtw89_debug (rtwdev,RTW89_DBG_TXRX , +"failed to setup BA session for %pM:%2d: %d\n" , + sta->addr, tid, ret); +if (ret == -EINVAL ) +set_bit (RTW89_TXQ_F_BLOCK_BA , &rtwtxq->flags); + } +skip_ba_work: +list_del_init (&rtwtxq->list); + } +spin_unlock_bh (&rtwdev->ba_lock); +} + +static void rtw89_core_free_sta_pending_ba (struct rtw89_dev *rtwdev, +struct ieee80211_sta *sta) +{ +struct rtw89_txq *rtwtxq, *tmp; + +spin_lock_bh (&rtwdev->ba_lock); +list_for_each_entry_safe (rtwtxq, tmp, &rtwdev->ba_list, list) { +struct ieee80211_txq *txq =rtw89_txq_to_txq (rtwtxq); + +if (sta == txq->sta) +list_del_init (&rtwtxq->list); + } +spin_unlock_bh (&rtwdev->ba_lock); +} + +static void rtw89_core_txq_check_agg (struct rtw89_dev *rtwdev, +struct rtw89_txq *rtwtxq, +struct sk_buff *skb) +{ +struct ieee80211_hw *hw = rtwdev->hw; +struct ieee80211_txq *txq =rtw89_txq_to_txq (rtwtxq); +struct ieee80211_sta *sta = txq->sta; +struct rtw89_sta *rtwsta = (struct rtw89_sta *)sta->drv_priv; + +if (unlikely (skb_get_queue_mapping (skb) ==IEEE80211_AC_VO )) +return ; + +if (unlikely (skb->protocol ==cpu_to_be16 (ETH_P_PAE ))) +return ; + +if (unlikely (!sta)) +return ; + +if (unlikely (test_bit (RTW89_TXQ_F_BLOCK_BA , &rtwtxq->flags))) +return ; + +if (test_bit (RTW89_TXQ_F_AMPDU , &rtwtxq->flags)) { +IEEE80211_SKB_CB (skb)->flags |=IEEE80211_TX_CTL_AMPDU ; +return ; + } + +spin_lock_bh (&rtwdev->ba_lock); +if (!rtwsta->disassoc &&list_empty (&rtwtxq->list)) { +list_add_tail (&rtwtxq->list, &rtwdev->ba_list); +ieee80211_queue_work (hw, &rtwdev->ba_work); + } +spin_unlock_bh (&rtwdev->ba_lock); +} + +static void rtw89_core_txq_push (struct rtw89_dev *rtwdev, +struct rtw89_txq *rtwtxq, +unsigned long frame_cnt, +unsigned long byte_cnt) +{ +struct ieee80211_txq *txq =rtw89_txq_to_txq (rtwtxq); +struct ieee80211_vif *vif = txq->vif; +struct ieee80211_sta *sta = txq->sta; +struct sk_buff *skb; +unsigned long i; +int ret; + +for (i =0 ; i < frame_cnt; i++) { + skb =ieee80211_tx_dequeue_ni (rtwdev->hw, txq); +if (!skb) { +rtw89_debug (rtwdev,RTW89_DBG_TXRX ,"dequeue a NULL skb\n" ); +return ; + } +rtw89_core_txq_check_agg (rtwdev, rtwtxq, skb); + ret =rtw89_core_tx_write (rtwdev, vif, sta, skb,NULL ); +if (ret) { +rtw89_err (rtwdev,"failed to push txq: %d\n" , ret); +ieee80211_free_txskb (rtwdev->hw, skb); +break ; + } + } +} + +static u32 rtw89_check_and_reclaim_tx_resource (struct rtw89_dev *rtwdev,u8 tid) +{ +u8 qsel, ch_dma; + + qsel =rtw89_core_get_qsel (rtwdev, tid); + ch_dma =rtw89_core_get_ch_dma (rtwdev, qsel); + +return rtw89_hci_check_and_reclaim_tx_resource (rtwdev, ch_dma); +} + +static bool rtw89_core_txq_agg_wait (struct rtw89_dev *rtwdev, +struct ieee80211_txq *txq, +unsigned long *frame_cnt, +bool *sched_txq,bool *reinvoke) +{ +struct rtw89_txq *rtwtxq = (struct rtw89_txq *)txq->drv_priv; +struct ieee80211_sta *sta = txq->sta; +struct rtw89_sta *rtwsta = (struct rtw89_sta *)sta->drv_priv; + +if (!sta || rtwsta->max_agg_wait <=0 ) +return false; + +if (rtwdev->stats.tx_tfc_lv <=RTW89_TFC_MID ) +return false; + +if (*frame_cnt >1 ) { + *frame_cnt -=1 ; + *sched_txq = true; + *reinvoke = true; + rtwtxq->wait_cnt =1 ; +return false; + } + +if (*frame_cnt ==1 && rtwtxq->wait_cnt < rtwsta->max_agg_wait) { + *reinvoke = true; + rtwtxq->wait_cnt++; +return true; + } + + rtwtxq->wait_cnt =0 ; +return false; +} + +static void rtw89_core_txq_schedule (struct rtw89_dev *rtwdev,u8 ac,bool *reinvoke) +{ +struct ieee80211_hw *hw = rtwdev->hw; +struct ieee80211_txq *txq; +struct rtw89_txq *rtwtxq; +unsigned long frame_cnt; +unsigned long byte_cnt; +u32 tx_resource; +bool sched_txq; + +ieee80211_txq_schedule_start (hw, ac); +while ((txq =ieee80211_next_txq (hw, ac))) { + rtwtxq = (struct rtw89_txq *)txq->drv_priv; + tx_resource =rtw89_check_and_reclaim_tx_resource (rtwdev, txq->tid); + sched_txq = false; + +ieee80211_txq_get_depth (txq, &frame_cnt, &byte_cnt); +if (rtw89_core_txq_agg_wait (rtwdev, txq, &frame_cnt, &sched_txq, reinvoke)) { +ieee80211_return_txq (hw, txq, true); +continue ; + } + frame_cnt = min_t(unsigned long, frame_cnt, tx_resource); +rtw89_core_txq_push (rtwdev, rtwtxq, frame_cnt, byte_cnt); +ieee80211_return_txq (hw, txq, sched_txq); +if (frame_cnt !=0 ) +rtw89_core_tx_kick_off (rtwdev,rtw89_core_get_qsel (rtwdev, txq->tid)); + } +ieee80211_txq_schedule_end (hw, ac); +} + +static void rtw89_core_txq_work (struct work_struct *w) +{ + struct rtw89_dev *rtwdev = container_of(w, struct rtw89_dev, txq_work); +bool reinvoke = false; +u8 ac; + +for (ac =0 ; ac <IEEE80211_NUM_ACS ; ac++) +rtw89_core_txq_schedule (rtwdev, ac, &reinvoke); + +if (reinvoke) { +/* reinvoke to process the last frame */ +mod_delayed_work (rtwdev->txq_wq, &rtwdev->txq_reinvoke_work,1 ); + } +} + +static void rtw89_core_txq_reinvoke_work (struct work_struct *w) +{ + struct rtw89_dev *rtwdev = container_of(w, struct rtw89_dev, + txq_reinvoke_work.work); + +queue_work (rtwdev->txq_wq, &rtwdev->txq_work); +} + +static enum rtw89_tfc_lv rtw89_get_traffic_level (struct rtw89_dev *rtwdev, +u32 throughput,u64 cnt) +{ +if (cnt <100 ) +return RTW89_TFC_IDLE ; +if (throughput >50 ) +return RTW89_TFC_HIGH ; +if (throughput >10 ) +return RTW89_TFC_MID ; +if (throughput >2 ) +return RTW89_TFC_LOW ; +return RTW89_TFC_ULTRA_LOW ; +} + +static bool rtw89_traffic_stats_calc (struct rtw89_dev *rtwdev, +struct rtw89_traffic_stats *stats) +{ +enum rtw89_tfc_lv tx_tfc_lv = stats->tx_tfc_lv; +enum rtw89_tfc_lv rx_tfc_lv = stats->rx_tfc_lv; + + stats->tx_throughput_raw = (u32)(stats->tx_unicast >>RTW89_TP_SHIFT ); + stats->rx_throughput_raw = (u32)(stats->rx_unicast >>RTW89_TP_SHIFT ); + +ewma_tp_add (&stats->tx_ewma_tp, stats->tx_throughput_raw); +ewma_tp_add (&stats->rx_ewma_tp, stats->rx_throughput_raw); + + stats->tx_throughput =ewma_tp_read (&stats->tx_ewma_tp); + stats->rx_throughput =ewma_tp_read (&stats->rx_ewma_tp); + stats->tx_tfc_lv =rtw89_get_traffic_level (rtwdev, stats->tx_throughput, + stats->tx_cnt); + stats->rx_tfc_lv =rtw89_get_traffic_level (rtwdev, stats->rx_throughput, + stats->rx_cnt); + stats->tx_avg_len = stats->tx_cnt ? +DIV_ROUND_DOWN_ULL (stats->tx_unicast, stats->tx_cnt) :0 ; + stats->rx_avg_len = stats->rx_cnt ? +DIV_ROUND_DOWN_ULL (stats->rx_unicast, stats->rx_cnt) :0 ; + + stats->tx_unicast =0 ; + stats->rx_unicast =0 ; + stats->tx_cnt =0 ; + stats->rx_cnt =0 ; + +if (tx_tfc_lv != stats->tx_tfc_lv || rx_tfc_lv != stats->rx_tfc_lv) { +ieee80211_queue_work (rtwdev->hw, &rtwdev->btc.wl_sta_notify_work); +return true; + } + +return false; +} + +static bool rtw89_traffic_stats_track (struct rtw89_dev *rtwdev) +{ +struct rtw89_vif *rtwvif; +bool tfc_changed; + + tfc_changed =rtw89_traffic_stats_calc (rtwdev, &rtwdev->stats); +rtw89_for_each_rtwvif (rtwdev, rtwvif) +rtw89_traffic_stats_calc (rtwdev, &rtwvif->stats); + +return tfc_changed; +} + +static void rtw89_vif_enter_lps (struct rtw89_dev *rtwdev,struct rtw89_vif *rtwvif) +{ +if (rtwvif->wifi_role !=RTW89_WIFI_ROLE_STATION ) +return ; + +if (rtwvif->stats.tx_tfc_lv ==RTW89_TFC_IDLE && + rtwvif->stats.rx_tfc_lv ==RTW89_TFC_IDLE ) +rtw89_enter_lps (rtwdev, rtwvif->mac_id); +} + +static void rtw89_enter_lps_track (struct rtw89_dev *rtwdev) +{ +struct rtw89_vif *rtwvif; + +rtw89_for_each_rtwvif (rtwdev, rtwvif) +rtw89_vif_enter_lps (rtwdev, rtwvif); +} + +void rtw89_traffic_stats_init (struct rtw89_dev *rtwdev, +struct rtw89_traffic_stats *stats) +{ + stats->tx_unicast =0 ; + stats->rx_unicast =0 ; + stats->tx_cnt =0 ; + stats->rx_cnt =0 ; +ewma_tp_init (&stats->tx_ewma_tp); +ewma_tp_init (&stats->rx_ewma_tp); +} + +static void rtw89_track_work (struct work_struct *work) +{ + struct rtw89_dev *rtwdev = container_of(work, struct rtw89_dev, + track_work.work); +bool tfc_changed; + +mutex_lock (&rtwdev->mutex); + +if (!test_bit (RTW89_FLAG_RUNNING , rtwdev->flags)) + goto out; + +ieee80211_queue_delayed_work (rtwdev->hw, &rtwdev->track_work, +RTW89_TRACK_WORK_PERIOD ); + + tfc_changed =rtw89_traffic_stats_track (rtwdev); +if (rtwdev->scanning) + goto out; + +rtw89_leave_lps (rtwdev); + +if (tfc_changed) +rtw89_hci_recalc_int_mit (rtwdev); +rtw89_mac_bf_monitor_track (rtwdev); +rtw89_phy_stat_track (rtwdev); +rtw89_phy_env_monitor_track (rtwdev); +rtw89_phy_dig (rtwdev); +rtw89_chip_rfk_track (rtwdev); +rtw89_phy_ra_update (rtwdev); +rtw89_phy_cfo_track (rtwdev); + +if (rtwdev->lps_enabled && !rtwdev->btc.lps) +rtw89_enter_lps_track (rtwdev); + +out: +mutex_unlock (&rtwdev->mutex); +} + +u8 rtw89_core_acquire_bit_map (unsigned long *addr,unsigned long size) +{ +unsigned long bit; + + bit =find_first_zero_bit (addr, size); +if (bit < size) +set_bit (bit, addr); + +return bit; +} + +void rtw89_core_release_bit_map (unsigned long *addr,u8 bit) +{ +clear_bit (bit, addr); +} + +void rtw89_core_release_all_bits_map (unsigned long *addr,unsigned int nbits) +{ +bitmap_zero (addr, nbits); +} + +#define RTW89_TYPE_MAPPING (_type) \ + case NL80211_IFTYPE_ ## _type: \ + rtwvif->wifi_role = RTW89_WIFI_ROLE_ ## _type; \ + break +void rtw89_vif_type_mapping (struct ieee80211_vif *vif,bool assoc) +{ +struct rtw89_vif *rtwvif = (struct rtw89_vif *)vif->drv_priv; + +switch (vif->type) { +RTW89_TYPE_MAPPING (ADHOC ); +RTW89_TYPE_MAPPING (STATION ); +RTW89_TYPE_MAPPING (AP ); +RTW89_TYPE_MAPPING (MONITOR ); +RTW89_TYPE_MAPPING (MESH_POINT ); +default : +WARN_ON (1 ); +break ; + } + +switch (vif->type) { +case NL80211_IFTYPE_AP : +case NL80211_IFTYPE_MESH_POINT : + rtwvif->net_type =RTW89_NET_TYPE_AP_MODE ; + rtwvif->self_role =RTW89_SELF_ROLE_AP ; +break ; +case NL80211_IFTYPE_ADHOC : + rtwvif->net_type =RTW89_NET_TYPE_AD_HOC ; + rtwvif->self_role =RTW89_SELF_ROLE_CLIENT ; +break ; +case NL80211_IFTYPE_STATION : +if (assoc) { + rtwvif->net_type =RTW89_NET_TYPE_INFRA ; + rtwvif->trigger = vif->bss_conf.he_support; + }else { + rtwvif->net_type =RTW89_NET_TYPE_NO_LINK ; + rtwvif->trigger = false; + } + rtwvif->self_role =RTW89_SELF_ROLE_CLIENT ; + rtwvif->addr_cam.sec_ent_mode =RTW89_ADDR_CAM_SEC_NORMAL ; +break ; +default : +WARN_ON (1 ); +break ; + } +} + +int rtw89_core_sta_add (struct rtw89_dev *rtwdev, +struct ieee80211_vif *vif, +struct ieee80211_sta *sta) +{ +struct rtw89_vif *rtwvif = (struct rtw89_vif *)vif->drv_priv; +struct rtw89_sta *rtwsta = (struct rtw89_sta *)sta->drv_priv; +int i; + + rtwsta->rtwvif = rtwvif; + rtwsta->prev_rssi =0 ; + +for (i =0 ; i <ARRAY_SIZE (sta->txq); i++) +rtw89_core_txq_init (rtwdev, sta->txq[i]); + +ewma_rssi_init (&rtwsta->avg_rssi); + +if (vif->type ==NL80211_IFTYPE_STATION ) { + rtwvif->mgd.ap = sta; +rtw89_btc_ntfy_role_info (rtwdev, rtwvif, rtwsta, +BTC_ROLE_MSTS_STA_CONN_START ); +rtw89_chip_rfk_channel (rtwdev); + } + +return 0 ; +} + +int rtw89_core_sta_disassoc (struct rtw89_dev *rtwdev, +struct ieee80211_vif *vif, +struct ieee80211_sta *sta) +{ +struct rtw89_sta *rtwsta = (struct rtw89_sta *)sta->drv_priv; + + rtwdev->total_sta_assoc--; + rtwsta->disassoc = true; + +return 0 ; +} + +int rtw89_core_sta_disconnect (struct rtw89_dev *rtwdev, +struct ieee80211_vif *vif, +struct ieee80211_sta *sta) +{ +struct rtw89_vif *rtwvif = (struct rtw89_vif *)vif->drv_priv; +int ret; + +rtw89_mac_bf_monitor_calc (rtwdev, sta, true); +rtw89_mac_bf_disassoc (rtwdev, vif, sta); +rtw89_core_free_sta_pending_ba (rtwdev, sta); + +rtw89_vif_type_mapping (vif, false); + + ret =rtw89_fw_h2c_assoc_cmac_tbl (rtwdev, vif, sta); +if (ret) { +rtw89_warn (rtwdev,"failed to send h2c cmac table\n" ); +return ret; + } + + ret =rtw89_fw_h2c_join_info (rtwdev, rtwvif,1 ); +if (ret) { +rtw89_warn (rtwdev,"failed to send h2c join info\n" ); +return ret; + } + +/* update cam aid mac_id net_type */ +rtw89_fw_h2c_cam (rtwdev, rtwvif); +if (ret) { +rtw89_warn (rtwdev,"failed to send h2c cam\n" ); +return ret; + } + +return ret; +} + +int rtw89_core_sta_assoc (struct rtw89_dev *rtwdev, +struct ieee80211_vif *vif, +struct ieee80211_sta *sta) +{ +struct rtw89_vif *rtwvif = (struct rtw89_vif *)vif->drv_priv; +struct rtw89_sta *rtwsta = (struct rtw89_sta *)sta->drv_priv; +int ret; + +rtw89_vif_type_mapping (vif, true); + + ret =rtw89_fw_h2c_assoc_cmac_tbl (rtwdev, vif, sta); +if (ret) { +rtw89_warn (rtwdev,"failed to send h2c cmac table\n" ); +return ret; + } + +/* for station mode, assign the mac_id from itself */ +if (vif->type ==NL80211_IFTYPE_STATION ) + rtwsta->mac_id = rtwvif->mac_id; + + ret =rtw89_fw_h2c_join_info (rtwdev, rtwvif,0 ); +if (ret) { +rtw89_warn (rtwdev,"failed to send h2c join info\n" ); +return ret; + } + +/* update cam aid mac_id net_type */ +rtw89_fw_h2c_cam (rtwdev, rtwvif); +if (ret) { +rtw89_warn (rtwdev,"failed to send h2c cam\n" ); +return ret; + } + + ret =rtw89_fw_h2c_general_pkt (rtwdev, rtwsta->mac_id); +if (ret) { +rtw89_warn (rtwdev,"failed to send h2c general packet\n" ); +return ret; + } + + rtwdev->total_sta_assoc++; +rtw89_phy_ra_assoc (rtwdev, sta); +rtw89_mac_bf_assoc (rtwdev, vif, sta); +rtw89_mac_bf_monitor_calc (rtwdev, sta, false); + +if (vif->type ==NL80211_IFTYPE_STATION ) { +rtw89_btc_ntfy_role_info (rtwdev, rtwvif, rtwsta, +BTC_ROLE_MSTS_STA_CONN_END ); +rtw89_core_get_no_ul_ofdma_htc (rtwdev, &rtwsta->htc_template); + } + +return ret; +} + +int rtw89_core_sta_remove (struct rtw89_dev *rtwdev, +struct ieee80211_vif *vif, +struct ieee80211_sta *sta) +{ +struct rtw89_vif *rtwvif = (struct rtw89_vif *)vif->drv_priv; +struct rtw89_sta *rtwsta = (struct rtw89_sta *)sta->drv_priv; + +if (vif->type ==NL80211_IFTYPE_STATION ) +rtw89_btc_ntfy_role_info (rtwdev, rtwvif, rtwsta, +BTC_ROLE_MSTS_STA_DIS_CONN ); + +return 0 ; +} + +static void rtw89_init_ht_cap (struct rtw89_dev *rtwdev, +struct ieee80211_sta_ht_cap *ht_cap) +{ +static const __le16 highest[RF_PATH_MAX ] = { +cpu_to_le16 (150 ),cpu_to_le16 (300 ),cpu_to_le16 (450 ),cpu_to_le16 (600 ), + }; +struct rtw89_hal *hal = &rtwdev->hal; +u8 nss = hal->rx_nss; +int i; + + ht_cap->ht_supported = true; + ht_cap->cap =0 ; + ht_cap->cap |=IEEE80211_HT_CAP_SGI_20 | +IEEE80211_HT_CAP_MAX_AMSDU | +IEEE80211_HT_CAP_TX_STBC | + (1 <<IEEE80211_HT_CAP_RX_STBC_SHIFT ); + ht_cap->cap |=IEEE80211_HT_CAP_LDPC_CODING ; + ht_cap->cap |=IEEE80211_HT_CAP_SUP_WIDTH_20_40 | +IEEE80211_HT_CAP_DSSSCCK40 | +IEEE80211_HT_CAP_SGI_40 ; + ht_cap->ampdu_factor =IEEE80211_HT_MAX_AMPDU_64K ; + ht_cap->ampdu_density =IEEE80211_HT_MPDU_DENSITY_NONE ; + ht_cap->mcs.tx_params =IEEE80211_HT_MCS_TX_DEFINED ; +for (i =0 ; i < nss; i++) + ht_cap->mcs.rx_mask[i] =0xFF ; + ht_cap->mcs.rx_mask[4 ] =0x01 ; + ht_cap->mcs.rx_highest = highest[nss -1 ]; +} + +static void rtw89_init_vht_cap (struct rtw89_dev *rtwdev, +struct ieee80211_sta_vht_cap *vht_cap) +{ +static const __le16 highest[RF_PATH_MAX ] = { +cpu_to_le16 (433 ),cpu_to_le16 (867 ),cpu_to_le16 (1300 ),cpu_to_le16 (1733 ), + }; +struct rtw89_hal *hal = &rtwdev->hal; +u16 tx_mcs_map =0 , rx_mcs_map =0 ; +u8 sts_cap =3 ; +int i; + +for (i =0 ; i <8 ; i++) { +if (i < hal->tx_nss) + tx_mcs_map |=IEEE80211_VHT_MCS_SUPPORT_0_9 << (i *2 ); +else + tx_mcs_map |=IEEE80211_VHT_MCS_NOT_SUPPORTED << (i *2 ); +if (i < hal->rx_nss) + rx_mcs_map |=IEEE80211_VHT_MCS_SUPPORT_0_9 << (i *2 ); +else + rx_mcs_map |=IEEE80211_VHT_MCS_NOT_SUPPORTED << (i *2 ); + } + + vht_cap->vht_supported = true; + vht_cap->cap =IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454 | +IEEE80211_VHT_CAP_SHORT_GI_80 | +IEEE80211_VHT_CAP_RXSTBC_1 | +IEEE80211_VHT_CAP_HTC_VHT | +IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK | +0 ; + vht_cap->cap |=IEEE80211_VHT_CAP_TXSTBC ; + vht_cap->cap |=IEEE80211_VHT_CAP_RXLDPC ; + vht_cap->cap |=IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE | +IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE ; + vht_cap->cap |= sts_cap <<IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT ; + vht_cap->vht_mcs.rx_mcs_map =cpu_to_le16 (rx_mcs_map); + vht_cap->vht_mcs.tx_mcs_map =cpu_to_le16 (tx_mcs_map); + vht_cap->vht_mcs.rx_highest = highest[hal->rx_nss -1 ]; + vht_cap->vht_mcs.tx_highest = highest[hal->tx_nss -1 ]; +} + +#define RTW89_SBAND_IFTYPES_NR 2 + +static void rtw89_init_he_cap (struct rtw89_dev *rtwdev, +enum nl80211_band band, +struct ieee80211_supported_band *sband) +{ +const struct rtw89_chip_info *chip = rtwdev->chip; +struct rtw89_hal *hal = &rtwdev->hal; +struct ieee80211_sband_iftype_data *iftype_data; +bool no_ng16 = (chip->chip_id ==RTL8852A && hal->cv ==CHIP_CBV ) || + (chip->chip_id ==RTL8852B && hal->cv ==CHIP_CAV ); +u16 mcs_map =0 ; +int i; +int nss = hal->rx_nss; +int idx =0 ; + + iftype_data =kcalloc (RTW89_SBAND_IFTYPES_NR ,sizeof (*iftype_data),GFP_KERNEL ); +if (!iftype_data) +return ; + +for (i =0 ; i <8 ; i++) { +if (i < nss) + mcs_map |=IEEE80211_HE_MCS_SUPPORT_0_11 << (i *2 ); +else + mcs_map |=IEEE80211_HE_MCS_NOT_SUPPORTED << (i *2 ); + } + +for (i =0 ; i <NUM_NL80211_IFTYPES ; i++) { +struct ieee80211_sta_he_cap *he_cap; +u8 *mac_cap_info; +u8 *phy_cap_info; + +switch (i) { +case NL80211_IFTYPE_STATION : +case NL80211_IFTYPE_AP : +break ; +default : +continue ; + } + +if (idx >=RTW89_SBAND_IFTYPES_NR ) { +rtw89_warn (rtwdev,"run out of iftype_data\n" ); +break ; + } + + iftype_data[idx].types_mask =BIT (i); + he_cap = &iftype_data[idx].he_cap; + mac_cap_info = he_cap->he_cap_elem.mac_cap_info; + phy_cap_info = he_cap->he_cap_elem.phy_cap_info; + + he_cap->has_he = true; +if (i ==NL80211_IFTYPE_AP ) + mac_cap_info[0 ] =IEEE80211_HE_MAC_CAP0_HTC_HE ; +if (i ==NL80211_IFTYPE_STATION ) + mac_cap_info[1 ] =IEEE80211_HE_MAC_CAP1_TF_MAC_PAD_DUR_16US ; + mac_cap_info[2 ] =IEEE80211_HE_MAC_CAP2_ALL_ACK | +IEEE80211_HE_MAC_CAP2_BSR ; + mac_cap_info[3 ] =IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_EXT_2 ; +if (i ==NL80211_IFTYPE_AP ) + mac_cap_info[3 ] |=IEEE80211_HE_MAC_CAP3_OMI_CONTROL ; + mac_cap_info[4 ] =IEEE80211_HE_MAC_CAP4_OPS | +IEEE80211_HE_MAC_CAP4_AMSDU_IN_AMPDU ; +if (i ==NL80211_IFTYPE_STATION ) + mac_cap_info[5 ] =IEEE80211_HE_MAC_CAP5_HT_VHT_TRIG_FRAME_RX ; + phy_cap_info[0 ] =IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G | +IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G ; + phy_cap_info[1 ] =IEEE80211_HE_PHY_CAP1_DEVICE_CLASS_A | +IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD | +IEEE80211_HE_PHY_CAP1_HE_LTF_AND_GI_FOR_HE_PPDUS_0_8US ; + phy_cap_info[2 ] = IEEE80211_HE_PHY_CAP2_NDP_4x_LTF_AND_3_2US | +IEEE80211_HE_PHY_CAP2_STBC_TX_UNDER_80MHZ | +IEEE80211_HE_PHY_CAP2_STBC_RX_UNDER_80MHZ | +IEEE80211_HE_PHY_CAP2_DOPPLER_TX ; + phy_cap_info[3 ] =IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_RX_16_QAM ; +if (i ==NL80211_IFTYPE_STATION ) + phy_cap_info[3 ] |=IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_TX_16_QAM | +IEEE80211_HE_PHY_CAP3_DCM_MAX_TX_NSS_2 ; +if (i ==NL80211_IFTYPE_AP ) + phy_cap_info[3 ] |=IEEE80211_HE_PHY_CAP3_RX_PARTIAL_BW_SU_IN_20MHZ_MU ; + phy_cap_info[4 ] =IEEE80211_HE_PHY_CAP4_SU_BEAMFORMEE | +IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_UNDER_80MHZ_4 ; + phy_cap_info[5 ] = no_ng16 ?0 : +IEEE80211_HE_PHY_CAP5_NG16_SU_FEEDBACK | +IEEE80211_HE_PHY_CAP5_NG16_MU_FEEDBACK ; + phy_cap_info[6 ] =IEEE80211_HE_PHY_CAP6_CODEBOOK_SIZE_42_SU | +IEEE80211_HE_PHY_CAP6_CODEBOOK_SIZE_75_MU | +IEEE80211_HE_PHY_CAP6_TRIG_SU_BEAMFORMING_FB | +IEEE80211_HE_PHY_CAP6_PARTIAL_BW_EXT_RANGE ; + phy_cap_info[7 ] =IEEE80211_HE_PHY_CAP7_POWER_BOOST_FACTOR_SUPP | +IEEE80211_HE_PHY_CAP7_HE_SU_MU_PPDU_4XLTF_AND_08_US_GI | +IEEE80211_HE_PHY_CAP7_MAX_NC_1 ; + phy_cap_info[8 ] =IEEE80211_HE_PHY_CAP8_HE_ER_SU_PPDU_4XLTF_AND_08_US_GI | +IEEE80211_HE_PHY_CAP8_HE_ER_SU_1XLTF_AND_08_US_GI | +IEEE80211_HE_PHY_CAP8_DCM_MAX_RU_996 ; + phy_cap_info[9 ] =IEEE80211_HE_PHY_CAP9_LONGER_THAN_16_SIGB_OFDM_SYM | +IEEE80211_HE_PHY_CAP9_RX_1024_QAM_LESS_THAN_242_TONE_RU | +IEEE80211_HE_PHY_CAP9_RX_FULL_BW_SU_USING_MU_WITH_COMP_SIGB | +IEEE80211_HE_PHY_CAP9_RX_FULL_BW_SU_USING_MU_WITH_NON_COMP_SIGB | +IEEE80211_HE_PHY_CAP9_NOMIMAL_PKT_PADDING_16US ; +if (i ==NL80211_IFTYPE_STATION ) + phy_cap_info[9 ] |=IEEE80211_HE_PHY_CAP9_TX_1024_QAM_LESS_THAN_242_TONE_RU ; + he_cap->he_mcs_nss_supp.rx_mcs_80 =cpu_to_le16 (mcs_map); + he_cap->he_mcs_nss_supp.tx_mcs_80 =cpu_to_le16 (mcs_map); + + idx++; + } + + sband->iftype_data = iftype_data; + sband->n_iftype_data = idx; +} + +static int rtw89_core_set_supported_band (struct rtw89_dev *rtwdev) +{ +struct ieee80211_hw *hw = rtwdev->hw; +struct ieee80211_supported_band *sband_2ghz =NULL , *sband_5ghz =NULL ; +u32 size =sizeof (struct ieee80211_supported_band ); + + sband_2ghz =kmemdup (&rtw89_sband_2ghz, size,GFP_KERNEL ); +if (!sband_2ghz) + goto err; +rtw89_init_ht_cap (rtwdev, &sband_2ghz->ht_cap); +rtw89_init_he_cap (rtwdev,NL80211_BAND_2GHZ , sband_2ghz); + hw->wiphy->bands[NL80211_BAND_2GHZ ] = sband_2ghz; + + sband_5ghz =kmemdup (&rtw89_sband_5ghz, size,GFP_KERNEL ); +if (!sband_5ghz) + goto err; +rtw89_init_ht_cap (rtwdev, &sband_5ghz->ht_cap); +rtw89_init_vht_cap (rtwdev, &sband_5ghz->vht_cap); +rtw89_init_he_cap (rtwdev,NL80211_BAND_5GHZ , sband_5ghz); + hw->wiphy->bands[NL80211_BAND_5GHZ ] = sband_5ghz; + +return 0 ; + +err: + hw->wiphy->bands[NL80211_BAND_2GHZ ] =NULL ; + hw->wiphy->bands[NL80211_BAND_5GHZ ] =NULL ; +if (sband_2ghz) +kfree (sband_2ghz->iftype_data); +if (sband_5ghz) +kfree (sband_5ghz->iftype_data); +kfree (sband_2ghz); +kfree (sband_5ghz); +return -ENOMEM ; +} + +static void rtw89_core_clr_supported_band (struct rtw89_dev *rtwdev) +{ +struct ieee80211_hw *hw = rtwdev->hw; + +kfree (hw->wiphy->bands[NL80211_BAND_2GHZ ]->iftype_data); +kfree (hw->wiphy->bands[NL80211_BAND_5GHZ ]->iftype_data); +kfree (hw->wiphy->bands[NL80211_BAND_2GHZ ]); +kfree (hw->wiphy->bands[NL80211_BAND_5GHZ ]); + hw->wiphy->bands[NL80211_BAND_2GHZ ] =NULL ; + hw->wiphy->bands[NL80211_BAND_5GHZ ] =NULL ; +} + +static void rtw89_core_ppdu_sts_init (struct rtw89_dev *rtwdev) +{ +int i; + +for (i =0 ; i <RTW89_PHY_MAX ; i++) +skb_queue_head_init (&rtwdev->ppdu_sts.rx_queue[i]); +for (i =0 ; i <RTW89_PHY_MAX ; i++) + rtwdev->ppdu_sts.curr_rx_ppdu_cnt[i] =U8_MAX ; +} + +int rtw89_core_start (struct rtw89_dev *rtwdev) +{ +int ret; + + rtwdev->mac.qta_mode =RTW89_QTA_SCC ; + ret =rtw89_mac_init (rtwdev); +if (ret) { +rtw89_err (rtwdev,"mac init fail, ret:%d\n" , ret); +return ret; + } + +rtw89_btc_ntfy_poweron (rtwdev); + +/* efuse process */ + +/* pre-config BB/RF, BB reset/RFC reset */ +rtw89_mac_disable_bb_rf (rtwdev); +rtw89_mac_enable_bb_rf (rtwdev); +rtw89_phy_init_bb_reg (rtwdev); +rtw89_phy_init_rf_reg (rtwdev); + +rtw89_btc_ntfy_init (rtwdev,BTC_MODE_NORMAL ); + +rtw89_phy_dm_init (rtwdev); + +rtw89_mac_cfg_ppdu_status (rtwdev,RTW89_MAC_0 , true); +rtw89_mac_update_rts_threshold (rtwdev,RTW89_MAC_0 ); + + ret =rtw89_hci_start (rtwdev); +if (ret) { +rtw89_err (rtwdev,"failed to start hci\n" ); +return ret; + } + +ieee80211_queue_delayed_work (rtwdev->hw, &rtwdev->track_work, +RTW89_TRACK_WORK_PERIOD ); + +set_bit (RTW89_FLAG_RUNNING , rtwdev->flags); + +rtw89_btc_ntfy_radio_state (rtwdev,BTC_RFCTRL_WL_ON ); +rtw89_fw_h2c_fw_log (rtwdev, rtwdev->fw.fw_log_enable); + +return 0 ; +} + +void rtw89_core_stop (struct rtw89_dev *rtwdev) +{ +struct rtw89_btc *btc = &rtwdev->btc; + +/* Prvent to stop twice; enter_ips and ops_stop */ +if (!test_bit (RTW89_FLAG_RUNNING , rtwdev->flags)) +return ; + +rtw89_btc_ntfy_radio_state (rtwdev,BTC_RFCTRL_WL_OFF ); + +clear_bit (RTW89_FLAG_RUNNING , rtwdev->flags); + +mutex_unlock (&rtwdev->mutex); + +cancel_work_sync (&rtwdev->c2h_work); +cancel_work_sync (&btc->eapol_notify_work); +cancel_work_sync (&btc->arp_notify_work); +cancel_work_sync (&btc->dhcp_notify_work); +cancel_work_sync (&btc->wl_sta_notify_work); +cancel_delayed_work_sync (&rtwdev->txq_reinvoke_work); +cancel_delayed_work_sync (&rtwdev->track_work); +cancel_delayed_work_sync (&rtwdev->coex_act1_work); +cancel_delayed_work_sync (&rtwdev->coex_bt_devinfo_work); +cancel_delayed_work_sync (&rtwdev->coex_rfk_chk_work); +cancel_delayed_work_sync (&rtwdev->cfo_track_work); + +mutex_lock (&rtwdev->mutex); + +rtw89_btc_ntfy_poweroff (rtwdev); +rtw89_hci_flush_queues (rtwdev,BIT (rtwdev->hw->queues) -1 , true); +rtw89_mac_flush_txq (rtwdev,BIT (rtwdev->hw->queues) -1 , true); +rtw89_hci_stop (rtwdev); +rtw89_hci_deinit (rtwdev); +rtw89_mac_pwr_off (rtwdev); +rtw89_hci_reset (rtwdev); +} + +int rtw89_core_init (struct rtw89_dev *rtwdev) +{ +struct rtw89_btc *btc = &rtwdev->btc; +int ret; + +INIT_LIST_HEAD (&rtwdev->ba_list); +INIT_LIST_HEAD (&rtwdev->rtwvifs_list); +INIT_WORK (&rtwdev->ba_work, rtw89_core_ba_work); +INIT_WORK (&rtwdev->txq_work, rtw89_core_txq_work); +INIT_DELAYED_WORK (&rtwdev->txq_reinvoke_work, rtw89_core_txq_reinvoke_work); +INIT_DELAYED_WORK (&rtwdev->track_work, rtw89_track_work); +INIT_DELAYED_WORK (&rtwdev->coex_act1_work, rtw89_coex_act1_work); +INIT_DELAYED_WORK (&rtwdev->coex_bt_devinfo_work, rtw89_coex_bt_devinfo_work); +INIT_DELAYED_WORK (&rtwdev->coex_rfk_chk_work, rtw89_coex_rfk_chk_work); +INIT_DELAYED_WORK (&rtwdev->cfo_track_work, rtw89_phy_cfo_track_work); + rtwdev->txq_wq =alloc_workqueue ("rtw89_tx_wq" ,WQ_UNBOUND |WQ_HIGHPRI ,0 ); +spin_lock_init (&rtwdev->ba_lock); +mutex_init (&rtwdev->mutex); +mutex_init (&rtwdev->rf_mutex); + rtwdev->total_sta_assoc =0 ; + +INIT_WORK (&rtwdev->c2h_work, rtw89_fw_c2h_work); +skb_queue_head_init (&rtwdev->c2h_queue); +rtw89_core_ppdu_sts_init (rtwdev); +rtw89_traffic_stats_init (rtwdev, &rtwdev->stats); + + rtwdev->ps_mode =rtw89_update_ps_mode (rtwdev); + rtwdev->hal.rx_fltr =DEFAULT_AX_RX_FLTR ; + +INIT_WORK (&btc->eapol_notify_work, rtw89_btc_ntfy_eapol_packet_work); +INIT_WORK (&btc->arp_notify_work, rtw89_btc_ntfy_arp_packet_work); +INIT_WORK (&btc->dhcp_notify_work, rtw89_btc_ntfy_dhcp_packet_work); +INIT_WORK (&btc->wl_sta_notify_work, rtw89_btc_ntfy_wl_sta_work); + + ret =rtw89_load_firmware (rtwdev); +if (ret) { +rtw89_warn (rtwdev,"no firmware loaded\n" ); +return ret; + } +rtw89_ser_init (rtwdev); + +return 0 ; +} +EXPORT_SYMBOL (rtw89_core_init); + +void rtw89_core_deinit (struct rtw89_dev *rtwdev) +{ +rtw89_ser_deinit (rtwdev); +rtw89_unload_firmware (rtwdev); + +destroy_workqueue (rtwdev->txq_wq); +mutex_destroy (&rtwdev->rf_mutex); +mutex_destroy (&rtwdev->mutex); +} +EXPORT_SYMBOL (rtw89_core_deinit); + +static void rtw89_read_chip_ver (struct rtw89_dev *rtwdev) +{ +u8 cv; + + cv =rtw89_read32_mask (rtwdev,R_AX_SYS_CFG1 ,B_AX_CHIP_VER_MASK ); +if (cv <=CHIP_CBV ) { +if (rtw89_read32 (rtwdev,R_AX_GPIO0_7_FUNC_SEL ) ==RTW89_R32_DEAD ) + cv =CHIP_CAV ; +else + cv =CHIP_CBV ; + } + + rtwdev->hal.cv = cv; +} + +static int rtw89_chip_efuse_info_setup (struct rtw89_dev *rtwdev) +{ +int ret; + + ret =rtw89_mac_partial_init (rtwdev); +if (ret) +return ret; + + ret =rtw89_parse_efuse_map (rtwdev); +if (ret) +return ret; + + ret =rtw89_parse_phycap_map (rtwdev); +if (ret) +return ret; + + ret =rtw89_mac_setup_phycap (rtwdev); +if (ret) +return ret; + +rtw89_mac_pwr_off (rtwdev); + +return 0 ; +} + +static int rtw89_chip_board_info_setup (struct rtw89_dev *rtwdev) +{ +rtw89_chip_fem_setup (rtwdev); + +return 0 ; +} + +int rtw89_chip_info_setup (struct rtw89_dev *rtwdev) +{ +int ret; + +rtw89_read_chip_ver (rtwdev); + + ret =rtw89_wait_firmware_completion (rtwdev); +if (ret) { +rtw89_err (rtwdev,"failed to wait firmware completion\n" ); +return ret; + } + + ret =rtw89_fw_recognize (rtwdev); +if (ret) { +rtw89_err (rtwdev,"failed to recognize firmware\n" ); +return ret; + } + + ret =rtw89_chip_efuse_info_setup (rtwdev); +if (ret) +return ret; + + ret =rtw89_chip_board_info_setup (rtwdev); +if (ret) +return ret; + +return 0 ; +} +EXPORT_SYMBOL (rtw89_chip_info_setup); + +static int rtw89_core_register_hw (struct rtw89_dev *rtwdev) +{ +struct ieee80211_hw *hw = rtwdev->hw; +struct rtw89_efuse *efuse = &rtwdev->efuse; +int ret; +int tx_headroom =IEEE80211_HT_CTL_LEN ; + + hw->vif_data_size =sizeof (struct rtw89_vif ); + hw->sta_data_size =sizeof (struct rtw89_sta ); + hw->txq_data_size =sizeof (struct rtw89_txq ); + +SET_IEEE80211_PERM_ADDR (hw, efuse->addr); + + hw->extra_tx_headroom = tx_headroom; + hw->queues =IEEE80211_NUM_ACS ; + hw->max_rx_aggregation_subframes =RTW89_MAX_RX_AGG_NUM ; + hw->max_tx_aggregation_subframes =RTW89_MAX_TX_AGG_NUM ; + +ieee80211_hw_set (hw,SIGNAL_DBM ); +ieee80211_hw_set (hw,HAS_RATE_CONTROL ); +ieee80211_hw_set (hw,MFP_CAPABLE ); +ieee80211_hw_set (hw,REPORTS_TX_ACK_STATUS ); +ieee80211_hw_set (hw,AMPDU_AGGREGATION ); +ieee80211_hw_set (hw,RX_INCLUDES_FCS ); +ieee80211_hw_set (hw,TX_AMSDU ); +ieee80211_hw_set (hw,SUPPORT_FAST_XMIT ); +ieee80211_hw_set (hw,SUPPORTS_AMSDU_IN_AMPDU ); +ieee80211_hw_set (hw,SUPPORTS_PS ); +ieee80211_hw_set (hw,SUPPORTS_DYNAMIC_PS ); + + hw->wiphy->interface_modes =BIT (NL80211_IFTYPE_STATION ); + hw->wiphy->available_antennas_tx =BIT (rtwdev->chip->rf_path_num) -1 ; + hw->wiphy->available_antennas_rx =BIT (rtwdev->chip->rf_path_num) -1 ; + + hw->wiphy->features |=NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR ; + +wiphy_ext_feature_set (hw->wiphy,NL80211_EXT_FEATURE_CAN_REPLACE_PTK0 ); + + ret =rtw89_core_set_supported_band (rtwdev); +if (ret) { +rtw89_err (rtwdev,"failed to set supported band\n" ); +return ret; + } + + hw->wiphy->reg_notifier = rtw89_regd_notifier; + hw->wiphy->sar_capa = &rtw89_sar_capa; + + ret =ieee80211_register_hw (hw); +if (ret) { +rtw89_err (rtwdev,"failed to register hw\n" ); + goto err; + } + + ret =rtw89_regd_init (rtwdev, rtw89_regd_notifier); +if (ret) { +rtw89_err (rtwdev,"failed to init regd\n" ); + goto err; + } + +return 0 ; + +err: +return ret; +} + +static void rtw89_core_unregister_hw (struct rtw89_dev *rtwdev) +{ +struct ieee80211_hw *hw = rtwdev->hw; + +ieee80211_unregister_hw (hw); +rtw89_core_clr_supported_band (rtwdev); +} + +int rtw89_core_register (struct rtw89_dev *rtwdev) +{ +int ret; + + ret =rtw89_core_register_hw (rtwdev); +if (ret) { +rtw89_err (rtwdev,"failed to register core hw\n" ); +return ret; + } + +rtw89_debugfs_init (rtwdev); + +return 0 ; +} +EXPORT_SYMBOL (rtw89_core_register); + +void rtw89_core_unregister (struct rtw89_dev *rtwdev) +{ +rtw89_core_unregister_hw (rtwdev); +} +EXPORT_SYMBOL (rtw89_core_unregister); + +MODULE_AUTHOR ("Realtek Corporation" ); +MODULE_DESCRIPTION ("Realtek 802.11ax wireless core module" ); +MODULE_LICENSE ("Dual BSD/GPL" );
drivers/net/wireless/realtek/rtw89/core.h new file +3360 -0
572 lines not on this page · raw message
drivers/net/wireless/realtek/rtw89/txrx.h new file +393 -0
393 lines not on this page · raw message
drivers/net/wireless/realtek/rtw89/util.h new file +17 -0
Lines of this file are not shown. View the raw message.
The rest of this diff is not shown. View the raw message.
Signature · 1 line
2.25.1
Part of the diff is not shown here. View the raw message.
Re: [PATCH v6 03/24] rtw89: add core and trx files
Quoted 48 lines · Ping-Ke Shih
Implement main flows that contains register/unregister mac80211 hw with hardware capability, power on/off sequence, STA state actions, and TX/RX path.
The chip info is read from efuse while probing PCI, and then it can be used to induce supported channel, band, bitrate, ht/vht/he capability, and etc. Then, we register hardware with these capabilities.
When network interface is up, driver does power-on sequence to enable MAC, BB and RF function blocks. Oppositely, do power-off sequence when interface is going to down.
To maintain STA state, five callbacks are implemented -- add, assoc, disassoc, disconnect and remove. In which state, driver tells firmware STA info via H2C.
TX flow:
When a SKB is going to be transmitted, we must know its type first. If the type is mgmt or fwcmd made by driver, SKB is queued into corresponding DMA channel and PCI ring. The other type is data frame that is more complex, because it needs to establish BA session to have better throughput with AMPDU and AMSDU.
In order to have better PCI DMA efficiency, we don't kick off DMA every SKB. With wake TX queue, kick off DMA after a bunch of SKBs are written. To achieve this, we have two HCI ops --tx_writeandtx_kick_off.BA establishment work:
For data frames, we start to establish BA session if the STA is associated with APMDU capability and the TID session isn't established, and then the BA work is used to ask mac80211 to start AMPDU actions. Driver implements AMPDU action callbacks to know the session is established, so that we can setAGG_ENbit in TX descriptor to enable AMPDU.RX flow:
When a RX SKB is delivered from PCI,rtw89_core_rx()process it depneds on its type -- WIFI, C2H or PPDU. If type is C2H, it's queued into a C2H queue, and wake a work to handle the C2H packet. If type is WIFI, it's a normal RX packet. When mgmt or data frame is received, it is queued into pending RX SKB queue to wait for corresponding PPDU packet (another RX packet with PPDU type) to fill itsrx_status, like RSSI. And, then indicate this packet to mac80211. When control frame is received, indicate it to mac80211 immediately.Track work:
Use track work to monitor PHY status to know the changes of environment, and then update RA status or do RFK accordingly.Signed-off-by Ping-Ke Shih <[email protected]>
[...]
drivers/net/wireless/realtek/rtw89/txrx.h line 391
Quoted 6 lines
drivers/net/wireless/realtek/rtw89/txrx.h new file +6
-0+static __always_inline void RTW89_SET_TXWD(u8 *txdesc, u32 val, u8 offset, u32 mask) +{ +u32 *txd32 = (u32 *)txdesc; + +le32p_replace_bits ((__le32 *)(txd32 + offset), val, mask); +}
I'm not convinced about this either, please just use inline.
Also having functions in upper case is not really something I recommend.
Signature · 3 lines
https://patchwork.kernel.org/project/linux-wireless/list/ https://wireless.wiki.kernel.org/en/developers/documentation/submittingpatches
Pkshih
maintainerRE: [PATCH v6 03/24] rtw89: add core and trx files
Quoted 62 lines
-----Original Message-----
From: [email protected] <[email protected]> On Behalf Of Kalle Valo
Sent: Saturday, October 2, 2021 12:26 AM
To: Pkshih <[email protected]>
Cc: [email protected]
Subject: Re: [PATCH v6 03/24] rtw89: add core and trx filesQuoted 48 lines · Ping-Ke Shih
Implement main flows that contains register/unregister mac80211 hw with hardware capability, power on/off sequence, STA state actions, and TX/RX path.
The chip info is read from efuse while probing PCI, and then it can be used to induce supported channel, band, bitrate, ht/vht/he capability, and etc. Then, we register hardware with these capabilities.
When network interface is up, driver does power-on sequence to enable MAC, BB and RF function blocks. Oppositely, do power-off sequence when interface is going to down.
To maintain STA state, five callbacks are implemented -- add, assoc, disassoc, disconnect and remove. In which state, driver tells firmware STA info via H2C.
TX flow:
When a SKB is going to be transmitted, we must know its type first. If the type is mgmt or fwcmd made by driver, SKB is queued into corresponding DMA channel and PCI ring. The other type is data frame that is more complex, because it needs to establish BA session to have better throughput with AMPDU and AMSDU.
In order to have better PCI DMA efficiency, we don't kick off DMA every SKB. With wake TX queue, kick off DMA after a bunch of SKBs are written. To achieve this, we have two HCI ops --tx_writeandtx_kick_off.BA establishment work:
For data frames, we start to establish BA session if the STA is associated with APMDU capability and the TID session isn't established, and then the BA work is used to ask mac80211 to start AMPDU actions. Driver implements AMPDU action callbacks to know the session is established, so that we can setAGG_ENbit in TX descriptor to enable AMPDU.RX flow:
When a RX SKB is delivered from PCI,rtw89_core_rx()process it depneds on its type -- WIFI, C2H or PPDU. If type is C2H, it's queued into a C2H queue, and wake a work to handle the C2H packet. If type is WIFI, it's a normal RX packet. When mgmt or data frame is received, it is queued into pending RX SKB queue to wait for corresponding PPDU packet (another RX packet with PPDU type) to fill itsrx_status, like RSSI. And, then indicate this packet to mac80211. When control frame is received, indicate it to mac80211 immediately.Track work:
Use track work to monitor PHY status to know the changes of environment, and then update RA status or do RFK accordingly.Signed-off-by Ping-Ke Shih <[email protected]>[...]
Quoted 8 lines
drivers/net/wireless/realtek/rtw89/txrx.h line 391
Quoted 6 lines
drivers/net/wireless/realtek/rtw89/txrx.h new file +6
-0+static __always_inline void RTW89_SET_TXWD(u8 *txdesc, u32 val, u8 offset, u32 mask) +{ +u32 *txd32 = (u32 *)txdesc; + +le32p_replace_bits ((__le32 *)(txd32 + offset), val, mask); +}I'm not convinced about this either, please just use inline.
This is because 'mask' argument of le32p_replace_bits() must be constant only. If I use inline and build this driver with ccflags-y += -Os, compiler reports errors:
In function 'field_multiplier',
inlined from 'le32_encode_bits' at ./include/linux/bitfield.h:154:1,
inlined from 'le32p_replace_bits' at ./include/linux/bitfield.h:154:1,
inlined from 'RTW89_SET_FWCMD_UA32.constprop' at /work/git-root/rtwlan/rtw89/fw.h:1397:2:
./include/linux/bitfield.h:119:3: error: call to '__bad_mask' declared with attribute error: bad bitfield mask
119 | __bad_mask();
| ^~~~~~~~~~~~I check the implement of le32p_replace_bits(), it looks like
static __always_inline void type##p_replace_bits(__##type *p, \
base val, base field) \
{ \
*p = (*p & ~to(field)) | type##_encode_bits(val, field); \}
So, I imitate the function to use __always_inline, and then it works.
Do you think I don't need to consider the case of Os? But, -Os seems a standard option of Linux kernel.
ifdef CONFIG_CC_OPTIMIZE_FOR_PERFORMANCEKBUILD_CFLAGS += -O2
else ifdef CONFIG_CC_OPTIMIZE_FOR_PERFORMANCE_O3KBUILD_CFLAGS += -O3
else ifdef CONFIG_CC_OPTIMIZE_FOR_SIZEKBUILD_CFLAGS += -Os
endif
--
Ping-Ke
Re: [PATCH v6 03/24] rtw89: add core and trx files
Quoted 97 lines · Pkshih
Quoted 62 lines
-----Original Message-----
From: [email protected] <[email protected]> On
Behalf Of Kalle Valo
Sent: Saturday, October 2, 2021 12:26 AM
To: Pkshih <[email protected]>
Cc: [email protected]
Subject: Re: [PATCH v6 03/24] rtw89: add core and trx filesQuoted 48 lines · Ping-Ke Shih
Implement main flows that contains register/unregister mac80211 hw with hardware capability, power on/off sequence, STA state actions, and TX/RX path.
The chip info is read from efuse while probing PCI, and then it can be used to induce supported channel, band, bitrate, ht/vht/he capability, and etc. Then, we register hardware with these capabilities.
When network interface is up, driver does power-on sequence to enable MAC, BB and RF function blocks. Oppositely, do power-off sequence when interface is going to down.
To maintain STA state, five callbacks are implemented -- add, assoc, disassoc, disconnect and remove. In which state, driver tells firmware STA info via H2C.
TX flow:
When a SKB is going to be transmitted, we must know its type first. If the type is mgmt or fwcmd made by driver, SKB is queued into corresponding DMA channel and PCI ring. The other type is data frame that is more complex, because it needs to establish BA session to have better throughput with AMPDU and AMSDU.
In order to have better PCI DMA efficiency, we don't kick off DMA every SKB. With wake TX queue, kick off DMA after a bunch of SKBs are written. To achieve this, we have two HCI ops --tx_writeandtx_kick_off.BA establishment work:
For data frames, we start to establish BA session if the STA is associated with APMDU capability and the TID session isn't established, and then the BA work is used to ask mac80211 to start AMPDU actions. Driver implements AMPDU action callbacks to know the session is established, so that we can setAGG_ENbit in TX descriptor to enable AMPDU.RX flow:
When a RX SKB is delivered from PCI,rtw89_core_rx()process it depneds on its type -- WIFI, C2H or PPDU. If type is C2H, it's queued into a C2H queue, and wake a work to handle the C2H packet. If type is WIFI, it's a normal RX packet. When mgmt or data frame is received, it is queued into pending RX SKB queue to wait for corresponding PPDU packet (another RX packet with PPDU type) to fill itsrx_status, like RSSI. And, then indicate this packet to mac80211. When control frame is received, indicate it to mac80211 immediately.Track work:
Use track work to monitor PHY status to know the changes of environment, and then update RA status or do RFK accordingly.Signed-off-by Ping-Ke Shih <[email protected]>[...]
Quoted 9 lines
drivers/net/wireless/realtek/rtw89/txrx.h line 391
Quoted 7 lines
+static
__always_inlinevoidRTW89_SET_TXWD(u8 *txdesc, u32 val, u8 offset, u32 mask)drivers/net/wireless/realtek/rtw89/txrx.h new file +5
-0+{ +u32 *txd32 = (u32 *)txdesc; + +le32p_replace_bits ((__le32 *)(txd32 + offset), val, mask); +}I'm not convinced about this either, please just use inline.
This is because 'mask' argument of
le32p_replace_bits()must be constant only. If I use inline and build this driver with ccflags-y += -Os, compiler reports errors:In function '
field_multiplier',inlined from 'le32_encode_bits' at ./include/linux/bitfield.h:154:1, inlined from 'le32p_replace_bits' at ./include/linux/bitfield.h:154:1, inlined from 'RTW89_SET_FWCMD_UA32.constprop' at /work/git-root/rtwlan/rtw89/fw.h:1397:2: ./include/linux/bitfield.h:119:3: error: call to '__bad_mask' declared with attribute error: bad bitfield mask 119 | __bad_mask(); | ^~~~~~~~~~~~I check the implement of
le32p_replace_bits(), it looks likestatic __always_inline void type##p_replace_bits(__##type *p, \ base val, base field) \ { \ *p = (*p & ~to(field)) | type##_encode_bits(val, field); \}
So, I imitate the function to use
__always_inline, and then it works.Do you think I don't need to consider the case of Os? But, -Os seems a standard option of Linux kernel.
Quoted 7 lines
ifdef
CONFIG_CC_OPTIMIZE_FOR_PERFORMANCEKBUILD_CFLAGS+= -O2
else ifdefCONFIG_CC_OPTIMIZE_FOR_PERFORMANCE_O3KBUILD_CFLAGS+= -O3
else ifdefCONFIG_CC_OPTIMIZE_FOR_SIZEKBUILD_CFLAGS+= -Os
endif
Yeah, we need to support -Os.
Arnd, what do you suggest? Is __always_inline good solution for this? I think we should at least add a comment explaining why it's needed.
Signature · 3 lines
https://patchwork.kernel.org/project/linux-wireless/list/ https://wireless.wiki.kernel.org/en/developers/documentation/submittingpatches
Re: [PATCH v6 03/24] rtw89: add core and trx files
Quoted 46 lines · Kalle Valo
Quoted 37 lines · Pkshih
Quoted 11 lines
From: [email protected]
drivers/net/wireless/realtek/rtw89/txrx.h line 391
Quoted 7 lines
+static
__always_inlinevoidRTW89_SET_TXWD(u8 *txdesc, u32 val, u8 offset, u32 mask)drivers/net/wireless/realtek/rtw89/txrx.h new file +5
-0+{ +u32 *txd32 = (u32 *)txdesc; + +le32p_replace_bits ((__le32 *)(txd32 + offset), val, mask); +}I'm not convinced about this either, please just use inline.
This is because 'mask' argument of
le32p_replace_bits()must be constant only. If I use inline and build this driver with ccflags-y += -Os, compiler reports errors:In function '
field_multiplier',inlined from 'le32_encode_bits' at ./include/linux/bitfield.h:154:1, inlined from 'le32p_replace_bits' at ./include/linux/bitfield.h:154:1, inlined from 'RTW89_SET_FWCMD_UA32.constprop' at /work/git-root/rtwlan/rtw89/fw.h:1397:2: ./include/linux/bitfield.h:119:3: error: call to '__bad_mask' declared with attribute error: bad bitfield mask 119 | __bad_mask(); | ^~~~~~~~~~~~I check the implement of
le32p_replace_bits(), it looks likestatic __always_inline void type##p_replace_bits(__##type *p, \ base val, base field) \ { \ *p = (*p & ~to(field)) | type##_encode_bits(val, field); \}
So, I imitate the function to use
__always_inline, and then it works.Do you think I don't need to consider the case of Os? But, -Os seems a standard option of Linux kernel.
Quoted 7 lines
ifdef
CONFIG_CC_OPTIMIZE_FOR_PERFORMANCEKBUILD_CFLAGS+= -O2
else ifdefCONFIG_CC_OPTIMIZE_FOR_PERFORMANCE_O3KBUILD_CFLAGS+= -O3
else ifdefCONFIG_CC_OPTIMIZE_FOR_SIZEKBUILD_CFLAGS+= -Os
endif
Quoted 4 lines
Yeah, we need to support -Os.
Arnd, what do you suggest? Is
__always_inlinegood solution for this? I think we should at least add a comment explaining why it's needed.
__always_inline can make sense to force the compiler to behave sanely if it doesn't work it out by itself, and I think that is how this function was meant to be used: the __compiletime_error in bitfield.h is intended to find any callers that have a non-constant argument, because that would result in horrible code.
I would suggest looking at the object code that you get with -Os after the added __always_inline, just to make sure that this isn't also horrible.
Looking at the driver code, as in
+#define RTW89_SET_TXWD_BODY_WP_OFFSET(txdesc, val) \
+ RTW89_SET_TXWD(txdesc, val, 0x00, GENMASK(31, 24)) +#define RTW89_SET_TXWD_BODY_MORE_DATA(txdesc, val) \
+ RTW89_SET_TXWD(txdesc, val, 0x00, BIT(23)) +#define RTW89_SET_TXWD_BODY_WD_INFO_EN(txdesc, val) \
+ RTW89_SET_TXWD(txdesc, val, 0x00, BIT(22)) +#define RTW89_SET_TXWD_BODY_FW_DL(txdesc, val) \
+ RTW89_SET_TXWD(txdesc, val, 0x00, BIT(20))
I would personally write this without the wrappers, instead defining the bitmask macros as the masks and then open-coding the le32p_replace_bits() calls instead, which I would find more intuitive while it avoids the problem with the bitmasks.
Going back one more step, I see that that rtw89_core_fill_txdesc() manipulates the descriptor fields in-memory, which also seems like a bad idea: The descriptor is mapped as cache-coherent, so on machines with no coherent DMA (i.e. most ARM or MIPS machines), that is uncached memory, and writing the descriptor using a series of read-modify-write cycles on uncached memory will be awfully slow. Maybe the answer is to just completely replace the descriptor access.
Arnd
Pkshih
maintainerRE: [PATCH v6 03/24] rtw89: add core and trx files
Quoted 68 lines
-----Original Message-----
From: Arnd Bergmann <[email protected]>
Sent: Tuesday, October 5, 2021 4:42 PM
To: Kalle Valo <[email protected]>
Cc: Pkshih <[email protected]>; Arnd Bergmann <[email protected]>; [email protected]
Subject: Re: [PATCH v6 03/24] rtw89: add core and trx filesQuoted 13 lines · Kalle Valo
Quoted 12 lines · Pkshih
Quoted 11 lines
From: [email protected]
drivers/net/wireless/realtek/rtw89/txrx.h line 391
Quoted 7 lines
+static
__always_inlinevoidRTW89_SET_TXWD(u8 *txdesc, u32 val, u8 offset, u32 mask)drivers/net/wireless/realtek/rtw89/txrx.h new file +5
-0+{ +u32 *txd32 = (u32 *)txdesc; + +le32p_replace_bits ((__le32 *)(txd32 + offset), val, mask); +}I'm not convinced about this either, please just use inline.
Quoted 8 lines
Quoted 8 lines
This is because 'mask' argument of
le32p_replace_bits()must be constant only. If I use inline and build this driver with ccflags-y += -Os, compiler reports errors:In function '
field_multiplier',inlined from 'le32_encode_bits' at ./include/linux/bitfield.h:154:1, inlined from 'le32p_replace_bits' at ./include/linux/bitfield.h:154:1, inlined from 'RTW89_SET_FWCMD_UA32.constprop' at/work/git-root/rtwlan/rtw89/fw.h:1397:2:
Quoted 1 line
Quoted 1 line
./include/linux/bitfield.h:119:3: error: call to '__bad_mask' declared with attribute error:bad bitfield mask
Quoted 24 lines
Quoted 16 lines
119 | __bad_mask(); | ^~~~~~~~~~~~I check the implement of
le32p_replace_bits(), it looks likestatic __always_inline void type##p_replace_bits(__##type *p, \ base val, base field) \ { \ *p = (*p & ~to(field)) | type##_encode_bits(val, field); \}
So, I imitate the function to use
__always_inline, and then it works.Do you think I don't need to consider the case of Os? But, -Os seems a standard option of Linux kernel.
Quoted 7 lines
ifdef
CONFIG_CC_OPTIMIZE_FOR_PERFORMANCEKBUILD_CFLAGS+= -O2
else ifdefCONFIG_CC_OPTIMIZE_FOR_PERFORMANCE_O3KBUILD_CFLAGS+= -O3
else ifdefCONFIG_CC_OPTIMIZE_FOR_SIZEKBUILD_CFLAGS+= -Os
endifQuoted 4 lines
Yeah, we need to support -Os.
Arnd, what do you suggest? Is
__always_inlinegood solution for this? I think we should at least add a comment explaining why it's needed.
__always_inlinecan make sense to force the compiler to behave sanely if it doesn't work it out by itself, and I think that is how this function was meant to be used: the__compiletime_errorinbitfield.his intended to find any callers that have a non-constant argument, because that would result in horrible code.
Quoted 3 lines
I would suggest looking at the object code that you get with -Os after the added
__always_inline, just to make sure that this isn't also horrible.
I check the function rtw89_core_fill_txdesc() which uses these macros. With inline, the object code size is 0x1AF. With __always_inline and -Os, the size is 0x1A4. (x86-64 platform)
Compare the object codes side-by-side, they are almost the same except to some instructions. I think this is because the inline function I apply __always_inline contains only a simple statement.
Quoted 12 lines
Looking at the driver code, as in
drivers/net/wireless/realtek/rtw89/txrx.h new file +8
-0+#define RTW89_SET_TXWD_BODY_WP_OFFSET (txdesc, val) \ + RTW89_SET_TXWD(txdesc, val, 0x00, GENMASK(31, 24)) +#define RTW89_SET_TXWD_BODY_MORE_DATA (txdesc, val) \ + RTW89_SET_TXWD(txdesc, val, 0x00, BIT(23)) +#define RTW89_SET_TXWD_BODY_WD_INFO_EN (txdesc, val) \ + RTW89_SET_TXWD(txdesc, val, 0x00, BIT(22)) +#define RTW89_SET_TXWD_BODY_FW_DL (txdesc, val) \ + RTW89_SET_TXWD(txdesc, val, 0x00, BIT(20))
Quoted 4 lines
I would personally write this without the wrappers, instead defining the bitmask macros as the masks and then open-coding the
le32p_replace_bits()calls instead, which I would find more intuitive while it avoids the problem with the bitmasks.
Use these macros can address offset and bit fields quickly. How about I use macro instead of inline function? Like,
#define RTW89_SET_TXWD (txdesc, val, offset, mask) \
do { \
u32 *txd32 = (u32 *)txdesc; \
le32p_replace_bits((__le32 *)(txd32 + offset), val, mask); \
} while (0)Quoted 8 lines
Going back one more step, I see that that
rtw89_core_fill_txdesc()manipulates the descriptor fields in-memory, which also seems like a bad idea: The descriptor is mapped as cache-coherent, so on machines with no coherent DMA (i.e. most ARM or MIPS machines), that is uncached memory, and writing the descriptor using a series of read-modify-write cycles on uncached memory will be awfully slow. Maybe the answer is to just completely replace the descriptor access.
I'll think if we can use chached memory with single_map/unmap for descriptor. That would improve the performance.
--
Ping-Ke
Re: [PATCH v6 03/24] rtw89: add core and trx files
Quoted 15 lines · Pkshih
Quoted 11 lines
Cc Pkshih <[email protected]>; Arnd Bergmann <[email protected]>; [email protected]Quoted 3 lines
Arnd, what do you suggest? Is
__always_inlinegood solution for this? I think we should at least add a comment explaining why it's needed.
__always_inlinecan make sense to force the compiler to behave sanely if it doesn't work it out by itself, and I think that is how this function was meant to be used: the__compiletime_errorinbitfield.his intended to find any callers that have a non-constant argument, because that would result in horrible code.Quoted 3 lines
I would suggest looking at the object code that you get with -Os after the added
__always_inline, just to make sure that this isn't also horrible.
Quoted 7 lines
I check the function
rtw89_core_fill_txdesc()which uses these macros. With inline, the object code size is0x1AF. With__always_inlineand -Os, the size is0x1A4. (x86-64 platform)Compare the object codes side-by-side, they are almost the same except to some instructions. I think this is because the inline function I apply
__always_inlinecontains only a simple statement.
Ok. Did you check the output for the configuration that showed the problem as well, after adding __always_inline? There are certain compile-time options that could cause the code to become unoptimized, e.g. KASAN, in addition to the OPTIMIZE_FOR_SIZE.
Quoted 17 lines
Quoted 9 lines
drivers/net/wireless/realtek/rtw89/txrx.h new file +8
-0+#define RTW89_SET_TXWD_BODY_WP_OFFSET (txdesc, val) \ + RTW89_SET_TXWD(txdesc, val, 0x00, GENMASK(31, 24)) +#define RTW89_SET_TXWD_BODY_MORE_DATA (txdesc, val) \ + RTW89_SET_TXWD(txdesc, val, 0x00, BIT(23)) +#define RTW89_SET_TXWD_BODY_WD_INFO_EN (txdesc, val) \ + RTW89_SET_TXWD(txdesc, val, 0x00, BIT(22)) +#define RTW89_SET_TXWD_BODY_FW_DL (txdesc, val) \ + RTW89_SET_TXWD(txdesc, val, 0x00, BIT(20))Quoted 4 lines
I would personally write this without the wrappers, instead defining the bitmask macros as the masks and then open-coding the
le32p_replace_bits()calls instead, which I would find more intuitive while it avoids the problem with the bitmasks.Use these macros can address offset and bit fields quickly. How about I use macro instead of inline function? Like,
Quoted 5 lines
#define
RTW89_SET_TXWD(txdesc, val, offset, mask) \do { \ u32 *txd32 = (u32 *)txdesc; \ le32p_replace_bits((__le32 *)(txd32 + offset), val, mask); \ } while (0)
That would obviously address the immediate bug, but I think using le32p_replace_bits() directly here would actually be more readable, after you define the descriptor layout using a structure with named __le32 members to replace the offset.
Quoted 11 lines
Quoted 8 lines
Going back one more step, I see that that
rtw89_core_fill_txdesc()manipulates the descriptor fields in-memory, which also seems like a bad idea: The descriptor is mapped as cache-coherent, so on machines with no coherent DMA (i.e. most ARM or MIPS machines), that is uncached memory, and writing the descriptor using a series of read-modify-write cycles on uncached memory will be awfully slow. Maybe the answer is to just completely replace the descriptor access.I'll think if we can use chached memory with single_map/unmap for descriptor. That would improve the performance.
Using dma_unmap_single() with its cache flush may not work correctly if the descriptor fields have to be written in a particular order. Usually the last field in a descriptor contains a 'valid' bit that must not be observed by the hardware before the rest is visible. The cache flush however would not guarantee the order of the update.
It would also likely be slower than dma_alloc_coherent() on machines that have cache-coherent PCI, such as most x86.
The best way is usually to construct the descriptor one word at a time in registers, and write that word using WRITE_ONCE(), with an explict dma_wmb() before the final write that makes the descriptor valid.
Arnd
Pkshih
maintainerRE: [PATCH v6 03/24] rtw89: add core and trx files
Quoted 30 lines
-----Original Message-----
From: Arnd Bergmann <[email protected]>
Sent: Tuesday, October 5, 2021 5:59 PM
To: Pkshih <[email protected]>
Cc: Arnd Bergmann <[email protected]>; Kalle Valo <[email protected]>; [email protected]
Subject: Re: [PATCH v6 03/24] rtw89: add core and trx filesQuoted 1 line · Pkshih
Quoted 1 line
Cc Pkshih <[email protected]>; Arnd Bergmann <[email protected]>;Quoted 18 lines
Quoted 10 lines
Quoted 3 lines
Arnd, what do you suggest? Is
__always_inlinegood solution for this? I think we should at least add a comment explaining why it's needed.
__always_inlinecan make sense to force the compiler to behave sanely if it doesn't work it out by itself, and I think that is how this function was meant to be used: the__compiletime_errorinbitfield.his intended to find any callers that have a non-constant argument, because that would result in horrible code.Quoted 3 lines
I would suggest looking at the object code that you get with -Os after the added
__always_inline, just to make sure that this isn't also horrible.I check the function
rtw89_core_fill_txdesc()which uses these macros. With inline, the object code size is0x1AF. With__always_inlineand -Os, the size is0x1A4. (x86-64 platform)
Quoted 8 lines
Quoted 3 lines
Compare the object codes side-by-side, they are almost the same except to some instructions. I think this is because the inline function I apply
__always_inlinecontains only a simple statement.Ok. Did you check the output for the configuration that showed the problem as well, after adding
__always_inline? There are certain compile-time options that could cause the code to become unoptimized, e.g. KASAN, in addition to theOPTIMIZE_FOR_SIZE.
Summarize object code size of the combinations:
ccflag default -Os ====== ======= ============= inline 0x1AF X
always_inline 0x1AA 0x1A4
With default ccflag, the difference of inline and always_inline is a je/jne instruction for 'if (!desc_info->en_wd_info)'. The always_inline doesn't affect the part that use RTW89_SET_TXWD().
Compare always_inline row, the case of default ccflag uses movzbl (4 bytes), but -Os case uses mov (3 bytes).
By the results, -Os affect the object code size. always_inline doesn't affect the code, but affect the instruction (je/jne) nearby.
I use Ubuntun kernel that doesn't enable KASAN.
# CONFIG_KASAN is not set
Quoted 24 lines
Quoted 18 lines
Quoted 10 lines
(quoted diff) +8
-0+#define RTW89_SET_TXWD_BODY_WP_OFFSET(txdesc, val) \ + RTW89_SET_TXWD(txdesc, val, 0x00, GENMASK(31, 24)) +#define RTW89_SET_TXWD_BODY_MORE_DATA(txdesc, val) \ + RTW89_SET_TXWD(txdesc, val, 0x00, BIT(23)) +#define RTW89_SET_TXWD_BODY_WD_INFO_EN(txdesc, val) \ + RTW89_SET_TXWD(txdesc, val, 0x00, BIT(22)) +#define RTW89_SET_TXWD_BODY_FW_DL(txdesc, val) \ + RTW89_SET_TXWD(txdesc, val, 0x00, BIT(20))Quoted 4 lines
I would personally write this without the wrappers, instead defining the bitmask macros as the masks and then open-coding the
le32p_replace_bits()calls instead, which I would find more intuitive while it avoids the problem with the bitmasks.Use these macros can address offset and bit fields quickly. How about I use macro instead of inline function? Like,
Quoted 5 lines
#define
RTW89_SET_TXWD(txdesc, val, offset, mask) \do { \ u32 *txd32 = (u32 *)txdesc; \ le32p_replace_bits((__le32 *)(txd32 + offset), val, mask); \ } while (0)
Quoted 4 lines
That would obviously address the immediate bug, but I think using
le32p_replace_bits()directly here would actually be more readable, after you define the descriptor layout using a structure with named__le32members to replace the offset.
I will remove the wrapper and use le32p_replace_bits() directly.
I don't plan to use structure, because these data contain bit-fields. Then, I need to maintain little-/big-endian formats, like
struct foo {
#if BIG_ENDINA __le32 msb:1; __le32 rsvd:30; __le32 lsb:1; #else __le32 lsb:1; __le32 rsvd:30; __le32 msb:1;
#endif
};
Quoted 13 lines
Quoted 10 lines
Quoted 9 lines
Going back one more step, I see that that
rtw89_core_fill_txdesc()manipulates the descriptor fields in-memory, which also seems like a bad idea: The descriptor is mapped as cache-coherent, so on machines with no coherent DMA (i.e. most ARM or MIPS machines), that is uncached memory, and writing the descriptor using a series of read-modify-write cycles on uncached memory will be awfully slow. Maybe the answer is to just completely replace the descriptor access.Quoted 2 lines
I'll think if we can use chached memory with single_map/unmap for descriptor. That would improve the performance.
Quoted 6 lines
Using
dma_unmap_single()with its cache flush may not work correctly if the descriptor fields have to be written in a particular order. Usually the last field in a descriptor contains a 'valid' bit that must not be observed by the hardware before the rest is visible. The cache flush however would not guarantee the order of the update.
Is it possible to flush cache twice? Writing the fields other than 'valid' bit, and do wmb() and first flush. Then, set 'valid' bit, and do second flush.
Quoted 9 lines
It would also likely be slower than
dma_alloc_coherent()on machines that have cache-coherent PCI, such as most x86.The best way is usually to construct the descriptor one word at a time in registers, and write that word using
WRITE_ONCE(), with an explictdma_wmb()before the final write that makes the descriptor valid.
Thanks for the guideline.
Fortunately, descriptor of this hardware uses circular ring buffer with read/write index instead of 'valid' bit. To issue a packet with descriptor to hardware, we fill descriptor and fill address of skb as well, and then update write index (a register) to trigger hardware to start DMA this packet. So, I think it is possible to use dma_map_single().
Anyway, I will try both methods later.
Thank you
--
Ping-Ke
Re: [PATCH v6 03/24] rtw89: add core and trx files
Quoted 21 lines · Pkshih
Quoted 9 lines
Quoted 4 lines
Compare the object codes side-by-side, they are almost the same except to some instructions. I think this is because the inline function I apply
__always_inlinecontains only a simple statement.Ok. Did you check the output for the configuration that showed the problem as well, after adding
__always_inline? There are certain compile-time options that could cause the code to become unoptimized, e.g. KASAN, in addition to theOPTIMIZE_FOR_SIZE.Summarize object code size of the combinations:
ccflag default -Os ====== ======= ============= inline 0x1AF X
always_inline0x1AA0x1A4With default ccflag, the difference of inline and
always_inlineis a je/jne instruction for 'if (!desc_info->en_wd_info)'. Thealways_inlinedoesn't affect the part that useRTW89_SET_TXWD().
Quoted 5 lines
Compare
always_inlinerow, the case of default ccflag uses movzbl (4 bytes), but -Os case uses mov (3 bytes).By the results, -Os affect the object code size.
always_inlinedoesn't affect the code, but affect the instruction (je/jne) nearby.
Those are the known-good cases, yes.
Quoted 2 lines
I use Ubuntun kernel that doesn't enable KASAN.
#CONFIG_KASANis not set
Ah, so you test using the driver backports package on a distro kernel? While this may be a good option for your development needs, I think it is generally a good idea to also be able to test your patches against the latest mainline or linux-next kernel directly, if only to ensure that there are no obvious regressions.
Quoted 42 lines
Quoted 23 lines
Quoted 17 lines
Quoted 9 lines
(quoted diff) +8
-0+#define RTW89_SET_TXWD_BODY_WP_OFFSET(txdesc, val) \ + RTW89_SET_TXWD(txdesc, val, 0x00, GENMASK(31, 24)) +#define RTW89_SET_TXWD_BODY_MORE_DATA(txdesc, val) \ + RTW89_SET_TXWD(txdesc, val, 0x00, BIT(23)) +#define RTW89_SET_TXWD_BODY_WD_INFO_EN(txdesc, val) \ + RTW89_SET_TXWD(txdesc, val, 0x00, BIT(22)) +#define RTW89_SET_TXWD_BODY_FW_DL(txdesc, val) \ + RTW89_SET_TXWD(txdesc, val, 0x00, BIT(20))Quoted 4 lines
I would personally write this without the wrappers, instead defining the bitmask macros as the masks and then open-coding the
le32p_replace_bits()calls instead, which I would find more intuitive while it avoids the problem with the bitmasks.Use these macros can address offset and bit fields quickly. How about I use macro instead of inline function? Like,
Quoted 5 lines
#define
RTW89_SET_TXWD(txdesc, val, offset, mask) \do { \ u32 *txd32 = (u32 *)txdesc; \ le32p_replace_bits((__le32 *)(txd32 + offset), val, mask); \ } while (0)Quoted 4 lines
That would obviously address the immediate bug, but I think using
le32p_replace_bits()directly here would actually be more readable, after you define the descriptor layout using a structure with named__le32members to replace the offset.I will remove the wrapper and use
le32p_replace_bits()directly.I don't plan to use structure, because these data contain bit-fields. Then, I need to maintain little-/big-endian formats, like
struct foo {
#if BIG_ENDINA __le32 msb:1; __le32 rsvd:30; __le32 lsb:1; #else __le32 lsb:1; __le32 rsvd:30; __le32 msb:1;
Quoted 2 lines
#endif
};
Right, bitfields would not work well here, as they are generally not portable. Using an "#ifdef __BIG_ENDIAN_BITFIELD" check can work, but as you say this is really ugly.
What I was trying to suggest instead is a structure like
struct descriptor {
__le32 word0;
__le32 word1;
__le32 word2;
__le32 word3;
};And then build the descriptor like (with proper naming of the fields of course)
void fill_descriptor(struct my_device *dev, struct sk_buff *skb, volatile struct descriptor *d)
{
d->word0 = build_desc_word0(fieldA, fieldB, fieldC, fieldD);
d->word1 = build_desc_word1(fieldE, fieldF);
...
}where the build_desc_word0() functions are the ones that encode the actual layout, e.g. using the linux/bitfield.h helpers like
static inline __le32 build_desc_word0(u32 fieldA, u32 fieldB, u32 fieldC, u32 fieldD)
{
u32 word = FIELD_PREP(REG_FIELD_A, fieldA) |
FIELD_PREP(REG_FIELD_B, fieldB) | FIELD_PREP(REG_FIELD_C, fieldC) | FIELD_PREP(REG_FIELD_D, fieldD);
return cpu_to_le32(word);
}
Doing it this way has the advantage of keeping the assignment separate, which makes sure you don't accidentally introduce a read-modify-write cycle on the descriptor. This should work well on all architectures using dma_alloc_coherent() buffers.
Quoted 19 lines
Quoted 12 lines
Quoted 11 lines
Quoted 8 lines
Going back one more step, I see that that
rtw89_core_fill_txdesc()manipulates the descriptor fields in-memory, which also seems like a bad idea: The descriptor is mapped as cache-coherent, so on machines with no coherent DMA (i.e. most ARM or MIPS machines), that is uncached memory, and writing the descriptor using a series of read-modify-write cycles on uncached memory will be awfully slow. Maybe the answer is to just completely replace the descriptor access.I'll think if we can use chached memory with single_map/unmap for descriptor. That would improve the performance.
Quoted 6 lines
Using
dma_unmap_single()with its cache flush may not work correctly if the descriptor fields have to be written in a particular order. Usually the last field in a descriptor contains a 'valid' bit that must not be observed by the hardware before the rest is visible. The cache flush however would not guarantee the order of the update.
Quoted 3 lines
Is it possible to flush cache twice? Writing the fields other than 'valid' bit, and do
wmb()and first flush. Then, set 'valid' bit, and do second flush.
This could work, but it would be really expensive, since the dma-mapping API is based on ownership state transitions, so you'd have to got through dma_sync_single_for_device(), dma_sync_single_for_cpu(), and another dma_sync_single_for_device(). On machines using swiotlb(), those would in turn translate into copy operations.
Quoted 11 lines
Quoted 8 lines
It would also likely be slower than
dma_alloc_coherent()on machines that have cache-coherent PCI, such as most x86.The best way is usually to construct the descriptor one word at a time in registers, and write that word using
WRITE_ONCE(), with an explictdma_wmb()before the final write that makes the descriptor valid.Thanks for the guideline.
Quoted 7 lines
Fortunately, descriptor of this hardware uses circular ring buffer with read/write index instead of 'valid' bit. To issue a packet with descriptor to hardware, we fill descriptor and fill address of skb as well, and then update write index (a register) to trigger hardware to start DMA this packet. So, I think it is possible to use
dma_map_single().Anyway, I will try both methods later.
If you end up with the streaming mapping, I would suggest using a single dma_alloc_noncoherent(), followed by dma_sync_single_* later on, rather than multiple map/unmap calls that would need to reprogram the IOMMU. The coherent API as I explained above should be more efficient though, unless you need to do a lot of reads from the descriptors.
Arnd
Pkshih
maintainerRE: [PATCH v6 03/24] rtw89: add core and trx files
Quoted 39 lines
-----Original Message-----
From: Arnd Bergmann <[email protected]>
Sent: Wednesday, October 6, 2021 3:33 PM
To: Pkshih <[email protected]>
Cc: Arnd Bergmann <[email protected]>; Kalle Valo <[email protected]>; [email protected]
Subject: Re: [PATCH v6 03/24] rtw89: add core and trx filesQuoted 21 lines · Pkshih
Quoted 9 lines
Quoted 4 lines
Compare the object codes side-by-side, they are almost the same except to some instructions. I think this is because the inline function I apply
__always_inlinecontains only a simple statement.Ok. Did you check the output for the configuration that showed the problem as well, after adding
__always_inline? There are certain compile-time options that could cause the code to become unoptimized, e.g. KASAN, in addition to theOPTIMIZE_FOR_SIZE.Summarize object code size of the combinations:
ccflag default -Os ====== ======= ============= inline 0x1AF X
always_inline0x1AA0x1A4With default ccflag, the difference of inline and
always_inlineis a je/jne instruction for 'if (!desc_info->en_wd_info)'. Thealways_inlinedoesn't affect the part that useRTW89_SET_TXWD().Quoted 5 lines
Compare
always_inlinerow, the case of default ccflag uses movzbl (4 bytes), but -Os case uses mov (3 bytes).By the results, -Os affect the object code size.
always_inlinedoesn't affect the code, but affect the instruction (je/jne) nearby.Those are the known-good cases, yes.
Quoted 8 lines
Quoted 2 lines
I use Ubuntun kernel that doesn't enable KASAN.
#CONFIG_KASANis not setAh, so you test using the driver backports package on a distro kernel? While this may be a good option for your development needs, I think it is generally a good idea to also be able to test your patches against the latest mainline or linux-next kernel directly, if only to ensure that there are no obvious regressions.
No, I don't use backport. I use Ubuntu kernel PPA [1] to upgrade my kernel regularly. So, it is almost the latest version.
Quoted 80 lines
Quoted 43 lines
Quoted 24 lines
Quoted 18 lines
Quoted 10 lines
(quoted diff) +8
-0+#define RTW89_SET_TXWD_BODY_WP_OFFSET(txdesc, val) \ + RTW89_SET_TXWD(txdesc, val, 0x00, GENMASK(31, 24)) +#define RTW89_SET_TXWD_BODY_MORE_DATA(txdesc, val) \ + RTW89_SET_TXWD(txdesc, val, 0x00, BIT(23)) +#define RTW89_SET_TXWD_BODY_WD_INFO_EN(txdesc, val) \ + RTW89_SET_TXWD(txdesc, val, 0x00, BIT(22)) +#define RTW89_SET_TXWD_BODY_FW_DL(txdesc, val) \ + RTW89_SET_TXWD(txdesc, val, 0x00, BIT(20))Quoted 4 lines
I would personally write this without the wrappers, instead defining the bitmask macros as the masks and then open-coding the
le32p_replace_bits()calls instead, which I would find more intuitive while it avoids the problem with the bitmasks.Use these macros can address offset and bit fields quickly. How about I use macro instead of inline function? Like,
Quoted 5 lines
#define
RTW89_SET_TXWD(txdesc, val, offset, mask) \do { \ u32 *txd32 = (u32 *)txdesc; \ le32p_replace_bits((__le32 *)(txd32 + offset), val, mask); \ } while (0)Quoted 4 lines
That would obviously address the immediate bug, but I think using
le32p_replace_bits()directly here would actually be more readable, after you define the descriptor layout using a structure with named__le32members to replace the offset.I will remove the wrapper and use
le32p_replace_bits()directly.I don't plan to use structure, because these data contain bit-fields. Then, I need to maintain little-/big-endian formats, like
struct foo {
#if BIG_ENDINA __le32 msb:1; __le32 rsvd:30; __le32 lsb:1; #else __le32 lsb:1; __le32 rsvd:30; __le32 msb:1;Quoted 2 lines
#endif
};Right, bitfields would not work well here, as they are generally not portable. Using an "#ifdef
__BIG_ENDIAN_BITFIELD" check can work, but as you say this is really ugly.What I was trying to suggest instead is a structure like
struct descriptor { __le32 word0; __le32 word1; __le32 word2; __le32 word3; };And then build the descriptor like (with proper naming of the fields of course)
void
fill_descriptor(struct my_device *dev, struct sk_buff *skb, volatile struct descriptor *d){ d->word0 = build_desc_word0(fieldA, fieldB, fieldC, fieldD); d->word1 = build_desc_word1(fieldE, fieldF); ... }where the
build_desc_word0()functions are the ones that encode the actual layout, e.g. using thelinux/bitfield.hhelpers likestatic inline
__le32build_desc_word0(u32 fieldA, u32 fieldB, u32 fieldC, u32 fieldD)
{
u32 word =FIELD_PREP(REG_FIELD_A, fieldA)|
FIELD_PREP(REG_FIELD_B, fieldB)|FIELD_PREP(REG_FIELD_C, fieldC)|FIELD_PREP(REG_FIELD_D, fieldD);
Quoted 7 lines
return
cpu_to_le32(word);
}Doing it this way has the advantage of keeping the assignment separate, which makes sure you don't accidentally introduce a read-modify-write cycle on the descriptor. This should work well on all architectures using
dma_alloc_coherent()buffers.
Got it.
Quoted 48 lines
Quoted 20 lines
Quoted 13 lines
Quoted 10 lines
Quoted 9 lines
Going back one more step, I see that that
rtw89_core_fill_txdesc()manipulates the descriptor fields in-memory, which also seems like a bad idea: The descriptor is mapped as cache-coherent, so on machines with no coherent DMA (i.e. most ARM or MIPS machines), that is uncached memory, and writing the descriptor using a series of read-modify-write cycles on uncached memory will be awfully slow. Maybe the answer is to just completely replace the descriptor access.Quoted 2 lines
I'll think if we can use chached memory with single_map/unmap for descriptor. That would improve the performance.
Quoted 6 lines
Using
dma_unmap_single()with its cache flush may not work correctly if the descriptor fields have to be written in a particular order. Usually the last field in a descriptor contains a 'valid' bit that must not be observed by the hardware before the rest is visible. The cache flush however would not guarantee the order of the update.Quoted 3 lines
Is it possible to flush cache twice? Writing the fields other than 'valid' bit, and do
wmb()and first flush. Then, set 'valid' bit, and do second flush.This could work, but it would be really expensive, since the dma-mapping API is based on ownership state transitions, so you'd have to got through
dma_sync_single_for_device(),dma_sync_single_for_cpu(), and anotherdma_sync_single_for_device(). On machines usingswiotlb(), those would in turn translate into copy operations.Quoted 17 lines
Quoted 8 lines
It would also likely be slower than
dma_alloc_coherent()on machines that have cache-coherent PCI, such as most x86.The best way is usually to construct the descriptor one word at a time in registers, and write that word using
WRITE_ONCE(), with an explictdma_wmb()before the final write that makes the descriptor valid.Thanks for the guideline.
Fortunately, descriptor of this hardware uses circular ring buffer with read/write index instead of 'valid' bit. To issue a packet with descriptor to hardware, we fill descriptor and fill address of skb as well, and then update write index (a register) to trigger hardware to start DMA this packet. So, I think it is possible to use
dma_map_single().
Quoted 9 lines
Quoted 1 line
Anyway, I will try both methods later.
If you end up with the streaming mapping, I would suggest using a single
dma_alloc_noncoherent(), followed by dma_sync_single_* later on, rather than multiple map/unmap calls that would need to reprogram the IOMMU. The coherent API as I explained above should be more efficient though, unless you need to do a lot of reads from the descriptors.
OK. I will try dma_alloc_noncoherent(), and measure the performance. But, it seems like you have told me the answer now.
Thanks again for your rich guideline.
[1] https://kernel.ubuntu.com/~kernel-ppa/mainline/
--
Ping-Ke
Ping-Ke Shih
maintainerPATCH v6 4/24 rtw89: add debug files
In reply to Ping-Ke ShihTo recognize issues happened in field, two debug methods, debug message and debugfs, are added.
The debug messages are written to kernel log, and four levels can be chosen according to the cases -- debug, info, warn and err.
Debugfs is used to read and write registers and driver status.
--- drivers/net/wireless/realtek/rtw89/debug.c | 2404 ++++++++++++++++++++ drivers/net/wireless/realtek/rtw89/debug.h | 77 + 2 files changed, 2481 insertions(+) create mode 100644 drivers/net/wireless/realtek/rtw89/debug.c create mode 100644 drivers/net/wireless/realtek/rtw89/debug.h
drivers/net/wireless/realtek/rtw89/debug.c new file +2404 -0
@@ -0,0 +1,2404 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* Copyright(c) 2019-2020 Realtek Corporation +*/ + +#include "coex.h" +#include "debug.h" +#include "fw.h" +#include "mac.h" +#include "ps.h" +#include "reg.h" +#include "sar.h" + +#ifdef CONFIG_RTW89_DEBUGMSG +unsigned int rtw89_debug_mask; +EXPORT_SYMBOL (rtw89_debug_mask); +module_param_named (debug_mask, rtw89_debug_mask, uint,0644 ); +MODULE_PARM_DESC (debug_mask,"Debugging mask" ); +#endif + +#ifdef CONFIG_RTW89_DEBUGFS +struct rtw89_debugfs_priv { +struct rtw89_dev *rtwdev; +int (*cb_read)(struct seq_file *m,void *v); +ssize_t (*cb_write)(struct file *filp,const char __user *buffer, +size_t count,loff_t *loff); +union { +u32 cb_data; +struct { +u32 addr; +u8 len; + } read_reg; +struct { +u32 addr; +u32 mask; +u8 path; + } read_rf; +struct { +u8 ss_dbg:1 ; +u8 dle_dbg:1 ; +u8 dmac_dbg:1 ; +u8 cmac_dbg:1 ; +u8 dbg_port:1 ; + } dbgpkg_en; +struct { +u32 start; +u32 len; +u8 sel; + } mac_mem; + }; +}; + +static int rtw89_debugfs_single_show (struct seq_file *m,void *v) +{ +struct rtw89_debugfs_priv *debugfs_priv = m->private; + +return debugfs_priv->cb_read (m, v); +} + +static ssize_t rtw89_debugfs_single_write (struct file *filp, +const char __user *buffer, +size_t count,loff_t *loff) +{ +struct rtw89_debugfs_priv *debugfs_priv = filp->private_data; + +return debugfs_priv->cb_write (filp, buffer, count, loff); +} + +static ssize_t rtw89_debugfs_seq_file_write (struct file *filp, +const char __user *buffer, +size_t count,loff_t *loff) +{ +struct seq_file *seqpriv = (struct seq_file *)filp->private_data; +struct rtw89_debugfs_priv *debugfs_priv = seqpriv->private; + +return debugfs_priv->cb_write (filp, buffer, count, loff); +} + +static int rtw89_debugfs_single_open (struct inode *inode,struct file *filp) +{ +return single_open (filp, rtw89_debugfs_single_show, inode->i_private); +} + +static int rtw89_debugfs_close (struct inode *inode,struct file *filp) +{ +return 0 ; +} + +static const struct file_operations file_ops_single_r = { + .owner =THIS_MODULE , + .open = rtw89_debugfs_single_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static const struct file_operations file_ops_common_rw = { + .owner =THIS_MODULE , + .open = rtw89_debugfs_single_open, + .release = single_release, + .read = seq_read, + .llseek = seq_lseek, + .write = rtw89_debugfs_seq_file_write, +}; + +static const struct file_operations file_ops_single_w = { + .owner =THIS_MODULE , + .write = rtw89_debugfs_single_write, + .open = simple_open, + .release = rtw89_debugfs_close, +}; + +static ssize_t +rtw89_debug_priv_read_reg_select (struct file *filp, +const char __user *user_buf, +size_t count,loff_t *loff) +{ +struct seq_file *m = (struct seq_file *)filp->private_data; +struct rtw89_debugfs_priv *debugfs_priv = m->private; +struct rtw89_dev *rtwdev = debugfs_priv->rtwdev; +char buf[32 ]; +size_t buf_size; +u32 addr, len; +int num; + + buf_size =min (count,sizeof (buf) -1 ); +if (copy_from_user (buf, user_buf, buf_size)) +return -EFAULT ; + + buf[buf_size] ='\0' ; + num =sscanf (buf,"%x %x" , &addr, &len); +if (num !=2 ) { +rtw89_info (rtwdev,"invalid format: <addr> <len>\n" ); +return -EINVAL ; + } + + debugfs_priv->read_reg.addr = addr; + debugfs_priv->read_reg.len = len; + +rtw89_info (rtwdev,"select read %d bytes from 0x%08x\n" , len, addr); + +return count; +} + +static int rtw89_debug_priv_read_reg_get (struct seq_file *m,void *v) +{ +struct rtw89_debugfs_priv *debugfs_priv = m->private; +struct rtw89_dev *rtwdev = debugfs_priv->rtwdev; +u32 addr, data; +u8 len; + + len = debugfs_priv->read_reg.len; + addr = debugfs_priv->read_reg.addr; + +switch (len) { +case 1 : + data =rtw89_read8 (rtwdev, addr); +break ; +case 2 : + data =rtw89_read16 (rtwdev, addr); +break ; +case 4 : + data =rtw89_read32 (rtwdev, addr); +break ; +default : +rtw89_info (rtwdev,"invalid read reg len %d\n" , len); +return -EINVAL ; + } + +seq_printf (m,"get %d bytes at 0x%08x=0x%08x\n" , len, addr, data); + +return 0 ; +} + +static ssize_t rtw89_debug_priv_write_reg_set (struct file *filp, +const char __user *user_buf, +size_t count,loff_t *loff) +{ +struct rtw89_debugfs_priv *debugfs_priv = filp->private_data; +struct rtw89_dev *rtwdev = debugfs_priv->rtwdev; +char buf[32 ]; +size_t buf_size; +u32 addr, val, len; +int num; + + buf_size =min (count,sizeof (buf) -1 ); +if (copy_from_user (buf, user_buf, buf_size)) +return -EFAULT ; + + buf[buf_size] ='\0' ; + num =sscanf (buf,"%x %x %x" , &addr, &val, &len); +if (num !=3 ) { +rtw89_info (rtwdev,"invalid format: <addr> <val> <len>\n" ); +return -EINVAL ; + } + +switch (len) { +case 1 : +rtw89_info (rtwdev,"reg write8 0x%08x: 0x%02x\n" , addr, val); +rtw89_write8 (rtwdev, addr, (u8 )val); +break ; +case 2 : +rtw89_info (rtwdev,"reg write16 0x%08x: 0x%04x\n" , addr, val); +rtw89_write16 (rtwdev, addr, (u16 )val); +break ; +case 4 : +rtw89_info (rtwdev,"reg write32 0x%08x: 0x%08x\n" , addr, val); +rtw89_write32 (rtwdev, addr, (u32 )val); +break ; +default : +rtw89_info (rtwdev,"invalid read write len %d\n" , len); +break ; + } + +return count; +} + +static ssize_t +rtw89_debug_priv_read_rf_select (struct file *filp, +const char __user *user_buf, +size_t count,loff_t *loff) +{ +struct seq_file *m = (struct seq_file *)filp->private_data; +struct rtw89_debugfs_priv *debugfs_priv = m->private; +struct rtw89_dev *rtwdev = debugfs_priv->rtwdev; +char buf[32 ]; +size_t buf_size; +u32 addr, mask; +u8 path; +int num; + + buf_size =min (count,sizeof (buf) -1 ); +if (copy_from_user (buf, user_buf, buf_size)) +return -EFAULT ; + + buf[buf_size] ='\0' ; + num =sscanf (buf,"%hhd %x %x" , &path, &addr, &mask); +if (num !=3 ) { +rtw89_info (rtwdev,"invalid format: <path> <addr> <mask>\n" ); +return -EINVAL ; + } + +if (path >= rtwdev->chip->rf_path_num) { +rtw89_info (rtwdev,"wrong rf path\n" ); +return -EINVAL ; + } + debugfs_priv->read_rf.addr = addr; + debugfs_priv->read_rf.mask = mask; + debugfs_priv->read_rf.path = path; + +rtw89_info (rtwdev,"select read rf path %d from 0x%08x\n" , path, addr); + +return count; +} + +static int rtw89_debug_priv_read_rf_get (struct seq_file *m,void *v) +{ +struct rtw89_debugfs_priv *debugfs_priv = m->private; +struct rtw89_dev *rtwdev = debugfs_priv->rtwdev; +u32 addr, data, mask; +u8 path; + + addr = debugfs_priv->read_rf.addr; + mask = debugfs_priv->read_rf.mask; + path = debugfs_priv->read_rf.path; + + data =rtw89_read_rf (rtwdev, path, addr, mask); + +seq_printf (m,"path %d, rf register 0x%08x=0x%08x\n" , path, addr, data); + +return 0 ; +} + +static ssize_t rtw89_debug_priv_write_rf_set (struct file *filp, +const char __user *user_buf, +size_t count,loff_t *loff) +{ +struct rtw89_debugfs_priv *debugfs_priv = filp->private_data; +struct rtw89_dev *rtwdev = debugfs_priv->rtwdev; +char buf[32 ]; +size_t buf_size; +u32 addr, val, mask; +u8 path; +int num; + + buf_size =min (count,sizeof (buf) -1 ); +if (copy_from_user (buf, user_buf, buf_size)) +return -EFAULT ; + + buf[buf_size] ='\0' ; + num =sscanf (buf,"%hhd %x %x %x" , &path, &addr, &mask, &val); +if (num !=4 ) { +rtw89_info (rtwdev,"invalid format: <path> <addr> <mask> <val>\n" ); +return -EINVAL ; + } + +if (path >= rtwdev->chip->rf_path_num) { +rtw89_info (rtwdev,"wrong rf path\n" ); +return -EINVAL ; + } + +rtw89_info (rtwdev,"path %d, rf register write 0x%08x=0x%08x (mask = 0x%08x)\n" , + path, addr, val, mask); +rtw89_write_rf (rtwdev, path, addr, mask, val); + +return count; +} + +static int rtw89_debug_priv_rf_reg_dump_get (struct seq_file *m,void *v) +{ +struct rtw89_debugfs_priv *debugfs_priv = m->private; +struct rtw89_dev *rtwdev = debugfs_priv->rtwdev; +const struct rtw89_chip_info *chip = rtwdev->chip; +u32 addr, offset, data; +u8 path; + +for (path =0 ; path < chip->rf_path_num; path++) { +seq_printf (m,"RF path %d:\n\n" , path); +for (addr =0 ; addr <0x100 ; addr +=4 ) { +seq_printf (m,"0x%08x: " , addr); +for (offset =0 ; offset <4 ; offset++) { + data =rtw89_read_rf (rtwdev, path, + addr + offset,RFREG_MASK ); +seq_printf (m,"0x%05x " , data); + } +seq_puts (m,"\n" ); + } +seq_puts (m,"\n" ); + } + +return 0 ; +} + +struct txpwr_ent { +const char *txt; +u8 len; +}; + +struct txpwr_map { +const struct txpwr_ent *ent; +u8 size; +u32 addr_from; +u32 addr_to; +}; + +#define __GEN_TXPWR_ENT2 (_t, _e0, _e1) \ + { .len = 2, .txt = _t "\t- " _e0 " " _e1 } + +#define __GEN_TXPWR_ENT4 (_t, _e0, _e1, _e2, _e3) \ + { .len = 4, .txt = _t "\t- " _e0 " " _e1 " " _e2 " " _e3 } + +#define __GEN_TXPWR_ENT8 (_t, _e0, _e1, _e2, _e3, _e4, _e5, _e6, _e7) \ + { .len = 8, .txt = _t "\t- " \ + _e0 " " _e1 " " _e2 " " _e3 " " \ + _e4 " " _e5 " " _e6 " " _e7 } + +static const struct txpwr_ent __txpwr_ent_byr[] = { +__GEN_TXPWR_ENT4 ("CCK " ,"1M " ,"2M " ,"5.5M " ,"11M " ), +__GEN_TXPWR_ENT4 ("LEGACY " ,"6M " ,"9M " ,"12M " ,"18M " ), +__GEN_TXPWR_ENT4 ("LEGACY " ,"24M " ,"36M " ,"48M " ,"54M " ), +/* 1NSS */ +__GEN_TXPWR_ENT4 ("MCS_1NSS " ,"MCS0 " ,"MCS1 " ,"MCS2 " ,"MCS3 " ), +__GEN_TXPWR_ENT4 ("MCS_1NSS " ,"MCS4 " ,"MCS5 " ,"MCS6 " ,"MCS7 " ), +__GEN_TXPWR_ENT4 ("MCS_1NSS " ,"MCS8 " ,"MCS9 " ,"MCS10" ,"MCS11" ), +__GEN_TXPWR_ENT4 ("HEDCM_1NSS" ,"MCS0 " ,"MCS1 " ,"MCS3 " ,"MCS4 " ), +/* 2NSS */ +__GEN_TXPWR_ENT4 ("MCS_2NSS " ,"MCS0 " ,"MCS1 " ,"MCS2 " ,"MCS3 " ), +__GEN_TXPWR_ENT4 ("MCS_2NSS " ,"MCS4 " ,"MCS5 " ,"MCS6 " ,"MCS7 " ), +__GEN_TXPWR_ENT4 ("MCS_2NSS " ,"MCS8 " ,"MCS9 " ,"MCS10" ,"MCS11" ), +__GEN_TXPWR_ENT4 ("HEDCM_2NSS" ,"MCS0 " ,"MCS1 " ,"MCS3 " ,"MCS4 " ), +}; + +static_assert ((ARRAY_SIZE (__txpwr_ent_byr) *4 ) == + (R_AX_PWR_BY_RATE_MAX -R_AX_PWR_BY_RATE +4 )); + +static const struct txpwr_map __txpwr_map_byr = { + .ent = __txpwr_ent_byr, + .size =ARRAY_SIZE (__txpwr_ent_byr), + .addr_from =R_AX_PWR_BY_RATE , + .addr_to =R_AX_PWR_BY_RATE_MAX , +}; + +static const struct txpwr_ent __txpwr_ent_lmt[] = { +/* 1TX */ +__GEN_TXPWR_ENT2 ("CCK_1TX_20M " ,"NON_BF" ,"BF" ), +__GEN_TXPWR_ENT2 ("CCK_1TX_40M " ,"NON_BF" ,"BF" ), +__GEN_TXPWR_ENT2 ("OFDM_1TX " ,"NON_BF" ,"BF" ), +__GEN_TXPWR_ENT2 ("MCS_1TX_20M_0 " ,"NON_BF" ,"BF" ), +__GEN_TXPWR_ENT2 ("MCS_1TX_20M_1 " ,"NON_BF" ,"BF" ), +__GEN_TXPWR_ENT2 ("MCS_1TX_20M_2 " ,"NON_BF" ,"BF" ), +__GEN_TXPWR_ENT2 ("MCS_1TX_20M_3 " ,"NON_BF" ,"BF" ), +__GEN_TXPWR_ENT2 ("MCS_1TX_20M_4 " ,"NON_BF" ,"BF" ), +__GEN_TXPWR_ENT2 ("MCS_1TX_20M_5 " ,"NON_BF" ,"BF" ), +__GEN_TXPWR_ENT2 ("MCS_1TX_20M_6 " ,"NON_BF" ,"BF" ), +__GEN_TXPWR_ENT2 ("MCS_1TX_20M_7 " ,"NON_BF" ,"BF" ), +__GEN_TXPWR_ENT2 ("MCS_1TX_40M_0 " ,"NON_BF" ,"BF" ), +__GEN_TXPWR_ENT2 ("MCS_1TX_40M_1 " ,"NON_BF" ,"BF" ), +__GEN_TXPWR_ENT2 ("MCS_1TX_40M_2 " ,"NON_BF" ,"BF" ), +__GEN_TXPWR_ENT2 ("MCS_1TX_40M_3 " ,"NON_BF" ,"BF" ), +__GEN_TXPWR_ENT2 ("MCS_1TX_80M_0 " ,"NON_BF" ,"BF" ), +__GEN_TXPWR_ENT2 ("MCS_1TX_80M_1 " ,"NON_BF" ,"BF" ), +__GEN_TXPWR_ENT2 ("MCS_1TX_160M " ,"NON_BF" ,"BF" ), +__GEN_TXPWR_ENT2 ("MCS_1TX_40M_0p5" ,"NON_BF" ,"BF" ), +__GEN_TXPWR_ENT2 ("MCS_1TX_40M_2p5" ,"NON_BF" ,"BF" ), +/* 2TX */ +__GEN_TXPWR_ENT2 ("CCK_2TX_20M " ,"NON_BF" ,"BF" ), +__GEN_TXPWR_ENT2 ("CCK_2TX_40M " ,"NON_BF" ,"BF" ), +__GEN_TXPWR_ENT2 ("OFDM_2TX " ,"NON_BF" ,"BF" ), +__GEN_TXPWR_ENT2 ("MCS_2TX_20M_0 " ,"NON_BF" ,"BF" ), +__GEN_TXPWR_ENT2 ("MCS_2TX_20M_1 " ,"NON_BF" ,"BF" ), +__GEN_TXPWR_ENT2 ("MCS_2TX_20M_2 " ,"NON_BF" ,"BF" ), +__GEN_TXPWR_ENT2 ("MCS_2TX_20M_3 " ,"NON_BF" ,"BF" ), +__GEN_TXPWR_ENT2 ("MCS_2TX_20M_4 " ,"NON_BF" ,"BF" ), +__GEN_TXPWR_ENT2 ("MCS_2TX_20M_5 " ,"NON_BF" ,"BF" ), +__GEN_TXPWR_ENT2 ("MCS_2TX_20M_6 " ,"NON_BF" ,"BF" ), +__GEN_TXPWR_ENT2 ("MCS_2TX_20M_7 " ,"NON_BF" ,"BF" ), +__GEN_TXPWR_ENT2 ("MCS_2TX_40M_0 " ,"NON_BF" ,"BF" ), +__GEN_TXPWR_ENT2 ("MCS_2TX_40M_1 " ,"NON_BF" ,"BF" ), +__GEN_TXPWR_ENT2 ("MCS_2TX_40M_2 " ,"NON_BF" ,"BF" ), +__GEN_TXPWR_ENT2 ("MCS_2TX_40M_3 " ,"NON_BF" ,"BF" ), +__GEN_TXPWR_ENT2 ("MCS_2TX_80M_0 " ,"NON_BF" ,"BF" ), +__GEN_TXPWR_ENT2 ("MCS_2TX_80M_1 " ,"NON_BF" ,"BF" ), +__GEN_TXPWR_ENT2 ("MCS_2TX_160M " ,"NON_BF" ,"BF" ), +__GEN_TXPWR_ENT2 ("MCS_2TX_40M_0p5" ,"NON_BF" ,"BF" ), +__GEN_TXPWR_ENT2 ("MCS_2TX_40M_2p5" ,"NON_BF" ,"BF" ), +}; + +static_assert ((ARRAY_SIZE (__txpwr_ent_lmt) *2 ) == + (R_AX_PWR_LMT_MAX -R_AX_PWR_LMT +4 )); + +static const struct txpwr_map __txpwr_map_lmt = { + .ent = __txpwr_ent_lmt, + .size =ARRAY_SIZE (__txpwr_ent_lmt), + .addr_from =R_AX_PWR_LMT , + .addr_to =R_AX_PWR_LMT_MAX , +}; + +static const struct txpwr_ent __txpwr_ent_lmt_ru[] = { +/* 1TX */ +__GEN_TXPWR_ENT8 ("1TX" ,"RU26__0" ,"RU26__1" ,"RU26__2" ,"RU26__3" , +"RU26__4" ,"RU26__5" ,"RU26__6" ,"RU26__7" ), +__GEN_TXPWR_ENT8 ("1TX" ,"RU52__0" ,"RU52__1" ,"RU52__2" ,"RU52__3" , +"RU52__4" ,"RU52__5" ,"RU52__6" ,"RU52__7" ), +__GEN_TXPWR_ENT8 ("1TX" ,"RU106_0" ,"RU106_1" ,"RU106_2" ,"RU106_3" , +"RU106_4" ,"RU106_5" ,"RU106_6" ,"RU106_7" ), +/* 2TX */ +__GEN_TXPWR_ENT8 ("2TX" ,"RU26__0" ,"RU26__1" ,"RU26__2" ,"RU26__3" , +"RU26__4" ,"RU26__5" ,"RU26__6" ,"RU26__7" ), +__GEN_TXPWR_ENT8 ("2TX" ,"RU52__0" ,"RU52__1" ,"RU52__2" ,"RU52__3" , +"RU52__4" ,"RU52__5" ,"RU52__6" ,"RU52__7" ), +__GEN_TXPWR_ENT8 ("2TX" ,"RU106_0" ,"RU106_1" ,"RU106_2" ,"RU106_3" , +"RU106_4" ,"RU106_5" ,"RU106_6" ,"RU106_7" ), +}; + +static_assert ((ARRAY_SIZE (__txpwr_ent_lmt_ru) *8 ) == + (R_AX_PWR_RU_LMT_MAX -R_AX_PWR_RU_LMT +4 )); + +static const struct txpwr_map __txpwr_map_lmt_ru = { + .ent = __txpwr_ent_lmt_ru, + .size =ARRAY_SIZE (__txpwr_ent_lmt_ru), + .addr_from =R_AX_PWR_RU_LMT , + .addr_to =R_AX_PWR_RU_LMT_MAX , +}; + +static u8 __print_txpwr_ent (struct seq_file *m,const struct txpwr_ent *ent, +const u8 *buf,const u8 cur) +{ +char *fmt; + +switch (ent->len) { +case 2 : + fmt ="%s\t| %3d, %3d,\tdBm\n" ; +seq_printf (m, fmt, ent->txt, buf[cur], buf[cur +1 ]); +return 2 ; +case 4 : + fmt ="%s\t| %3d, %3d, %3d, %3d,\tdBm\n" ; +seq_printf (m, fmt, ent->txt, buf[cur], buf[cur +1 ], + buf[cur +2 ], buf[cur +3 ]); +return 4 ; +case 8 : + fmt ="%s\t| %3d, %3d, %3d, %3d, %3d, %3d, %3d, %3d,\tdBm\n" ; +seq_printf (m, fmt, ent->txt, buf[cur], buf[cur +1 ], + buf[cur +2 ], buf[cur +3 ], buf[cur +4 ], + buf[cur +5 ], buf[cur +6 ], buf[cur +7 ]); +return 8 ; +default : +return 0 ; + } +} + +static int __print_txpwr_map (struct seq_file *m,struct rtw89_dev *rtwdev, +const struct txpwr_map *map) +{ +u8 fct = rtwdev->chip->txpwr_factor_mac; +u8 *buf, cur, i; +u32 val, addr; +int ret; + + buf =vzalloc (map->addr_to - map->addr_from +4 ); +if (!buf) +return -ENOMEM ; + +for (addr = map->addr_from; addr <= map->addr_to; addr +=4 ) { + ret =rtw89_mac_txpwr_read32 (rtwdev,RTW89_PHY_0 , addr, &val); +if (ret) + val =MASKDWORD ; + + cur = addr - map->addr_from; +for (i =0 ; i <4 ; i++, val >>=8 ) + buf[cur + i] =FIELD_GET (MASKBYTE0 , val) >> fct; + } + +for (cur =0 , i =0 ; i < map->size; i++) + cur +=__print_txpwr_ent (m, &map->ent[i], buf, cur); + +vfree (buf); +return 0 ; +} + +#define case_REGD (_regd) \ + case RTW89_ ## _regd: \ + seq_puts(m, #_regd "\n"); \ + break + +static void __print_regd (struct seq_file *m,struct rtw89_dev *rtwdev) +{ +u8 band = rtwdev->hal.current_band_type; +u8 regd =rtw89_regd_get (rtwdev, band); + +switch (regd) { +default : +seq_printf (m,"UNKNOWN: %d\n" , regd); +break ; +case_REGD (WW ); +case_REGD (ETSI ); +case_REGD (FCC ); +case_REGD (MKK ); +case_REGD (NA ); +case_REGD (IC ); +case_REGD (KCC ); +case_REGD (NCC ); +case_REGD (CHILE ); +case_REGD (ACMA ); +case_REGD (MEXICO ); +case_REGD (UKRAINE ); +case_REGD (CN ); + } +} + +#undef case_REGD + +static int rtw89_debug_priv_txpwr_table_get (struct seq_file *m,void *v) +{ +struct rtw89_debugfs_priv *debugfs_priv = m->private; +struct rtw89_dev *rtwdev = debugfs_priv->rtwdev; +int ret =0 ; + +mutex_lock (&rtwdev->mutex); +rtw89_leave_ps_mode (rtwdev); + +seq_puts (m,"[Regulatory] " ); +__print_regd (m, rtwdev); + +seq_puts (m,"[SAR]\n" ); +rtw89_print_sar (m, rtwdev); + +seq_puts (m,"\n[TX power byrate]\n" ); + ret =__print_txpwr_map (m, rtwdev, &__txpwr_map_byr); +if (ret) + goto err; + +seq_puts (m,"\n[TX power limit]\n" ); + ret =__print_txpwr_map (m, rtwdev, &__txpwr_map_lmt); +if (ret) + goto err; + +seq_puts (m,"\n[TX power limit_ru]\n" ); + ret =__print_txpwr_map (m, rtwdev, &__txpwr_map_lmt_ru); +if (ret) + goto err; + +err: +mutex_unlock (&rtwdev->mutex); +return ret; +} + +static ssize_t +rtw89_debug_priv_mac_reg_dump_select (struct file *filp, +const char __user *user_buf, +size_t count,loff_t *loff) +{ +struct seq_file *m = (struct seq_file *)filp->private_data; +struct rtw89_debugfs_priv *debugfs_priv = m->private; +struct rtw89_dev *rtwdev = debugfs_priv->rtwdev; +char buf[32 ]; +size_t buf_size; +int sel; +int ret; + + buf_size =min (count,sizeof (buf) -1 ); +if (copy_from_user (buf, user_buf, buf_size)) +return -EFAULT ; + + buf[buf_size] ='\0' ; + ret =kstrtoint (buf,0 , &sel); +if (ret) +return ret; + +if (sel <RTW89_DBG_SEL_MAC_00 || sel >RTW89_DBG_SEL_RFC ) { +rtw89_info (rtwdev,"invalid args: %d\n" , sel); +return -EINVAL ; + } + + debugfs_priv->cb_data = sel; +rtw89_info (rtwdev,"select mac page dump %d\n" , debugfs_priv->cb_data); + +return count; +} + +#define RTW89_MAC_PAGE_SIZE 0x100 + +static int rtw89_debug_priv_mac_reg_dump_get (struct seq_file *m,void *v) +{ +struct rtw89_debugfs_priv *debugfs_priv = m->private; +struct rtw89_dev *rtwdev = debugfs_priv->rtwdev; +enum rtw89_debug_mac_reg_sel reg_sel = debugfs_priv->cb_data; +u32 start, end; +u32 i, j, k, page; +u32 val; + +switch (reg_sel) { +case RTW89_DBG_SEL_MAC_00 : +seq_puts (m,"Debug selected MAC page 0x00\n" ); + start =0x000 ; + end =0x014 ; +break ; +case RTW89_DBG_SEL_MAC_40 : +seq_puts (m,"Debug selected MAC page 0x40\n" ); + start =0x040 ; + end =0x07f ; +break ; +case RTW89_DBG_SEL_MAC_80 : +seq_puts (m,"Debug selected MAC page 0x80\n" ); + start =0x080 ; + end =0x09f ; +break ; +case RTW89_DBG_SEL_MAC_C0 : +seq_puts (m,"Debug selected MAC page 0xc0\n" ); + start =0x0c0 ; + end =0x0df ; +break ; +case RTW89_DBG_SEL_MAC_E0 : +seq_puts (m,"Debug selected MAC page 0xe0\n" ); + start =0x0e0 ; + end =0x0ff ; +break ; +case RTW89_DBG_SEL_BB : +seq_puts (m,"Debug selected BB register\n" ); + start =0x100 ; + end =0x17f ; +break ; +case RTW89_DBG_SEL_IQK : +seq_puts (m,"Debug selected IQK register\n" ); + start =0x180 ; + end =0x1bf ; +break ; +case RTW89_DBG_SEL_RFC : +seq_puts (m,"Debug selected RFC register\n" ); + start =0x1c0 ; + end =0x1ff ; +break ; +default : +seq_puts (m,"Selected invalid register page\n" ); +return -EINVAL ; + } + +for (i = start; i <= end; i++) { + page = i <<8 ; +for (j = page; j < page +RTW89_MAC_PAGE_SIZE ; j +=16 ) { +seq_printf (m,"%08xh : " ,0x18600000 + j); +for (k =0 ; k <4 ; k++) { + val =rtw89_read32 (rtwdev, j + (k <<2 )); +seq_printf (m,"%08x " , val); + } +seq_puts (m,"\n" ); + } + } + +return 0 ; +} + +static ssize_t +rtw89_debug_priv_mac_mem_dump_select (struct file *filp, +const char __user *user_buf, +size_t count,loff_t *loff) +{ +struct seq_file *m = (struct seq_file *)filp->private_data; +struct rtw89_debugfs_priv *debugfs_priv = m->private; +struct rtw89_dev *rtwdev = debugfs_priv->rtwdev; +char buf[32 ]; +size_t buf_size; +u32 sel, start_addr, len; +int num; + + buf_size =min (count,sizeof (buf) -1 ); +if (copy_from_user (buf, user_buf, buf_size)) +return -EFAULT ; + + buf[buf_size] ='\0' ; + num =sscanf (buf,"%x %x %x" , &sel, &start_addr, &len); +if (num !=3 ) { +rtw89_info (rtwdev,"invalid format: <sel> <start> <len>\n" ); +return -EINVAL ; + } + + debugfs_priv->mac_mem.sel = sel; + debugfs_priv->mac_mem.start = start_addr; + debugfs_priv->mac_mem.len = len; + +rtw89_info (rtwdev,"select mem %d start %d len %d\n" , + sel, start_addr, len); + +return count; +} + +static const u32 mac_mem_base_addr_table[RTW89_MAC_MEM_MAX ] = { + [RTW89_MAC_MEM_SHARED_BUF ] =SHARED_BUF_BASE_ADDR , + [RTW89_MAC_MEM_DMAC_TBL ] =DMAC_TBL_BASE_ADDR , + [RTW89_MAC_MEM_SHCUT_MACHDR ] =SHCUT_MACHDR_BASE_ADDR , + [RTW89_MAC_MEM_STA_SCHED ] =STA_SCHED_BASE_ADDR , + [RTW89_MAC_MEM_RXPLD_FLTR_CAM ] =RXPLD_FLTR_CAM_BASE_ADDR , + [RTW89_MAC_MEM_SECURITY_CAM ] =SECURITY_CAM_BASE_ADDR , + [RTW89_MAC_MEM_WOW_CAM ] =WOW_CAM_BASE_ADDR , + [RTW89_MAC_MEM_CMAC_TBL ] =CMAC_TBL_BASE_ADDR , + [RTW89_MAC_MEM_ADDR_CAM ] =ADDR_CAM_BASE_ADDR , + [RTW89_MAC_MEM_BA_CAM ] =BA_CAM_BASE_ADDR , + [RTW89_MAC_MEM_BCN_IE_CAM0 ] =BCN_IE_CAM0_BASE_ADDR , + [RTW89_MAC_MEM_BCN_IE_CAM1 ] =BCN_IE_CAM1_BASE_ADDR , +}; + +static void rtw89_debug_dump_mac_mem (struct seq_file *m, +struct rtw89_dev *rtwdev, +u8 sel,u32 start_addr,u32 len) +{ +u32 base_addr, start_page, residue; +u32 i, j, p, pages; +u32 dump_len, remain; +u32 val; + + remain = len; + pages = len /MAC_MEM_DUMP_PAGE_SIZE +1 ; + start_page = start_addr /MAC_MEM_DUMP_PAGE_SIZE ; + residue = start_addr %MAC_MEM_DUMP_PAGE_SIZE ; + base_addr = mac_mem_base_addr_table[sel]; + base_addr += start_page *MAC_MEM_DUMP_PAGE_SIZE ; + +for (p =0 ; p < pages; p++) { + dump_len =min_t (u32, remain,MAC_MEM_DUMP_PAGE_SIZE ); +rtw89_write32 (rtwdev,R_AX_FILTER_MODEL_ADDR , base_addr); +for (i =R_AX_INDIR_ACCESS_ENTRY + residue; + i <R_AX_INDIR_ACCESS_ENTRY + dump_len;) { +seq_printf (m,"%08xh:" , i); +for (j =0 ; + j <4 && i <R_AX_INDIR_ACCESS_ENTRY + dump_len; + j++, i +=4 ) { + val =rtw89_read32 (rtwdev, i); +seq_printf (m," %08x" , val); + remain -=4 ; + } +seq_puts (m,"\n" ); + } + base_addr +=MAC_MEM_DUMP_PAGE_SIZE ; + } +} + +static int +rtw89_debug_priv_mac_mem_dump_get (struct seq_file *m,void *v) +{ +struct rtw89_debugfs_priv *debugfs_priv = m->private; +struct rtw89_dev *rtwdev = debugfs_priv->rtwdev; + +mutex_lock (&rtwdev->mutex); +rtw89_leave_ps_mode (rtwdev); +rtw89_debug_dump_mac_mem (m, rtwdev, + debugfs_priv->mac_mem.sel, + debugfs_priv->mac_mem.start, + debugfs_priv->mac_mem.len); +mutex_unlock (&rtwdev->mutex); + +return 0 ; +} + +static ssize_t +rtw89_debug_priv_mac_dbg_port_dump_select (struct file *filp, +const char __user *user_buf, +size_t count,loff_t *loff) +{ +struct seq_file *m = (struct seq_file *)filp->private_data; +struct rtw89_debugfs_priv *debugfs_priv = m->private; +struct rtw89_dev *rtwdev = debugfs_priv->rtwdev; +char buf[32 ]; +size_t buf_size; +int sel, set; +int num; +bool enable; + + buf_size =min (count,sizeof (buf) -1 ); +if (copy_from_user (buf, user_buf, buf_size)) +return -EFAULT ; + + buf[buf_size] ='\0' ; + num =sscanf (buf,"%d %d" , &sel, &set); +if (num !=2 ) { +rtw89_info (rtwdev,"invalid format: <sel> <set>\n" ); +return -EINVAL ; + } + + enable = set ==0 ? false : true; +switch (sel) { +case 0 : + debugfs_priv->dbgpkg_en.ss_dbg = enable; +break ; +case 1 : + debugfs_priv->dbgpkg_en.dle_dbg = enable; +break ; +case 2 : + debugfs_priv->dbgpkg_en.dmac_dbg = enable; +break ; +case 3 : + debugfs_priv->dbgpkg_en.cmac_dbg = enable; +break ; +case 4 : + debugfs_priv->dbgpkg_en.dbg_port = enable; +break ; +default : +rtw89_info (rtwdev,"invalid args: sel %d set %d\n" , sel, set); +return -EINVAL ; + } + +rtw89_info (rtwdev,"%s debug port dump %d\n" , + enable ?"Enable" :"Disable" , sel); + +return count; +} + +static int rtw89_debug_mac_dump_ss_dbg (struct rtw89_dev *rtwdev, +struct seq_file *m) +{ +return 0 ; +} + +static int rtw89_debug_mac_dump_dle_dbg (struct rtw89_dev *rtwdev, +struct seq_file *m) +{ +#define DLE_DFI_DUMP (__type, __target, __sel) \ +({ \ + u32 __ctrl; \ + u32 __reg_ctrl = R_AX_##__type##_DBG_FUN_INTF_CTL; \ + u32 __reg_data = R_AX_##__type##_DBG_FUN_INTF_DATA; \ + u32 __data, __val32; \ + int __ret; \ + \ + __ctrl = FIELD_PREP(B_AX_##__type##_DFI_TRGSEL_MASK, \ + DLE_DFI_TYPE_##__target) | \ + FIELD_PREP(B_AX_##__type##_DFI_ADDR_MASK, __sel) | \ + B_AX_WDE_DFI_ACTIVE; \ + rtw89_write32(rtwdev, __reg_ctrl, __ctrl); \ + __ret = read_poll_timeout(rtw89_read32, __val32, \ + !(__val32 & B_AX_##__type##_DFI_ACTIVE), \ + 1000, 50000, false, \ + rtwdev, __reg_ctrl); \ + if (__ret) { \ + rtw89_err(rtwdev, "failed to dump DLE %s %s %d\n", \ + #__type, #__target, __sel); \ + return __ret; \ + } \ + \ + __data = rtw89_read32(rtwdev, __reg_data); \ + __data; \ +}) + +#define DLE_DFI_FREE_PAGE_DUMP (__m, __type) \ +({ \ + u32 __freepg, __pubpg; \ + u32 __freepg_head, __freepg_tail, __pubpg_num; \ + \ + __freepg = DLE_DFI_DUMP(__type, FREEPG, 0); \ + __pubpg = DLE_DFI_DUMP(__type, FREEPG, 1); \ + __freepg_head = FIELD_GET(B_AX_DLE_FREE_HEADPG, __freepg); \ + __freepg_tail = FIELD_GET(B_AX_DLE_FREE_TAILPG, __freepg); \ + __pubpg_num = FIELD_GET(B_AX_DLE_PUB_PGNUM, __pubpg); \ + seq_printf(__m, "[%s] freepg head: %d\n", \ + #__type, __freepg_head); \ + seq_printf(__m, "[%s] freepg tail: %d\n", \ + #__type, __freepg_tail); \ + seq_printf(__m, "[%s] pubpg num : %d\n", \ + #__type, __pubpg_num); \ +}) + +#define case_QUOTA (__m, __type, __id) \ + case __type##_QTAID_##__id: \ + val32 = DLE_DFI_DUMP(__type, QUOTA, __type##_QTAID_##__id); \ + rsv_pgnum = FIELD_GET(B_AX_DLE_RSV_PGNUM, val32); \ + use_pgnum = FIELD_GET(B_AX_DLE_USE_PGNUM, val32); \ + seq_printf(__m, "[%s][%s] rsv_pgnum: %d\n", \ + #__type, #__id, rsv_pgnum); \ + seq_printf(__m, "[%s][%s] use_pgnum: %d\n", \ + #__type, #__id, use_pgnum); \ + break +u32 quota_id; +u32 val32; +u16 rsv_pgnum, use_pgnum; +int ret; + + ret =rtw89_mac_check_mac_en (rtwdev,0 ,RTW89_DMAC_SEL ); +if (ret) { +seq_puts (m,"[DLE] : DMAC not enabled\n" ); +return ret; + } + +DLE_DFI_FREE_PAGE_DUMP (m,WDE ); +DLE_DFI_FREE_PAGE_DUMP (m,PLE ); +for (quota_id =0 ; quota_id <=WDE_QTAID_CPUIO ; quota_id++) { +switch (quota_id) { +case_QUOTA (m,WDE ,HOST_IF ); +case_QUOTA (m,WDE ,WLAN_CPU ); +case_QUOTA (m,WDE ,DATA_CPU ); +case_QUOTA (m,WDE ,PKTIN ); +case_QUOTA (m,WDE ,CPUIO ); + } + } +for (quota_id =0 ; quota_id <=PLE_QTAID_CPUIO ; quota_id++) { +switch (quota_id) { +case_QUOTA (m,PLE ,B0_TXPL ); +case_QUOTA (m,PLE ,B1_TXPL ); +case_QUOTA (m,PLE ,C2H ); +case_QUOTA (m,PLE ,H2C ); +case_QUOTA (m,PLE ,WLAN_CPU ); +case_QUOTA (m,PLE ,MPDU ); +case_QUOTA (m,PLE ,CMAC0_RX ); +case_QUOTA (m,PLE ,CMAC1_RX ); +case_QUOTA (m,PLE ,CMAC1_BBRPT ); +case_QUOTA (m,PLE ,WDRLS ); +case_QUOTA (m,PLE ,CPUIO ); + } + } + +return 0 ; + +#undef case_QUOTA +#undef DLE_DFI_DUMP +#undef DLE_DFI_FREE_PAGE_DUMP +} + +static int rtw89_debug_mac_dump_dmac_dbg (struct rtw89_dev *rtwdev, +struct seq_file *m) +{ +int ret; + + ret =rtw89_mac_check_mac_en (rtwdev,0 ,RTW89_DMAC_SEL ); +if (ret) { +seq_puts (m,"[DMAC] : DMAC not enabled\n" ); +return ret; + } + +seq_printf (m,"R_AX_DMAC_ERR_ISR=0x%08x\n" , +rtw89_read32 (rtwdev,R_AX_DMAC_ERR_ISR )); +seq_printf (m,"[0]R_AX_WDRLS_ERR_ISR=0x%08x\n" , +rtw89_read32 (rtwdev,R_AX_WDRLS_ERR_ISR )); +seq_printf (m,"[1]R_AX_SEC_ERR_IMR_ISR=0x%08x\n" , +rtw89_read32 (rtwdev,R_AX_SEC_ERR_IMR_ISR )); +seq_printf (m,"[2.1]R_AX_MPDU_TX_ERR_ISR=0x%08x\n" , +rtw89_read32 (rtwdev,R_AX_MPDU_TX_ERR_ISR )); +seq_printf (m,"[2.2]R_AX_MPDU_RX_ERR_ISR=0x%08x\n" , +rtw89_read32 (rtwdev,R_AX_MPDU_RX_ERR_ISR )); +seq_printf (m,"[3]R_AX_STA_SCHEDULER_ERR_ISR=0x%08x\n" , +rtw89_read32 (rtwdev,R_AX_STA_SCHEDULER_ERR_ISR )); +seq_printf (m,"[4]R_AX_WDE_ERR_ISR=0x%08x\n" , +rtw89_read32 (rtwdev,R_AX_WDE_ERR_ISR )); +seq_printf (m,"[5.1]R_AX_TXPKTCTL_ERR_IMR_ISR=0x%08x\n" , +rtw89_read32 (rtwdev,R_AX_TXPKTCTL_ERR_IMR_ISR )); +seq_printf (m,"[5.2]R_AX_TXPKTCTL_ERR_IMR_ISR_B1=0x%08x\n" , +rtw89_read32 (rtwdev,R_AX_TXPKTCTL_ERR_IMR_ISR_B1 )); +seq_printf (m,"[6]R_AX_PLE_ERR_FLAG_ISR=0x%08x\n" , +rtw89_read32 (rtwdev,R_AX_PLE_ERR_FLAG_ISR )); +seq_printf (m,"[7]R_AX_PKTIN_ERR_ISR=0x%08x\n" , +rtw89_read32 (rtwdev,R_AX_PKTIN_ERR_ISR )); +seq_printf (m,"[8.1]R_AX_OTHER_DISPATCHER_ERR_ISR=0x%08x\n" , +rtw89_read32 (rtwdev,R_AX_OTHER_DISPATCHER_ERR_ISR )); +seq_printf (m,"[8.2]R_AX_HOST_DISPATCHER_ERR_ISR=0x%08x\n" , +rtw89_read32 (rtwdev,R_AX_HOST_DISPATCHER_ERR_ISR )); +seq_printf (m,"[8.3]R_AX_CPU_DISPATCHER_ERR_ISR=0x%08x\n" , +rtw89_read32 (rtwdev,R_AX_CPU_DISPATCHER_ERR_ISR )); +seq_printf (m,"[10]R_AX_CPUIO_ERR_ISR=0x%08x\n" , +rtw89_read32 (rtwdev,R_AX_CPUIO_ERR_ISR )); +seq_printf (m,"[11.1]R_AX_BBRPT_COM_ERR_IMR_ISR=0x%08x\n" , +rtw89_read32 (rtwdev,R_AX_BBRPT_COM_ERR_IMR_ISR )); +seq_printf (m,"[11.2]R_AX_BBRPT_CHINFO_ERR_IMR_ISR=0x%08x\n" , +rtw89_read32 (rtwdev,R_AX_BBRPT_CHINFO_ERR_IMR_ISR )); +seq_printf (m,"[11.3]R_AX_BBRPT_DFS_ERR_IMR_ISR=0x%08x\n" , +rtw89_read32 (rtwdev,R_AX_BBRPT_DFS_ERR_IMR_ISR )); +seq_printf (m,"[11.4]R_AX_LA_ERRFLAG=0x%08x\n" , +rtw89_read32 (rtwdev,R_AX_LA_ERRFLAG )); + +return 0 ; +} + +static int rtw89_debug_mac_dump_cmac_dbg (struct rtw89_dev *rtwdev, +struct seq_file *m) +{ +int ret; + + ret =rtw89_mac_check_mac_en (rtwdev,0 ,RTW89_CMAC_SEL ); +if (ret) { +seq_puts (m,"[CMAC] : CMAC 0 not enabled\n" ); +return ret; + } + +seq_printf (m,"R_AX_CMAC_ERR_ISR=0x%08x\n" , +rtw89_read32 (rtwdev,R_AX_CMAC_ERR_ISR )); +seq_printf (m,"[0]R_AX_SCHEDULE_ERR_ISR=0x%08x\n" , +rtw89_read32 (rtwdev,R_AX_SCHEDULE_ERR_ISR )); +seq_printf (m,"[1]R_AX_PTCL_ISR0=0x%08x\n" , +rtw89_read32 (rtwdev,R_AX_PTCL_ISR0 )); +seq_printf (m,"[3]R_AX_DLE_CTRL=0x%08x\n" , +rtw89_read32 (rtwdev,R_AX_DLE_CTRL )); +seq_printf (m,"[4]R_AX_PHYINFO_ERR_ISR=0x%08x\n" , +rtw89_read32 (rtwdev,R_AX_PHYINFO_ERR_ISR )); +seq_printf (m,"[5]R_AX_TXPWR_ISR=0x%08x\n" , +rtw89_read32 (rtwdev,R_AX_TXPWR_ISR )); +seq_printf (m,"[6]R_AX_RMAC_ERR_ISR=0x%08x\n" , +rtw89_read32 (rtwdev,R_AX_RMAC_ERR_ISR )); +seq_printf (m,"[7]R_AX_TMAC_ERR_IMR_ISR=0x%08x\n" , +rtw89_read32 (rtwdev,R_AX_TMAC_ERR_IMR_ISR )); + + ret =rtw89_mac_check_mac_en (rtwdev,1 ,RTW89_CMAC_SEL ); +if (ret) { +seq_puts (m,"[CMAC] : CMAC 1 not enabled\n" ); +return ret; + } + +seq_printf (m,"R_AX_CMAC_ERR_ISR_C1=0x%08x\n" , +rtw89_read32 (rtwdev,R_AX_CMAC_ERR_ISR_C1 )); +seq_printf (m,"[0]R_AX_SCHEDULE_ERR_ISR_C1=0x%08x\n" , +rtw89_read32 (rtwdev,R_AX_SCHEDULE_ERR_ISR_C1 )); +seq_printf (m,"[1]R_AX_PTCL_ISR0_C1=0x%08x\n" , +rtw89_read32 (rtwdev,R_AX_PTCL_ISR0_C1 )); +seq_printf (m,"[3]R_AX_DLE_CTRL_C1=0x%08x\n" , +rtw89_read32 (rtwdev,R_AX_DLE_CTRL_C1 )); +seq_printf (m,"[4]R_AX_PHYINFO_ERR_ISR_C1=0x%02x\n" , +rtw89_read32 (rtwdev,R_AX_PHYINFO_ERR_ISR_C1 )); +seq_printf (m,"[5]R_AX_TXPWR_ISR_C1=0x%08x\n" , +rtw89_read32 (rtwdev,R_AX_TXPWR_ISR_C1 )); +seq_printf (m,"[6]R_AX_RMAC_ERR_ISR_C1=0x%08x\n" , +rtw89_read32 (rtwdev,R_AX_RMAC_ERR_ISR_C1 )); +seq_printf (m,"[7]R_AX_TMAC_ERR_IMR_ISR_C1=0x%08x\n" , +rtw89_read32 (rtwdev,R_AX_TMAC_ERR_IMR_ISR_C1 )); + +return 0 ; +} + +static const struct rtw89_mac_dbg_port_info dbg_port_ptcl_c0 = { + .sel_addr =R_AX_PTCL_DBG , + .sel_byte =1 , + .sel_msk =B_AX_PTCL_DBG_SEL_MASK , + .srt =0x00 , + .end =0x3F , + .rd_addr =R_AX_PTCL_DBG_INFO , + .rd_byte =4 , + .rd_msk =B_AX_PTCL_DBG_INFO_MASK +}; + +static const struct rtw89_mac_dbg_port_info dbg_port_ptcl_c1 = { + .sel_addr =R_AX_PTCL_DBG_C1 , + .sel_byte =1 , + .sel_msk =B_AX_PTCL_DBG_SEL_MASK , + .srt =0x00 , + .end =0x3F , + .rd_addr =R_AX_PTCL_DBG_INFO_C1 , + .rd_byte =4 , + .rd_msk =B_AX_PTCL_DBG_INFO_MASK +}; + +static const struct rtw89_mac_dbg_port_info dbg_port_sch_c0 = { + .sel_addr =R_AX_SCH_DBG_SEL , + .sel_byte =1 , + .sel_msk =B_AX_SCH_DBG_SEL_MASK , + .srt =0x00 , + .end =0x2F , + .rd_addr =R_AX_SCH_DBG , + .rd_byte =4 , + .rd_msk =B_AX_SCHEDULER_DBG_MASK +}; + +static const struct rtw89_mac_dbg_port_info dbg_port_sch_c1 = { + .sel_addr =R_AX_SCH_DBG_SEL_C1 , + .sel_byte =1 , + .sel_msk =B_AX_SCH_DBG_SEL_MASK , + .srt =0x00 , + .end =0x2F , + .rd_addr =R_AX_SCH_DBG_C1 , + .rd_byte =4 , + .rd_msk =B_AX_SCHEDULER_DBG_MASK +}; + +static const struct rtw89_mac_dbg_port_info dbg_port_tmac_c0 = { + .sel_addr =R_AX_MACTX_DBG_SEL_CNT , + .sel_byte =1 , + .sel_msk =B_AX_DBGSEL_MACTX_MASK , + .srt =0x00 , + .end =0x19 , + .rd_addr =R_AX_DBG_PORT_SEL , + .rd_byte =4 , + .rd_msk =B_AX_DEBUG_ST_MASK +}; + +static const struct rtw89_mac_dbg_port_info dbg_port_tmac_c1 = { + .sel_addr =R_AX_MACTX_DBG_SEL_CNT_C1 , + .sel_byte =1 , + .sel_msk =B_AX_DBGSEL_MACTX_MASK , + .srt =0x00 , + .end =0x19 , + .rd_addr =R_AX_DBG_PORT_SEL , + .rd_byte =4 , + .rd_msk =B_AX_DEBUG_ST_MASK +}; + +static const struct rtw89_mac_dbg_port_info dbg_port_rmac_c0 = { + .sel_addr =R_AX_RX_DEBUG_SELECT , + .sel_byte =1 , + .sel_msk =B_AX_DEBUG_SEL_MASK , + .srt =0x00 , + .end =0x58 , + .rd_addr =R_AX_DBG_PORT_SEL , + .rd_byte =4 , + .rd_msk =B_AX_DEBUG_ST_MASK +}; + +static const struct rtw89_mac_dbg_port_info dbg_port_rmac_c1 = { + .sel_addr =R_AX_RX_DEBUG_SELECT_C1 , + .sel_byte =1 , + .sel_msk =B_AX_DEBUG_SEL_MASK , + .srt =0x00 , + .end =0x58 , + .rd_addr =R_AX_DBG_PORT_SEL , + .rd_byte =4 , + .rd_msk =B_AX_DEBUG_ST_MASK +}; + +static const struct rtw89_mac_dbg_port_info dbg_port_rmacst_c0 = { + .sel_addr =R_AX_RX_STATE_MONITOR , + .sel_byte =1 , + .sel_msk =B_AX_STATE_SEL_MASK , + .srt =0x00 , + .end =0x17 , + .rd_addr =R_AX_RX_STATE_MONITOR , + .rd_byte =4 , + .rd_msk =B_AX_RX_STATE_MONITOR_MASK +}; + +static const struct rtw89_mac_dbg_port_info dbg_port_rmacst_c1 = { + .sel_addr =R_AX_RX_STATE_MONITOR_C1 , + .sel_byte =1 , + .sel_msk =B_AX_STATE_SEL_MASK , + .srt =0x00 , + .end =0x17 , + .rd_addr =R_AX_RX_STATE_MONITOR_C1 , + .rd_byte =4 , + .rd_msk =B_AX_RX_STATE_MONITOR_MASK +}; + +static const struct rtw89_mac_dbg_port_info dbg_port_rmac_plcp_c0 = { + .sel_addr =R_AX_RMAC_PLCP_MON , + .sel_byte =4 , + .sel_msk =B_AX_PCLP_MON_SEL_MASK , + .srt =0x0 , + .end =0xF , + .rd_addr =R_AX_RMAC_PLCP_MON , + .rd_byte =4 , + .rd_msk =B_AX_RMAC_PLCP_MON_MASK +}; + +static const struct rtw89_mac_dbg_port_info dbg_port_rmac_plcp_c1 = { + .sel_addr =R_AX_RMAC_PLCP_MON_C1 , + .sel_byte =4 , + .sel_msk =B_AX_PCLP_MON_SEL_MASK , + .srt =0x0 , + .end =0xF , + .rd_addr =R_AX_RMAC_PLCP_MON_C1 , + .rd_byte =4 , + .rd_msk =B_AX_RMAC_PLCP_MON_MASK +}; + +static const struct rtw89_mac_dbg_port_info dbg_port_trxptcl_c0 = { + .sel_addr =R_AX_DBGSEL_TRXPTCL , + .sel_byte =1 , + .sel_msk =B_AX_DBGSEL_TRXPTCL_MASK , + .srt =0x08 , + .end =0x10 , + .rd_addr =R_AX_DBG_PORT_SEL , + .rd_byte =4 , + .rd_msk =B_AX_DEBUG_ST_MASK +}; + +static const struct rtw89_mac_dbg_port_info dbg_port_trxptcl_c1 = { + .sel_addr =R_AX_DBGSEL_TRXPTCL_C1 , + .sel_byte =1 , + .sel_msk =B_AX_DBGSEL_TRXPTCL_MASK , + .srt =0x08 , + .end =0x10 , + .rd_addr =R_AX_DBG_PORT_SEL , + .rd_byte =4 , + .rd_msk =B_AX_DEBUG_ST_MASK +}; + +static const struct rtw89_mac_dbg_port_info dbg_port_tx_infol_c0 = { + .sel_addr =R_AX_WMAC_TX_CTRL_DEBUG , + .sel_byte =1 , + .sel_msk =B_AX_TX_CTRL_DEBUG_SEL_MASK , + .srt =0x00 , + .end =0x07 , + .rd_addr =R_AX_WMAC_TX_INFO0_DEBUG , + .rd_byte =4 , + .rd_msk =B_AX_TX_CTRL_INFO_P0_MASK +}; + +static const struct rtw89_mac_dbg_port_info dbg_port_tx_infoh_c0 = { + .sel_addr =R_AX_WMAC_TX_CTRL_DEBUG , + .sel_byte =1 , + .sel_msk =B_AX_TX_CTRL_DEBUG_SEL_MASK , + .srt =0x00 , + .end =0x07 , + .rd_addr =R_AX_WMAC_TX_INFO1_DEBUG , + .rd_byte =4 , + .rd_msk =B_AX_TX_CTRL_INFO_P1_MASK +}; + +static const struct rtw89_mac_dbg_port_info dbg_port_tx_infol_c1 = { + .sel_addr =R_AX_WMAC_TX_CTRL_DEBUG_C1 , + .sel_byte =1 , + .sel_msk =B_AX_TX_CTRL_DEBUG_SEL_MASK , + .srt =0x00 , + .end =0x07 , + .rd_addr =R_AX_WMAC_TX_INFO0_DEBUG_C1 , + .rd_byte =4 , + .rd_msk =B_AX_TX_CTRL_INFO_P0_MASK +}; + +static const struct rtw89_mac_dbg_port_info dbg_port_tx_infoh_c1 = { + .sel_addr =R_AX_WMAC_TX_CTRL_DEBUG_C1 , + .sel_byte =1 , + .sel_msk =B_AX_TX_CTRL_DEBUG_SEL_MASK , + .srt =0x00 , + .end =0x07 , + .rd_addr =R_AX_WMAC_TX_INFO1_DEBUG_C1 , + .rd_byte =4 , + .rd_msk =B_AX_TX_CTRL_INFO_P1_MASK +}; + +static const struct rtw89_mac_dbg_port_info dbg_port_txtf_infol_c0 = { + .sel_addr =R_AX_WMAC_TX_TF_INFO_0 , + .sel_byte =1 , + .sel_msk =B_AX_WMAC_TX_TF_INFO_SEL_MASK , + .srt =0x00 , + .end =0x04 , + .rd_addr =R_AX_WMAC_TX_TF_INFO_1 , + .rd_byte =4 , + .rd_msk =B_AX_WMAC_TX_TF_INFO_P0_MASK +}; + +static const struct rtw89_mac_dbg_port_info dbg_port_txtf_infoh_c0 = { + .sel_addr =R_AX_WMAC_TX_TF_INFO_0 , + .sel_byte =1 , + .sel_msk =B_AX_WMAC_TX_TF_INFO_SEL_MASK , + .srt =0x00 , + .end =0x04 , + .rd_addr =R_AX_WMAC_TX_TF_INFO_2 , + .rd_byte =4 , + .rd_msk =B_AX_WMAC_TX_TF_INFO_P1_MASK +}; + +static const struct rtw89_mac_dbg_port_info dbg_port_txtf_infol_c1 = { + .sel_addr =R_AX_WMAC_TX_TF_INFO_0_C1 , + .sel_byte =1 , + .sel_msk =B_AX_WMAC_TX_TF_INFO_SEL_MASK , + .srt =0x00 , + .end =0x04 , + .rd_addr =R_AX_WMAC_TX_TF_INFO_1_C1 , + .rd_byte =4 , + .rd_msk =B_AX_WMAC_TX_TF_INFO_P0_MASK +}; + +static const struct rtw89_mac_dbg_port_info dbg_port_txtf_infoh_c1 = { + .sel_addr =R_AX_WMAC_TX_TF_INFO_0_C1 , + .sel_byte =1 , + .sel_msk =B_AX_WMAC_TX_TF_INFO_SEL_MASK , + .srt =0x00 , + .end =0x04 , + .rd_addr =R_AX_WMAC_TX_TF_INFO_2_C1 , + .rd_byte =4 , + .rd_msk =B_AX_WMAC_TX_TF_INFO_P1_MASK +}; + +static const struct rtw89_mac_dbg_port_info dbg_port_wde_bufmgn_freepg = { + .sel_addr =R_AX_WDE_DBG_FUN_INTF_CTL , + .sel_byte =4 , + .sel_msk =B_AX_WDE_DFI_DATA_MASK , + .srt =0x80000000 , + .end =0x80000001 , + .rd_addr =R_AX_WDE_DBG_FUN_INTF_DATA , + .rd_byte =4 , + .rd_msk =B_AX_WDE_DFI_DATA_MASK +}; + +static const struct rtw89_mac_dbg_port_info dbg_port_wde_bufmgn_quota = { + .sel_addr =R_AX_WDE_DBG_FUN_INTF_CTL , + .sel_byte =4 , + .sel_msk =B_AX_WDE_DFI_DATA_MASK , + .srt =0x80010000 , + .end =0x80010004 , + .rd_addr =R_AX_WDE_DBG_FUN_INTF_DATA , + .rd_byte =4 , + .rd_msk =B_AX_WDE_DFI_DATA_MASK +}; + +static const struct rtw89_mac_dbg_port_info dbg_port_wde_bufmgn_pagellt = { + .sel_addr =R_AX_WDE_DBG_FUN_INTF_CTL , + .sel_byte =4 , + .sel_msk =B_AX_WDE_DFI_DATA_MASK , + .srt =0x80020000 , + .end =0x80020FFF , + .rd_addr =R_AX_WDE_DBG_FUN_INTF_DATA , + .rd_byte =4 , + .rd_msk =B_AX_WDE_DFI_DATA_MASK +}; + +static const struct rtw89_mac_dbg_port_info dbg_port_wde_bufmgn_pktinfo = { + .sel_addr =R_AX_WDE_DBG_FUN_INTF_CTL , + .sel_byte =4 , + .sel_msk =B_AX_WDE_DFI_DATA_MASK , + .srt =0x80030000 , + .end =0x80030FFF , + .rd_addr =R_AX_WDE_DBG_FUN_INTF_DATA , + .rd_byte =4 , + .rd_msk =B_AX_WDE_DFI_DATA_MASK +}; + +static const struct rtw89_mac_dbg_port_info dbg_port_wde_quemgn_prepkt = { + .sel_addr =R_AX_WDE_DBG_FUN_INTF_CTL , + .sel_byte =4 , + .sel_msk =B_AX_WDE_DFI_DATA_MASK , + .srt =0x80040000 , + .end =0x80040FFF , + .rd_addr =R_AX_WDE_DBG_FUN_INTF_DATA , + .rd_byte =4 , + .rd_msk =B_AX_WDE_DFI_DATA_MASK +}; + +static const struct rtw89_mac_dbg_port_info dbg_port_wde_quemgn_nxtpkt = { + .sel_addr =R_AX_WDE_DBG_FUN_INTF_CTL , + .sel_byte =4 , + .sel_msk =B_AX_WDE_DFI_DATA_MASK , + .srt =0x80050000 , + .end =0x80050FFF , + .rd_addr =R_AX_WDE_DBG_FUN_INTF_DATA , + .rd_byte =4 , + .rd_msk =B_AX_WDE_DFI_DATA_MASK +}; + +static const struct rtw89_mac_dbg_port_info dbg_port_wde_quemgn_qlnktbl = { + .sel_addr =R_AX_WDE_DBG_FUN_INTF_CTL , + .sel_byte =4 , + .sel_msk =B_AX_WDE_DFI_DATA_MASK , + .srt =0x80060000 , + .end =0x80060453 , + .rd_addr =R_AX_WDE_DBG_FUN_INTF_DATA , + .rd_byte =4 , + .rd_msk =B_AX_WDE_DFI_DATA_MASK +}; + +static const struct rtw89_mac_dbg_port_info dbg_port_wde_quemgn_qempty = { + .sel_addr =R_AX_WDE_DBG_FUN_INTF_CTL , + .sel_byte =4 , + .sel_msk =B_AX_WDE_DFI_DATA_MASK , + .srt =0x80070000 , + .end =0x80070011 , + .rd_addr =R_AX_WDE_DBG_FUN_INTF_DATA , + .rd_byte =4 , + .rd_msk =B_AX_WDE_DFI_DATA_MASK +}; + +static const struct rtw89_mac_dbg_port_info dbg_port_ple_bufmgn_freepg = { + .sel_addr =R_AX_PLE_DBG_FUN_INTF_CTL , + .sel_byte =4 , + .sel_msk =B_AX_PLE_DFI_DATA_MASK , + .srt =0x80000000 , + .end =0x80000001 , + .rd_addr =R_AX_PLE_DBG_FUN_INTF_DATA , + .rd_byte =4 , + .rd_msk =B_AX_PLE_DFI_DATA_MASK +}; + +static const struct rtw89_mac_dbg_port_info dbg_port_ple_bufmgn_quota = { + .sel_addr =R_AX_PLE_DBG_FUN_INTF_CTL , + .sel_byte =4 , + .sel_msk =B_AX_PLE_DFI_DATA_MASK , + .srt =0x80010000 , + .end =0x8001000A , + .rd_addr =R_AX_PLE_DBG_FUN_INTF_DATA , + .rd_byte =4 , + .rd_msk =B_AX_PLE_DFI_DATA_MASK +}; + +static const struct rtw89_mac_dbg_port_info dbg_port_ple_bufmgn_pagellt = { + .sel_addr =R_AX_PLE_DBG_FUN_INTF_CTL , + .sel_byte =4 , + .sel_msk =B_AX_PLE_DFI_DATA_MASK , + .srt =0x80020000 , + .end =0x80020DBF , + .rd_addr =R_AX_PLE_DBG_FUN_INTF_DATA , + .rd_byte =4 , + .rd_msk =B_AX_PLE_DFI_DATA_MASK +}; + +static const struct rtw89_mac_dbg_port_info dbg_port_ple_bufmgn_pktinfo = { + .sel_addr =R_AX_PLE_DBG_FUN_INTF_CTL , + .sel_byte =4 , + .sel_msk =B_AX_PLE_DFI_DATA_MASK , + .srt =0x80030000 , + .end =0x80030DBF , + .rd_addr =R_AX_PLE_DBG_FUN_INTF_DATA , + .rd_byte =4 , + .rd_msk =B_AX_PLE_DFI_DATA_MASK +}; + +static const struct rtw89_mac_dbg_port_info dbg_port_ple_quemgn_prepkt = { + .sel_addr =R_AX_PLE_DBG_FUN_INTF_CTL , + .sel_byte =4 , + .sel_msk =B_AX_PLE_DFI_DATA_MASK , + .srt =0x80040000 , + .end =0x80040DBF , + .rd_addr =R_AX_PLE_DBG_FUN_INTF_DATA , + .rd_byte =4 , + .rd_msk =B_AX_PLE_DFI_DATA_MASK +}; + +static const struct rtw89_mac_dbg_port_info dbg_port_ple_quemgn_nxtpkt = { + .sel_addr =R_AX_PLE_DBG_FUN_INTF_CTL , + .sel_byte =4 , + .sel_msk =B_AX_PLE_DFI_DATA_MASK , + .srt =0x80050000 , + .end =0x80050DBF , + .rd_addr =R_AX_PLE_DBG_FUN_INTF_DATA , + .rd_byte =4 , + .rd_msk =B_AX_PLE_DFI_DATA_MASK +}; + +static const struct rtw89_mac_dbg_port_info dbg_port_ple_quemgn_qlnktbl = { + .sel_addr =R_AX_PLE_DBG_FUN_INTF_CTL , + .sel_byte =4 , + .sel_msk =B_AX_PLE_DFI_DATA_MASK , + .srt =0x80060000 , + .end =0x80060041 , + .rd_addr =R_AX_PLE_DBG_FUN_INTF_DATA , + .rd_byte =4 , + .rd_msk =B_AX_PLE_DFI_DATA_MASK +}; + +static const struct rtw89_mac_dbg_port_info dbg_port_ple_quemgn_qempty = { + .sel_addr =R_AX_PLE_DBG_FUN_INTF_CTL , + .sel_byte =4 , + .sel_msk =B_AX_PLE_DFI_DATA_MASK , + .srt =0x80070000 , + .end =0x80070001 , + .rd_addr =R_AX_PLE_DBG_FUN_INTF_DATA , + .rd_byte =4 , + .rd_msk =B_AX_PLE_DFI_DATA_MASK +}; + +static const struct rtw89_mac_dbg_port_info dbg_port_pktinfo = { + .sel_addr =R_AX_DBG_FUN_INTF_CTL , + .sel_byte =4 , + .sel_msk =B_AX_DFI_DATA_MASK , + .srt =0x80000000 , + .end =0x8000017f , + .rd_addr =R_AX_DBG_FUN_INTF_DATA , + .rd_byte =4 , + .rd_msk =B_AX_DFI_DATA_MASK +}; + +static const struct rtw89_mac_dbg_port_info dbg_port_pcie_txdma = { + .sel_addr =R_AX_PCIE_DBG_CTRL , + .sel_byte =2 , + .sel_msk =B_AX_DBG_SEL_MASK , + .srt =0x00 , + .end =0x03 , + .rd_addr =R_AX_DBG_PORT_SEL , + .rd_byte =4 , + .rd_msk =B_AX_DEBUG_ST_MASK +}; + +static const struct rtw89_mac_dbg_port_info dbg_port_pcie_rxdma = { + .sel_addr =R_AX_PCIE_DBG_CTRL , + .sel_byte =2 , + .sel_msk =B_AX_DBG_SEL_MASK , + .srt =0x00 , + .end =0x04 , + .rd_addr =R_AX_DBG_PORT_SEL , + .rd_byte =4 , + .rd_msk =B_AX_DEBUG_ST_MASK +}; + +static const struct rtw89_mac_dbg_port_info dbg_port_pcie_cvt = { + .sel_addr =R_AX_PCIE_DBG_CTRL , + .sel_byte =2 , + .sel_msk =B_AX_DBG_SEL_MASK , + .srt =0x00 , + .end =0x01 , + .rd_addr =R_AX_DBG_PORT_SEL , + .rd_byte =4 , + .rd_msk =B_AX_DEBUG_ST_MASK +}; + +static const struct rtw89_mac_dbg_port_info dbg_port_pcie_cxpl = { + .sel_addr =R_AX_PCIE_DBG_CTRL , + .sel_byte =2 , + .sel_msk =B_AX_DBG_SEL_MASK , + .srt =0x00 , + .end =0x05 , + .rd_addr =R_AX_DBG_PORT_SEL , + .rd_byte =4 , + .rd_msk =B_AX_DEBUG_ST_MASK +}; + +static const struct rtw89_mac_dbg_port_info dbg_port_pcie_io = { + .sel_addr =R_AX_PCIE_DBG_CTRL , + .sel_byte =2 , + .sel_msk =B_AX_DBG_SEL_MASK , + .srt =0x00 , + .end =0x05 , + .rd_addr =R_AX_DBG_PORT_SEL , + .rd_byte =4 , + .rd_msk =B_AX_DEBUG_ST_MASK +}; + +static const struct rtw89_mac_dbg_port_info dbg_port_pcie_misc = { + .sel_addr =R_AX_PCIE_DBG_CTRL , + .sel_byte =2 , + .sel_msk =B_AX_DBG_SEL_MASK , + .srt =0x00 , + .end =0x06 , + .rd_addr =R_AX_DBG_PORT_SEL , + .rd_byte =4 , + .rd_msk =B_AX_DEBUG_ST_MASK +}; + +static const struct rtw89_mac_dbg_port_info dbg_port_pcie_misc2 = { + .sel_addr =R_AX_DBG_CTRL , + .sel_byte =1 , + .sel_msk =B_AX_DBG_SEL0 , + .srt =0x34 , + .end =0x3C , + .rd_addr =R_AX_DBG_PORT_SEL , + .rd_byte =4 , + .rd_msk =B_AX_DEBUG_ST_MASK +}; + +static const struct rtw89_mac_dbg_port_info * +rtw89_debug_mac_dbg_port_sel (struct seq_file *m, +struct rtw89_dev *rtwdev,u32 sel) +{ +const struct rtw89_mac_dbg_port_info *info; +u32 val32; +u16 val16; +u8 val8; + +switch (sel) { +case RTW89_DBG_PORT_SEL_PTCL_C0 : + info = &dbg_port_ptcl_c0; + val16 =rtw89_read16 (rtwdev,R_AX_PTCL_DBG ); + val16 |=B_AX_PTCL_DBG_EN ; +rtw89_write16 (rtwdev,R_AX_PTCL_DBG , val16); +seq_puts (m,"Enable PTCL C0 dbgport.\n" ); +break ; +case RTW89_DBG_PORT_SEL_PTCL_C1 : + info = &dbg_port_ptcl_c1; + val16 =rtw89_read16 (rtwdev,R_AX_PTCL_DBG_C1 ); + val16 |=B_AX_PTCL_DBG_EN ; +rtw89_write16 (rtwdev,R_AX_PTCL_DBG_C1 , val16); +seq_puts (m,"Enable PTCL C1 dbgport.\n" ); +break ; +case RTW89_DBG_PORT_SEL_SCH_C0 : + info = &dbg_port_sch_c0; + val32 =rtw89_read32 (rtwdev,R_AX_SCH_DBG_SEL ); + val32 |=B_AX_SCH_DBG_EN ; +rtw89_write32 (rtwdev,R_AX_SCH_DBG_SEL , val32); +seq_puts (m,"Enable SCH C0 dbgport.\n" ); +break ; +case RTW89_DBG_PORT_SEL_SCH_C1 : + info = &dbg_port_sch_c1; + val32 =rtw89_read32 (rtwdev,R_AX_SCH_DBG_SEL_C1 ); + val32 |=B_AX_SCH_DBG_EN ; +rtw89_write32 (rtwdev,R_AX_SCH_DBG_SEL_C1 , val32); +seq_puts (m,"Enable SCH C1 dbgport.\n" ); +break ; +case RTW89_DBG_PORT_SEL_TMAC_C0 : + info = &dbg_port_tmac_c0; + val32 =rtw89_read32 (rtwdev,R_AX_DBGSEL_TRXPTCL ); + val32 =u32_replace_bits (val32,TRXPTRL_DBG_SEL_TMAC , +B_AX_DBGSEL_TRXPTCL_MASK ); +rtw89_write32 (rtwdev,R_AX_DBGSEL_TRXPTCL , val32); + + val32 =rtw89_read32 (rtwdev,R_AX_DBG_CTRL ); + val32 =u32_replace_bits (val32,TMAC_DBG_SEL_C0 ,B_AX_DBG_SEL0 ); + val32 =u32_replace_bits (val32,TMAC_DBG_SEL_C0 ,B_AX_DBG_SEL1 ); +rtw89_write32 (rtwdev,R_AX_DBG_CTRL , val32); + + val32 =rtw89_read32 (rtwdev,R_AX_SYS_STATUS1 ); + val32 =u32_replace_bits (val32,MAC_DBG_SEL ,B_AX_SEL_0XC0_MASK ); +rtw89_write32 (rtwdev,R_AX_SYS_STATUS1 , val32); +seq_puts (m,"Enable TMAC C0 dbgport.\n" ); +break ; +case RTW89_DBG_PORT_SEL_TMAC_C1 : + info = &dbg_port_tmac_c1; + val32 =rtw89_read32 (rtwdev,R_AX_DBGSEL_TRXPTCL_C1 ); + val32 =u32_replace_bits (val32,TRXPTRL_DBG_SEL_TMAC , +B_AX_DBGSEL_TRXPTCL_MASK ); +rtw89_write32 (rtwdev,R_AX_DBGSEL_TRXPTCL_C1 , val32); + + val32 =rtw89_read32 (rtwdev,R_AX_DBG_CTRL ); + val32 =u32_replace_bits (val32,TMAC_DBG_SEL_C1 ,B_AX_DBG_SEL0 ); + val32 =u32_replace_bits (val32,TMAC_DBG_SEL_C1 ,B_AX_DBG_SEL1 ); +rtw89_write32 (rtwdev,R_AX_DBG_CTRL , val32); + + val32 =rtw89_read32 (rtwdev,R_AX_SYS_STATUS1 ); + val32 =u32_replace_bits (val32,MAC_DBG_SEL ,B_AX_SEL_0XC0_MASK ); +rtw89_write32 (rtwdev,R_AX_SYS_STATUS1 , val32); +seq_puts (m,"Enable TMAC C1 dbgport.\n" ); +break ; +case RTW89_DBG_PORT_SEL_RMAC_C0 : + info = &dbg_port_rmac_c0; + val32 =rtw89_read32 (rtwdev,R_AX_DBGSEL_TRXPTCL ); + val32 =u32_replace_bits (val32,TRXPTRL_DBG_SEL_RMAC , +B_AX_DBGSEL_TRXPTCL_MASK ); +rtw89_write32 (rtwdev,R_AX_DBGSEL_TRXPTCL , val32); + + val32 =rtw89_read32 (rtwdev,R_AX_DBG_CTRL ); + val32 =u32_replace_bits (val32,RMAC_DBG_SEL_C0 ,B_AX_DBG_SEL0 ); + val32 =u32_replace_bits (val32,RMAC_DBG_SEL_C0 ,B_AX_DBG_SEL1 ); +rtw89_write32 (rtwdev,R_AX_DBG_CTRL , val32); + + val32 =rtw89_read32 (rtwdev,R_AX_SYS_STATUS1 ); + val32 =u32_replace_bits (val32,MAC_DBG_SEL ,B_AX_SEL_0XC0_MASK ); +rtw89_write32 (rtwdev,R_AX_SYS_STATUS1 , val32); + + val8 =rtw89_read8 (rtwdev,R_AX_DBGSEL_TRXPTCL ); + val8 =u8_replace_bits (val8,RMAC_CMAC_DBG_SEL , +B_AX_DBGSEL_TRXPTCL_MASK ); +rtw89_write8 (rtwdev,R_AX_DBGSEL_TRXPTCL , val8); +seq_puts (m,"Enable RMAC C0 dbgport.\n" ); +break ; +case RTW89_DBG_PORT_SEL_RMAC_C1 : + info = &dbg_port_rmac_c1; + val32 =rtw89_read32 (rtwdev,R_AX_DBGSEL_TRXPTCL_C1 ); + val32 =u32_replace_bits (val32,TRXPTRL_DBG_SEL_RMAC , +B_AX_DBGSEL_TRXPTCL_MASK ); +rtw89_write32 (rtwdev,R_AX_DBGSEL_TRXPTCL_C1 , val32); + + val32 =rtw89_read32 (rtwdev,R_AX_DBG_CTRL ); + val32 =u32_replace_bits (val32,RMAC_DBG_SEL_C1 ,B_AX_DBG_SEL0 ); + val32 =u32_replace_bits (val32,RMAC_DBG_SEL_C1 ,B_AX_DBG_SEL1 ); +rtw89_write32 (rtwdev,R_AX_DBG_CTRL , val32); + + val32 =rtw89_read32 (rtwdev,R_AX_SYS_STATUS1 ); + val32 =u32_replace_bits (val32,MAC_DBG_SEL ,B_AX_SEL_0XC0_MASK ); +rtw89_write32 (rtwdev,R_AX_SYS_STATUS1 , val32); + + val8 =rtw89_read8 (rtwdev,R_AX_DBGSEL_TRXPTCL_C1 ); + val8 =u8_replace_bits (val8,RMAC_CMAC_DBG_SEL , +B_AX_DBGSEL_TRXPTCL_MASK ); +rtw89_write8 (rtwdev,R_AX_DBGSEL_TRXPTCL_C1 , val8); +seq_puts (m,"Enable RMAC C1 dbgport.\n" ); +break ; +case RTW89_DBG_PORT_SEL_RMACST_C0 : + info = &dbg_port_rmacst_c0; +seq_puts (m,"Enable RMAC state C0 dbgport.\n" ); +break ; +case RTW89_DBG_PORT_SEL_RMACST_C1 : + info = &dbg_port_rmacst_c1; +seq_puts (m,"Enable RMAC state C1 dbgport.\n" ); +break ; +case RTW89_DBG_PORT_SEL_RMAC_PLCP_C0 : + info = &dbg_port_rmac_plcp_c0; +seq_puts (m,"Enable RMAC PLCP C0 dbgport.\n" ); +break ; +case RTW89_DBG_PORT_SEL_RMAC_PLCP_C1 : + info = &dbg_port_rmac_plcp_c1; +seq_puts (m,"Enable RMAC PLCP C1 dbgport.\n" ); +break ; +case RTW89_DBG_PORT_SEL_TRXPTCL_C0 : + info = &dbg_port_trxptcl_c0; + val32 =rtw89_read32 (rtwdev,R_AX_DBG_CTRL ); + val32 =u32_replace_bits (val32,TRXPTCL_DBG_SEL_C0 ,B_AX_DBG_SEL0 ); + val32 =u32_replace_bits (val32,TRXPTCL_DBG_SEL_C0 ,B_AX_DBG_SEL1 ); +rtw89_write32 (rtwdev,R_AX_DBG_CTRL , val32); + + val32 =rtw89_read32 (rtwdev,R_AX_SYS_STATUS1 ); + val32 =u32_replace_bits (val32,MAC_DBG_SEL ,B_AX_SEL_0XC0_MASK ); +rtw89_write32 (rtwdev,R_AX_SYS_STATUS1 , val32); +seq_puts (m,"Enable TRXPTCL C0 dbgport.\n" ); +break ; +case RTW89_DBG_PORT_SEL_TRXPTCL_C1 : + info = &dbg_port_trxptcl_c1; + val32 =rtw89_read32 (rtwdev,R_AX_DBG_CTRL ); + val32 =u32_replace_bits (val32,TRXPTCL_DBG_SEL_C1 ,B_AX_DBG_SEL0 ); + val32 =u32_replace_bits (val32,TRXPTCL_DBG_SEL_C1 ,B_AX_DBG_SEL1 ); +rtw89_write32 (rtwdev,R_AX_DBG_CTRL , val32); + + val32 =rtw89_read32 (rtwdev,R_AX_SYS_STATUS1 ); + val32 =u32_replace_bits (val32,MAC_DBG_SEL ,B_AX_SEL_0XC0_MASK ); +rtw89_write32 (rtwdev,R_AX_SYS_STATUS1 , val32); +seq_puts (m,"Enable TRXPTCL C1 dbgport.\n" ); +break ; +case RTW89_DBG_PORT_SEL_TX_INFOL_C0 : + info = &dbg_port_tx_infol_c0; + val32 =rtw89_read32 (rtwdev,R_AX_TCR1 ); + val32 |=B_AX_TCR_FORCE_READ_TXDFIFO ; +rtw89_write32 (rtwdev,R_AX_TCR1 , val32); +seq_puts (m,"Enable tx infol dump.\n" ); +break ; +case RTW89_DBG_PORT_SEL_TX_INFOH_C0 : + info = &dbg_port_tx_infoh_c0; + val32 =rtw89_read32 (rtwdev,R_AX_TCR1 ); + val32 |=B_AX_TCR_FORCE_READ_TXDFIFO ; +rtw89_write32 (rtwdev,R_AX_TCR1 , val32); +seq_puts (m,"Enable tx infoh dump.\n" ); +break ; +case RTW89_DBG_PORT_SEL_TX_INFOL_C1 : + info = &dbg_port_tx_infol_c1; + val32 =rtw89_read32 (rtwdev,R_AX_TCR1_C1 ); + val32 |=B_AX_TCR_FORCE_READ_TXDFIFO ; +rtw89_write32 (rtwdev,R_AX_TCR1_C1 , val32); +seq_puts (m,"Enable tx infol dump.\n" ); +break ; +case RTW89_DBG_PORT_SEL_TX_INFOH_C1 : + info = &dbg_port_tx_infoh_c1; + val32 =rtw89_read32 (rtwdev,R_AX_TCR1_C1 ); + val32 |=B_AX_TCR_FORCE_READ_TXDFIFO ; +rtw89_write32 (rtwdev,R_AX_TCR1_C1 , val32); +seq_puts (m,"Enable tx infoh dump.\n" ); +break ; +case RTW89_DBG_PORT_SEL_TXTF_INFOL_C0 : + info = &dbg_port_txtf_infol_c0; + val32 =rtw89_read32 (rtwdev,R_AX_TCR1 ); + val32 |=B_AX_TCR_FORCE_READ_TXDFIFO ; +rtw89_write32 (rtwdev,R_AX_TCR1 , val32); +seq_puts (m,"Enable tx tf infol dump.\n" ); +break ; +case RTW89_DBG_PORT_SEL_TXTF_INFOH_C0 : + info = &dbg_port_txtf_infoh_c0; + val32 =rtw89_read32 (rtwdev,R_AX_TCR1 ); + val32 |=B_AX_TCR_FORCE_READ_TXDFIFO ; +rtw89_write32 (rtwdev,R_AX_TCR1 , val32); +seq_puts (m,"Enable tx tf infoh dump.\n" ); +break ; +case RTW89_DBG_PORT_SEL_TXTF_INFOL_C1 : + info = &dbg_port_txtf_infol_c1; + val32 =rtw89_read32 (rtwdev,R_AX_TCR1_C1 ); + val32 |=B_AX_TCR_FORCE_READ_TXDFIFO ; +rtw89_write32 (rtwdev,R_AX_TCR1_C1 , val32); +seq_puts (m,"Enable tx tf infol dump.\n" ); +break ; +case RTW89_DBG_PORT_SEL_TXTF_INFOH_C1 : + info = &dbg_port_txtf_infoh_c1; + val32 =rtw89_read32 (rtwdev,R_AX_TCR1_C1 ); + val32 |=B_AX_TCR_FORCE_READ_TXDFIFO ; +rtw89_write32 (rtwdev,R_AX_TCR1_C1 , val32); +seq_puts (m,"Enable tx tf infoh dump.\n" ); +break ; +case RTW89_DBG_PORT_SEL_WDE_BUFMGN_FREEPG : + info = &dbg_port_wde_bufmgn_freepg; +seq_puts (m,"Enable wde bufmgn freepg dump.\n" ); +break ; +case RTW89_DBG_PORT_SEL_WDE_BUFMGN_QUOTA : + info = &dbg_port_wde_bufmgn_quota; +seq_puts (m,"Enable wde bufmgn quota dump.\n" ); +break ; +case RTW89_DBG_PORT_SEL_WDE_BUFMGN_PAGELLT : + info = &dbg_port_wde_bufmgn_pagellt; +seq_puts (m,"Enable wde bufmgn pagellt dump.\n" ); +break ; +case RTW89_DBG_PORT_SEL_WDE_BUFMGN_PKTINFO : + info = &dbg_port_wde_bufmgn_pktinfo; +seq_puts (m,"Enable wde bufmgn pktinfo dump.\n" ); +break ; +case RTW89_DBG_PORT_SEL_WDE_QUEMGN_PREPKT : + info = &dbg_port_wde_quemgn_prepkt; +seq_puts (m,"Enable wde quemgn prepkt dump.\n" ); +break ; +case RTW89_DBG_PORT_SEL_WDE_QUEMGN_NXTPKT : + info = &dbg_port_wde_quemgn_nxtpkt; +seq_puts (m,"Enable wde quemgn nxtpkt dump.\n" ); +break ; +case RTW89_DBG_PORT_SEL_WDE_QUEMGN_QLNKTBL : + info = &dbg_port_wde_quemgn_qlnktbl; +seq_puts (m,"Enable wde quemgn qlnktbl dump.\n" ); +break ; +case RTW89_DBG_PORT_SEL_WDE_QUEMGN_QEMPTY : + info = &dbg_port_wde_quemgn_qempty; +seq_puts (m,"Enable wde quemgn qempty dump.\n" ); +break ; +case RTW89_DBG_PORT_SEL_PLE_BUFMGN_FREEPG : + info = &dbg_port_ple_bufmgn_freepg; +seq_puts (m,"Enable ple bufmgn freepg dump.\n" ); +break ; +case RTW89_DBG_PORT_SEL_PLE_BUFMGN_QUOTA : + info = &dbg_port_ple_bufmgn_quota; +seq_puts (m,"Enable ple bufmgn quota dump.\n" ); +break ; +case RTW89_DBG_PORT_SEL_PLE_BUFMGN_PAGELLT : + info = &dbg_port_ple_bufmgn_pagellt; +seq_puts (m,"Enable ple bufmgn pagellt dump.\n" ); +break ; +case RTW89_DBG_PORT_SEL_PLE_BUFMGN_PKTINFO : + info = &dbg_port_ple_bufmgn_pktinfo; +seq_puts (m,"Enable ple bufmgn pktinfo dump.\n" ); +break ; +case RTW89_DBG_PORT_SEL_PLE_QUEMGN_PREPKT : + info = &dbg_port_ple_quemgn_prepkt; +seq_puts (m,"Enable ple quemgn prepkt dump.\n" ); +break ; +case RTW89_DBG_PORT_SEL_PLE_QUEMGN_NXTPKT : + info = &dbg_port_ple_quemgn_nxtpkt; +seq_puts (m,"Enable ple quemgn nxtpkt dump.\n" ); +break ; +case RTW89_DBG_PORT_SEL_PLE_QUEMGN_QLNKTBL : + info = &dbg_port_ple_quemgn_qlnktbl; +seq_puts (m,"Enable ple quemgn qlnktbl dump.\n" ); +break ; +case RTW89_DBG_PORT_SEL_PLE_QUEMGN_QEMPTY : + info = &dbg_port_ple_quemgn_qempty; +seq_puts (m,"Enable ple quemgn qempty dump.\n" ); +break ; +case RTW89_DBG_PORT_SEL_PKTINFO : + info = &dbg_port_pktinfo; +seq_puts (m,"Enable pktinfo dump.\n" ); +break ; +case RTW89_DBG_PORT_SEL_PCIE_TXDMA : + info = &dbg_port_pcie_txdma; + val32 =rtw89_read32 (rtwdev,R_AX_DBG_CTRL ); + val32 =u32_replace_bits (val32,PCIE_TXDMA_DBG_SEL ,B_AX_DBG_SEL0 ); + val32 =u32_replace_bits (val32,PCIE_TXDMA_DBG_SEL ,B_AX_DBG_SEL1 ); +rtw89_write32 (rtwdev,R_AX_DBG_CTRL , val32); +seq_puts (m,"Enable pcie txdma dump.\n" ); +break ; +case RTW89_DBG_PORT_SEL_PCIE_RXDMA : + info = &dbg_port_pcie_rxdma; + val32 =rtw89_read32 (rtwdev,R_AX_DBG_CTRL ); + val32 =u32_replace_bits (val32,PCIE_RXDMA_DBG_SEL ,B_AX_DBG_SEL0 ); + val32 =u32_replace_bits (val32,PCIE_RXDMA_DBG_SEL ,B_AX_DBG_SEL1 ); +rtw89_write32 (rtwdev,R_AX_DBG_CTRL , val32); +seq_puts (m,"Enable pcie rxdma dump.\n" ); +break ; +case RTW89_DBG_PORT_SEL_PCIE_CVT : + info = &dbg_port_pcie_cvt; + val32 =rtw89_read32 (rtwdev,R_AX_DBG_CTRL ); + val32 =u32_replace_bits (val32,PCIE_CVT_DBG_SEL ,B_AX_DBG_SEL0 ); + val32 =u32_replace_bits (val32,PCIE_CVT_DBG_SEL ,B_AX_DBG_SEL1 ); +rtw89_write32 (rtwdev,R_AX_DBG_CTRL , val32); +seq_puts (m,"Enable pcie cvt dump.\n" ); +break ; +case RTW89_DBG_PORT_SEL_PCIE_CXPL : + info = &dbg_port_pcie_cxpl; + val32 =rtw89_read32 (rtwdev,R_AX_DBG_CTRL ); + val32 =u32_replace_bits (val32,PCIE_CXPL_DBG_SEL ,B_AX_DBG_SEL0 ); + val32 =u32_replace_bits (val32,PCIE_CXPL_DBG_SEL ,B_AX_DBG_SEL1 ); +rtw89_write32 (rtwdev,R_AX_DBG_CTRL , val32); +seq_puts (m,"Enable pcie cxpl dump.\n" ); +break ; +case RTW89_DBG_PORT_SEL_PCIE_IO : + info = &dbg_port_pcie_io; + val32 =rtw89_read32 (rtwdev,R_AX_DBG_CTRL ); + val32 =u32_replace_bits (val32,PCIE_IO_DBG_SEL ,B_AX_DBG_SEL0 ); + val32 =u32_replace_bits (val32,PCIE_IO_DBG_SEL ,B_AX_DBG_SEL1 ); +rtw89_write32 (rtwdev,R_AX_DBG_CTRL , val32); +seq_puts (m,"Enable pcie io dump.\n" ); +break ; +case RTW89_DBG_PORT_SEL_PCIE_MISC : + info = &dbg_port_pcie_misc; + val32 =rtw89_read32 (rtwdev,R_AX_DBG_CTRL ); + val32 =u32_replace_bits (val32,PCIE_MISC_DBG_SEL ,B_AX_DBG_SEL0 ); + val32 =u32_replace_bits (val32,PCIE_MISC_DBG_SEL ,B_AX_DBG_SEL1 ); +rtw89_write32 (rtwdev,R_AX_DBG_CTRL , val32); +seq_puts (m,"Enable pcie misc dump.\n" ); +break ; +case RTW89_DBG_PORT_SEL_PCIE_MISC2 : + info = &dbg_port_pcie_misc2; + val16 =rtw89_read16 (rtwdev,R_AX_PCIE_DBG_CTRL ); + val16 =u16_replace_bits (val16,PCIE_MISC2_DBG_SEL , +B_AX_DBG_SEL_MASK ); +rtw89_write16 (rtwdev,R_AX_PCIE_DBG_CTRL , val16); +seq_puts (m,"Enable pcie misc2 dump.\n" ); +break ; +default : +seq_puts (m,"Dbg port select err\n" ); +return NULL ; + } + +return info; +} + +static bool is_dbg_port_valid (struct rtw89_dev *rtwdev,u32 sel) +{ +if (rtwdev->hci.type !=RTW89_HCI_TYPE_PCIE && + sel >=RTW89_DBG_PORT_SEL_PCIE_TXDMA && + sel <=RTW89_DBG_PORT_SEL_PCIE_MISC2 ) +return false; +if (rtwdev->chip->chip_id ==RTL8852B && + sel >=RTW89_DBG_PORT_SEL_PTCL_C1 && + sel <=RTW89_DBG_PORT_SEL_TXTF_INFOH_C1 ) +return false; +if (rtw89_mac_check_mac_en (rtwdev,0 ,RTW89_DMAC_SEL ) && + sel >=RTW89_DBG_PORT_SEL_WDE_BUFMGN_FREEPG && + sel <=RTW89_DBG_PORT_SEL_PKTINFO ) +return false; +if (rtw89_mac_check_mac_en (rtwdev,0 ,RTW89_CMAC_SEL ) && + sel >=RTW89_DBG_PORT_SEL_PTCL_C0 && + sel <=RTW89_DBG_PORT_SEL_TXTF_INFOH_C0 ) +return false; +if (rtw89_mac_check_mac_en (rtwdev,1 ,RTW89_CMAC_SEL ) && + sel >=RTW89_DBG_PORT_SEL_PTCL_C1 && + sel <=RTW89_DBG_PORT_SEL_TXTF_INFOH_C1 ) +return false; + +return true; +} + +static int rtw89_debug_mac_dbg_port_dump (struct rtw89_dev *rtwdev, +struct seq_file *m,u32 sel) +{ +const struct rtw89_mac_dbg_port_info *info; +u8 val8; +u16 val16; +u32 val32; +u32 i; + + info =rtw89_debug_mac_dbg_port_sel (m, rtwdev, sel); +if (!info) { +rtw89_err (rtwdev,"failed to select debug port %d\n" , sel); +return -EINVAL ; + } + +#define case_DBG_SEL (__sel) \ + case RTW89_DBG_PORT_SEL_##__sel: \ + seq_puts(m, "Dump debug port " #__sel ":\n"); \ + break + +switch (sel) { +case_DBG_SEL (PTCL_C0 ); +case_DBG_SEL (PTCL_C1 ); +case_DBG_SEL (SCH_C0 ); +case_DBG_SEL (SCH_C1 ); +case_DBG_SEL (TMAC_C0 ); +case_DBG_SEL (TMAC_C1 ); +case_DBG_SEL (RMAC_C0 ); +case_DBG_SEL (RMAC_C1 ); +case_DBG_SEL (RMACST_C0 ); +case_DBG_SEL (RMACST_C1 ); +case_DBG_SEL (TRXPTCL_C0 ); +case_DBG_SEL (TRXPTCL_C1 ); +case_DBG_SEL (TX_INFOL_C0 ); +case_DBG_SEL (TX_INFOH_C0 ); +case_DBG_SEL (TX_INFOL_C1 ); +case_DBG_SEL (TX_INFOH_C1 ); +case_DBG_SEL (TXTF_INFOL_C0 ); +case_DBG_SEL (TXTF_INFOH_C0 ); +case_DBG_SEL (TXTF_INFOL_C1 ); +case_DBG_SEL (TXTF_INFOH_C1 ); +case_DBG_SEL (WDE_BUFMGN_FREEPG ); +case_DBG_SEL (WDE_BUFMGN_QUOTA ); +case_DBG_SEL (WDE_BUFMGN_PAGELLT ); +case_DBG_SEL (WDE_BUFMGN_PKTINFO ); +case_DBG_SEL (WDE_QUEMGN_PREPKT ); +case_DBG_SEL (WDE_QUEMGN_NXTPKT ); +case_DBG_SEL (WDE_QUEMGN_QLNKTBL ); +case_DBG_SEL (WDE_QUEMGN_QEMPTY ); +case_DBG_SEL (PLE_BUFMGN_FREEPG ); +case_DBG_SEL (PLE_BUFMGN_QUOTA ); +case_DBG_SEL (PLE_BUFMGN_PAGELLT ); +case_DBG_SEL (PLE_BUFMGN_PKTINFO ); +case_DBG_SEL (PLE_QUEMGN_PREPKT ); +case_DBG_SEL (PLE_QUEMGN_NXTPKT ); +case_DBG_SEL (PLE_QUEMGN_QLNKTBL ); +case_DBG_SEL (PLE_QUEMGN_QEMPTY ); +case_DBG_SEL (PKTINFO ); +case_DBG_SEL (PCIE_TXDMA ); +case_DBG_SEL (PCIE_RXDMA ); +case_DBG_SEL (PCIE_CVT ); +case_DBG_SEL (PCIE_CXPL ); +case_DBG_SEL (PCIE_IO ); +case_DBG_SEL (PCIE_MISC ); +case_DBG_SEL (PCIE_MISC2 ); + } + +#undef case_DBG_SEL + +seq_printf (m,"Sel addr = 0x%X\n" , info->sel_addr); +seq_printf (m,"Read addr = 0x%X\n" , info->rd_addr); + +for (i = info->srt; i <= info->end; i++) { +switch (info->sel_byte) { +case 1 : +default : +rtw89_write8_mask (rtwdev, info->sel_addr, + info->sel_msk, i); +seq_printf (m,"0x%02X: " , i); +break ; +case 2 : +rtw89_write16_mask (rtwdev, info->sel_addr, + info->sel_msk, i); +seq_printf (m,"0x%04X: " , i); +break ; +case 4 : +rtw89_write32_mask (rtwdev, info->sel_addr, + info->sel_msk, i); +seq_printf (m,"0x%04X: " , i); +break ; + } + +udelay (10 ); + +switch (info->rd_byte) { +case 1 : +default : + val8 =rtw89_read8_mask (rtwdev, + info->rd_addr, info->rd_msk); +seq_printf (m,"0x%02X\n" , val8); +break ; +case 2 : + val16 =rtw89_read16_mask (rtwdev, + info->rd_addr, info->rd_msk); +seq_printf (m,"0x%04X\n" , val16); +break ; +case 4 : + val32 =rtw89_read32_mask (rtwdev, + info->rd_addr, info->rd_msk); +seq_printf (m,"0x%08X\n" , val32); +break ; + } + } + +return 0 ; +} + +static int rtw89_debug_mac_dump_dbg_port (struct rtw89_dev *rtwdev, +struct seq_file *m) +{ +u32 sel; +int ret =0 ; + +for (sel =RTW89_DBG_PORT_SEL_PTCL_C0 ; + sel <RTW89_DBG_PORT_SEL_LAST ; sel++) { +if (!is_dbg_port_valid (rtwdev, sel)) +continue ; + ret =rtw89_debug_mac_dbg_port_dump (rtwdev, m, sel); +if (ret) { +rtw89_err (rtwdev, +"failed to dump debug port %d\n" , sel); +break ; + } + } + +return ret; +} + +static int +rtw89_debug_priv_mac_dbg_port_dump_get (struct seq_file *m,void *v) +{ +struct rtw89_debugfs_priv *debugfs_priv = m->private; +struct rtw89_dev *rtwdev = debugfs_priv->rtwdev; + +if (debugfs_priv->dbgpkg_en.ss_dbg) +rtw89_debug_mac_dump_ss_dbg (rtwdev, m); +if (debugfs_priv->dbgpkg_en.dle_dbg) +rtw89_debug_mac_dump_dle_dbg (rtwdev, m); +if (debugfs_priv->dbgpkg_en.dmac_dbg) +rtw89_debug_mac_dump_dmac_dbg (rtwdev, m); +if (debugfs_priv->dbgpkg_en.cmac_dbg) +rtw89_debug_mac_dump_cmac_dbg (rtwdev, m); +if (debugfs_priv->dbgpkg_en.dbg_port) +rtw89_debug_mac_dump_dbg_port (rtwdev, m); + +return 0 ; +}; + +static ssize_t rtw89_debug_priv_send_h2c_set (struct file *filp, +const char __user *user_buf, +size_t count,loff_t *loff) +{ +struct rtw89_debugfs_priv *debugfs_priv = filp->private_data; +struct rtw89_dev *rtwdev = debugfs_priv->rtwdev; +char buf[256 ]; +size_t buf_size; +u8 h2c[128 ]; +int num; + + buf_size =min (count,sizeof (buf) -1 ); +if (copy_from_user (buf, user_buf, buf_size)) +return -EFAULT ; + + num = buf_size /2 ; +if (hex2bin (h2c, buf, num)) { +rtw89_info (rtwdev,"invalid format: H1H2H3...\n" ); +return -EINVAL ; + } + +rtw89_fw_h2c_raw (rtwdev, h2c, num); + +return count; +} + +static int rtw89_debug_priv_btc_info_get (struct seq_file *m,void *v) +{ +struct rtw89_debugfs_priv *debugfs_priv = m->private; +struct rtw89_dev *rtwdev = debugfs_priv->rtwdev; + +rtw89_btc_dump_info (rtwdev, m); + +return 0 ; +} + +static ssize_t rtw89_debug_priv_btc_manual_set (struct file *filp, +const char __user *user_buf, +size_t count,loff_t *loff) +{ +struct rtw89_debugfs_priv *debugfs_priv = filp->private_data; +struct rtw89_dev *rtwdev = debugfs_priv->rtwdev; +struct rtw89_btc *btc = &rtwdev->btc; +bool btc_manual; + +if (kstrtobool_from_user (user_buf, count, &btc_manual)) + goto out; + + btc->ctrl.manual = btc_manual; +out: +return count; +} + +static ssize_t rtw89_debug_fw_log_btc_manual_set (struct file *filp, +const char __user *user_buf, +size_t count,loff_t *loff) +{ +struct rtw89_debugfs_priv *debugfs_priv = filp->private_data; +struct rtw89_dev *rtwdev = debugfs_priv->rtwdev; +struct rtw89_fw_info *fw_info = &rtwdev->fw; +bool fw_log_manual; + +if (kstrtobool_from_user (user_buf, count, &fw_log_manual)) + goto out; + +mutex_lock (&rtwdev->mutex); + fw_info->fw_log_enable = fw_log_manual; +rtw89_fw_h2c_fw_log (rtwdev, fw_log_manual); +mutex_unlock (&rtwdev->mutex); +out: +return count; +} + +static void rtw89_sta_info_get_iter (void *data,struct ieee80211_sta *sta) +{ +static const char *const he_gi_str[] = { + [NL80211_RATE_INFO_HE_GI_0_8 ] ="0.8" , + [NL80211_RATE_INFO_HE_GI_1_6 ] ="1.6" , + [NL80211_RATE_INFO_HE_GI_3_2 ] ="3.2" , + }; +struct rtw89_sta *rtwsta = (struct rtw89_sta *)sta->drv_priv; +struct rate_info *rate = &rtwsta->ra_report.txrate; +struct ieee80211_rx_status *status = &rtwsta->rx_status; +struct seq_file *m = (struct seq_file *)data; +u8 rssi; + +seq_printf (m,"TX rate [%d]: " , rtwsta->mac_id); + +if (rate->flags &RATE_INFO_FLAGS_MCS ) +seq_printf (m,"HT MCS-%d%s" , rate->mcs, + rate->flags &RATE_INFO_FLAGS_SHORT_GI ?" SGI" :"" ); +else if (rate->flags &RATE_INFO_FLAGS_VHT_MCS ) +seq_printf (m,"VHT %dSS MCS-%d%s" , rate->nss, rate->mcs, + rate->flags &RATE_INFO_FLAGS_SHORT_GI ?" SGI" :"" ); +else if (rate->flags &RATE_INFO_FLAGS_HE_MCS ) +seq_printf (m,"HE %dSS MCS-%d GI:%s" , rate->nss, rate->mcs, + rate->he_gi <=NL80211_RATE_INFO_HE_GI_3_2 ? + he_gi_str[rate->he_gi] :"N/A" ); +else +seq_printf (m,"Legacy %d" , rate->legacy); +seq_printf (m,"\t(hw_rate=0x%x)" , rtwsta->ra_report.hw_rate); +seq_printf (m,"\t==> agg_wait=%d (%d)\n" , rtwsta->max_agg_wait, + sta->max_rc_amsdu_len); + +seq_printf (m,"RX rate [%d]: " , rtwsta->mac_id); + +switch (status->encoding) { +case RX_ENC_LEGACY : +seq_printf (m,"Legacy %d" , status->rate_idx + + (status->band ==NL80211_BAND_5GHZ ?4 :0 )); +break ; +case RX_ENC_HT : +seq_printf (m,"HT MCS-%d%s" , status->rate_idx, + status->enc_flags &RX_ENC_FLAG_SHORT_GI ?" SGI" :"" ); +break ; +case RX_ENC_VHT : +seq_printf (m,"VHT %dSS MCS-%d%s" , status->nss, status->rate_idx, + status->enc_flags &RX_ENC_FLAG_SHORT_GI ?" SGI" :"" ); +break ; +case RX_ENC_HE : +seq_printf (m,"HE %dSS MCS-%d GI:%s" , status->nss, status->rate_idx, + status->he_gi <=NL80211_RATE_INFO_HE_GI_3_2 ? + he_gi_str[rate->he_gi] :"N/A" ); +break ; + } +seq_printf (m,"\t(hw_rate=0x%x)\n" , rtwsta->rx_hw_rate); + + rssi =ewma_rssi_read (&rtwsta->avg_rssi); +seq_printf (m,"RSSI: %d dBm (raw=%d, prev=%d)\n" , +RTW89_RSSI_RAW_TO_DBM (rssi), rssi, rtwsta->prev_rssi); +} + +static void +rtw89_debug_append_rx_rate (struct seq_file *m,struct rtw89_pkt_stat *pkt_stat, +enum rtw89_hw_rate first_rate,int len) +{ +int i; + +for (i =0 ; i < len; i++) +seq_printf (m,"%s%u" , i ==0 ?"" :", " , + pkt_stat->rx_rate_cnt[first_rate + i]); +} + +static const struct rtw89_rx_rate_cnt_info { +enum rtw89_hw_rate first_rate; +int len; +const char *rate_mode; +} rtw89_rx_rate_cnt_infos[] = { + {RTW89_HW_RATE_CCK1 ,4 ,"Legacy:" }, + {RTW89_HW_RATE_OFDM6 ,8 ,"OFDM:" }, + {RTW89_HW_RATE_MCS0 ,8 ,"HT 0:" }, + {RTW89_HW_RATE_MCS8 ,8 ,"HT 1:" }, + {RTW89_HW_RATE_VHT_NSS1_MCS0 ,10 ,"VHT 1SS:" }, + {RTW89_HW_RATE_VHT_NSS2_MCS0 ,10 ,"VHT 2SS:" }, + {RTW89_HW_RATE_HE_NSS1_MCS0 ,12 ,"HE 1SS:" }, + {RTW89_HW_RATE_HE_NSS2_MCS0 ,12 ,"HE 2ss:" }, +}; + +static int rtw89_debug_priv_phy_info_get (struct seq_file *m,void *v) +{ +struct rtw89_debugfs_priv *debugfs_priv = m->private; +struct rtw89_dev *rtwdev = debugfs_priv->rtwdev; +struct rtw89_traffic_stats *stats = &rtwdev->stats; +struct rtw89_pkt_stat *pkt_stat = &rtwdev->phystat.last_pkt_stat; +const struct rtw89_rx_rate_cnt_info *info; +int i; + +seq_printf (m,"TP TX: %u [%u] Mbps (lv: %d), RX: %u [%u] Mbps (lv: %d)\n" , + stats->tx_throughput, stats->tx_throughput_raw, stats->tx_tfc_lv, + stats->rx_throughput, stats->rx_throughput_raw, stats->rx_tfc_lv); +seq_printf (m,"Beacon: %u\n" , pkt_stat->beacon_nr); +seq_printf (m,"Avg packet length: TX=%u, RX=%u\n" , stats->tx_avg_len, + stats->rx_avg_len); + +seq_puts (m,"RX count:\n" ); +for (i =0 ; i <ARRAY_SIZE (rtw89_rx_rate_cnt_infos); i++) { + info = &rtw89_rx_rate_cnt_infos[i]; +seq_printf (m,"%10s [" , info->rate_mode); +rtw89_debug_append_rx_rate (m, pkt_stat, + info->first_rate, info->len); +seq_puts (m,"]\n" ); + } + +ieee80211_iterate_stations_atomic (rtwdev->hw, rtw89_sta_info_get_iter, m); + +return 0 ; +} + +static struct rtw89_debugfs_priv rtw89_debug_priv_read_reg = { + .cb_read = rtw89_debug_priv_read_reg_get, + .cb_write = rtw89_debug_priv_read_reg_select, +}; + +static struct rtw89_debugfs_priv rtw89_debug_priv_write_reg = { + .cb_write = rtw89_debug_priv_write_reg_set, +}; + +static struct rtw89_debugfs_priv rtw89_debug_priv_read_rf = { + .cb_read = rtw89_debug_priv_read_rf_get, + .cb_write = rtw89_debug_priv_read_rf_select, +}; + +static struct rtw89_debugfs_priv rtw89_debug_priv_write_rf = { + .cb_write = rtw89_debug_priv_write_rf_set, +}; + +static struct rtw89_debugfs_priv rtw89_debug_priv_rf_reg_dump = { + .cb_read = rtw89_debug_priv_rf_reg_dump_get, +}; + +static struct rtw89_debugfs_priv rtw89_debug_priv_txpwr_table = { + .cb_read = rtw89_debug_priv_txpwr_table_get, +}; + +static struct rtw89_debugfs_priv rtw89_debug_priv_mac_reg_dump = { + .cb_read = rtw89_debug_priv_mac_reg_dump_get, + .cb_write = rtw89_debug_priv_mac_reg_dump_select, +}; + +static struct rtw89_debugfs_priv rtw89_debug_priv_mac_mem_dump = { + .cb_read = rtw89_debug_priv_mac_mem_dump_get, + .cb_write = rtw89_debug_priv_mac_mem_dump_select, +}; + +static struct rtw89_debugfs_priv rtw89_debug_priv_mac_dbg_port_dump = { + .cb_read = rtw89_debug_priv_mac_dbg_port_dump_get, + .cb_write = rtw89_debug_priv_mac_dbg_port_dump_select, +}; + +static struct rtw89_debugfs_priv rtw89_debug_priv_send_h2c = { + .cb_write = rtw89_debug_priv_send_h2c_set, +}; + +static struct rtw89_debugfs_priv rtw89_debug_priv_btc_info = { + .cb_read = rtw89_debug_priv_btc_info_get, +}; + +static struct rtw89_debugfs_priv rtw89_debug_priv_btc_manual = { + .cb_write = rtw89_debug_priv_btc_manual_set, +}; + +static struct rtw89_debugfs_priv rtw89_debug_priv_fw_log_manual = { + .cb_write = rtw89_debug_fw_log_btc_manual_set, +}; + +static struct rtw89_debugfs_priv rtw89_debug_priv_phy_info = { + .cb_read = rtw89_debug_priv_phy_info_get, +}; + +#define rtw89_debugfs_add (name, mode, fopname, parent) \ + do { \ + rtw89_debug_priv_ ##name.rtwdev = rtwdev; \ + if (!debugfs_create_file(#name, mode, \ + parent, &rtw89_debug_priv_ ##name, \ + &file_ops_ ##fopname)) \ + pr_debug("Unable to initialize debugfs:%s\n", #name); \ + } while (0) + +#define rtw89_debugfs_add_w (name) \ + rtw89_debugfs_add(name, S_IFREG | 0222, single_w, debugfs_topdir) +#define rtw89_debugfs_add_rw (name) \ + rtw89_debugfs_add(name, S_IFREG | 0666, common_rw, debugfs_topdir) +#define rtw89_debugfs_add_r (name) \ + rtw89_debugfs_add(name, S_IFREG | 0444, single_r, debugfs_topdir) + +void rtw89_debugfs_init (struct rtw89_dev *rtwdev) +{ +struct dentry *debugfs_topdir; + + debugfs_topdir =debugfs_create_dir ("rtw89" , + rtwdev->hw->wiphy->debugfsdir); + +rtw89_debugfs_add_rw (read_reg); +rtw89_debugfs_add_w (write_reg); +rtw89_debugfs_add_rw (read_rf); +rtw89_debugfs_add_w (write_rf); +rtw89_debugfs_add_r (rf_reg_dump); +rtw89_debugfs_add_r (txpwr_table); +rtw89_debugfs_add_rw (mac_reg_dump); +rtw89_debugfs_add_rw (mac_mem_dump); +rtw89_debugfs_add_rw (mac_dbg_port_dump); +rtw89_debugfs_add_w (send_h2c); +rtw89_debugfs_add_r (btc_info); +rtw89_debugfs_add_w (btc_manual); +rtw89_debugfs_add_w (fw_log_manual); +rtw89_debugfs_add_r (phy_info); +} +#endif + +#ifdef CONFIG_RTW89_DEBUGMSG +void __rtw89_debug (struct rtw89_dev *rtwdev, +enum rtw89_debug_mask mask, +const char *fmt, ...) +{ +struct va_format vaf = { + .fmt = fmt, + }; + +va_list args; + +va_start (args, fmt); + vaf.va = &args; + +if (rtw89_debug_mask & mask) +dev_printk (KERN_DEBUG , rtwdev->dev,"%pV" , &vaf); + +va_end (args); +} +EXPORT_SYMBOL (__rtw89_debug); +#endif
drivers/net/wireless/realtek/rtw89/debug.h new file +77 -0
77 lines not on this page · raw message
Signature · 1 line
2.25.1
Ping-Ke Shih
maintainerPATCH v6 5/24 rtw89: add efuse files
In reply to Ping-Ke ShihEfuse is divided into several parts, in which physical map and PHY cap parts are read and parsed by driver.
Storing main data, physical map is translated into logic map, and then use chip specific map to explain the logic map. Then, we can have MAC address, country code, thermal tracking calibration values, and so on.
PHY cap part is used to store PHY data. We don't need to translate it, because it's a direct map, like logic map.
--- drivers/net/wireless/realtek/rtw89/efuse.c | 188 +++++++++++++++++++++ drivers/net/wireless/realtek/rtw89/efuse.h | 13 ++ 2 files changed, 201 insertions(+) create mode 100644 drivers/net/wireless/realtek/rtw89/efuse.c create mode 100644 drivers/net/wireless/realtek/rtw89/efuse.h
drivers/net/wireless/realtek/rtw89/efuse.c new file +188 -0
@@ -0,0 +1,188 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* Copyright(c) 2019-2020 Realtek Corporation +*/ + +#include "debug.h" +#include "efuse.h" +#include "reg.h" + +enum rtw89_efuse_bank { +RTW89_EFUSE_BANK_WIFI , +RTW89_EFUSE_BANK_BT , +}; + +static int rtw89_switch_efuse_bank (struct rtw89_dev *rtwdev, +enum rtw89_efuse_bank bank) +{ +u8 val; + + val =rtw89_read32_mask (rtwdev,R_AX_EFUSE_CTRL_1 , +B_AX_EF_CELL_SEL_MASK ); +if (bank == val) +return 0 ; + +rtw89_write32_mask (rtwdev,R_AX_EFUSE_CTRL_1 ,B_AX_EF_CELL_SEL_MASK , + bank); + + val =rtw89_read32_mask (rtwdev,R_AX_EFUSE_CTRL_1 , +B_AX_EF_CELL_SEL_MASK ); +if (bank == val) +return 0 ; + +return -EBUSY ; +} + +static int rtw89_dump_physical_efuse_map (struct rtw89_dev *rtwdev,u8 *map, +u32 dump_addr,u32 dump_size) +{ +u32 efuse_ctl; +u32 addr; +int ret; + +rtw89_switch_efuse_bank (rtwdev,RTW89_EFUSE_BANK_WIFI ); + +for (addr = dump_addr; addr < dump_addr + dump_size; addr++) { + efuse_ctl =u32_encode_bits (addr,B_AX_EF_ADDR_MASK ); +rtw89_write32 (rtwdev,R_AX_EFUSE_CTRL , efuse_ctl & ~B_AX_EF_RDY ); + + ret =read_poll_timeout_atomic (rtw89_read32, efuse_ctl, + efuse_ctl &B_AX_EF_RDY ,1 ,1000000 , + true, rtwdev,R_AX_EFUSE_CTRL ); +if (ret) +return -EBUSY ; + + *map++ = (u8)(efuse_ctl &0xff ); + } + +return 0 ; +} + +#define invalid_efuse_header (hdr1, hdr2) \ + ((hdr1) == 0xff || (hdr2) == 0xff) +#define invalid_efuse_content (word_en, i) \ + (((word_en) & BIT(i)) != 0x0) +#define get_efuse_blk_idx (hdr1, hdr2) \ + ((((hdr2) & 0xf0) >> 4) | (((hdr1) & 0x0f) << 4)) +#define block_idx_to_logical_idx (blk_idx, i) \ + (((blk_idx) << 3) + ((i) << 1)) +static int rtw89_dump_logical_efuse_map (struct rtw89_dev *rtwdev,u8 *phy_map, +u8 *log_map) +{ +u32 physical_size = rtwdev->chip->physical_efuse_size; +u32 logical_size = rtwdev->chip->logical_efuse_size; +u8 sec_ctrl_size = rtwdev->chip->sec_ctrl_efuse_size; +u32 phy_idx = sec_ctrl_size; +u32 log_idx; +u8 hdr1, hdr2; +u8 blk_idx; +u8 word_en; +int i; + +while (phy_idx < physical_size - sec_ctrl_size) { + hdr1 = phy_map[phy_idx]; + hdr2 = phy_map[phy_idx +1 ]; +if (invalid_efuse_header (hdr1, hdr2)) +break ; + + blk_idx =get_efuse_blk_idx (hdr1, hdr2); + word_en = hdr2 &0xf ; + phy_idx +=2 ; + +for (i =0 ; i <4 ; i++) { +if (invalid_efuse_content (word_en, i)) +continue ; + + log_idx =block_idx_to_logical_idx (blk_idx, i); +if (phy_idx +1 > physical_size - sec_ctrl_size -1 || + log_idx +1 > logical_size) +return -EINVAL ; + + log_map[log_idx] = phy_map[phy_idx]; + log_map[log_idx +1 ] = phy_map[phy_idx +1 ]; + phy_idx +=2 ; + } + } +return 0 ; +} + +int rtw89_parse_efuse_map (struct rtw89_dev *rtwdev) +{ +u32 phy_size = rtwdev->chip->physical_efuse_size; +u32 log_size = rtwdev->chip->logical_efuse_size; +u8 *phy_map =NULL ; +u8 *log_map =NULL ; +int ret; + +if (rtw89_read16 (rtwdev,R_AX_SYS_WL_EFUSE_CTRL ) &B_AX_AUTOLOAD_SUS ) + rtwdev->efuse.valid = true; +else +rtw89_warn (rtwdev,"failed to check efuse autoload\n" ); + + phy_map =kmalloc (phy_size,GFP_KERNEL ); + log_map =kmalloc (log_size,GFP_KERNEL ); + +if (!phy_map || !log_map) { + ret = -ENOMEM ; + goto out_free; + } + + ret =rtw89_dump_physical_efuse_map (rtwdev, phy_map,0 , phy_size); +if (ret) { +rtw89_warn (rtwdev,"failed to dump efuse physical map\n" ); + goto out_free; + } + +memset (log_map,0xff , log_size); + ret =rtw89_dump_logical_efuse_map (rtwdev, phy_map, log_map); +if (ret) { +rtw89_warn (rtwdev,"failed to dump efuse logical map\n" ); + goto out_free; + } + +rtw89_hex_dump (rtwdev,RTW89_DBG_FW ,"log_map: " , log_map, log_size); + + ret = rtwdev->chip->ops->read_efuse (rtwdev, log_map); +if (ret) { +rtw89_warn (rtwdev,"failed to read efuse map\n" ); + goto out_free; + } + +out_free: +kfree (log_map); +kfree (phy_map); + +return ret; +} + +int rtw89_parse_phycap_map (struct rtw89_dev *rtwdev) +{ +u32 phycap_addr = rtwdev->chip->phycap_addr; +u32 phycap_size = rtwdev->chip->phycap_size; +u8 *phycap_map =NULL ; +int ret =0 ; + +if (!phycap_size) +return 0 ; + + phycap_map =kmalloc (phycap_size,GFP_KERNEL ); +if (!phycap_map) +return -ENOMEM ; + + ret =rtw89_dump_physical_efuse_map (rtwdev, phycap_map, + phycap_addr, phycap_size); +if (ret) { +rtw89_warn (rtwdev,"failed to dump phycap map\n" ); + goto out_free; + } + + ret = rtwdev->chip->ops->read_phycap (rtwdev, phycap_map); +if (ret) { +rtw89_warn (rtwdev,"failed to read phycap map\n" ); + goto out_free; + } + +out_free: +kfree (phycap_map); + +return ret; +}
drivers/net/wireless/realtek/rtw89/efuse.h new file +13 -0
@@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* Copyright(c) 2019-2020 Realtek Corporation +*/ + +#ifndef __RTW89_EFUSE_H__ +#define __RTW89_EFUSE_H__ + +#include "core.h" + +int rtw89_parse_efuse_map (struct rtw89_dev *rtwdev); +int rtw89_parse_phycap_map (struct rtw89_dev *rtwdev); + +#endif
Signature · 1 line
2.25.1
Ping-Ke Shih
maintainerPATCH v6 6/24 rtw89: add files to download and communicate with firmware
In reply to Ping-Ke ShihThe firmware must be downloaded right after HCI basic initialization, and then we can obtain hardware capabilities that are used to do mac80211 register hw.
To download firmware, we need to parse the header to know how many sections the firmware has, and then download each section to proper location.
We introduce H2C and C2H to do bi-direction communication with firmware, and both support packet-based and register-based methods. Normally, we use packet-based H2C/C2H, because it has no number and size limits. In contrast, register-based H2C/C2H has only one message and fix-four-dword in size.
Header size of packet-based H2C/C2H is eight bytes (two dwords), which uses a hierarchical IDs, containing type, category, class and function, to classify a H2C or C2H command.
When a C2H is received in interrupt context, we don't process it right there, but queue the skb and wake up a ieee80211 work to handle the skb.
--- drivers/net/wireless/realtek/rtw89/fw.c | 1600 +++++++++++++++++++++++ drivers/net/wireless/realtek/rtw89/fw.h | 1394 ++++++++++++++++++++ 2 files changed, 2994 insertions(+) create mode 100644 drivers/net/wireless/realtek/rtw89/fw.c create mode 100644 drivers/net/wireless/realtek/rtw89/fw.h
drivers/net/wireless/realtek/rtw89/fw.c new file +1600 -0
@@ -0,0 +1,1600 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* Copyright(c) 2019-2020 Realtek Corporation +*/ + +#include "cam.h" +#include "coex.h" +#include "debug.h" +#include "fw.h" +#include "mac.h" +#include "phy.h" +#include "reg.h" + +static struct sk_buff *rtw89_fw_h2c_alloc_skb (u32 len,bool header) +{ +struct sk_buff *skb; +u32 header_len =0 ; + +if (header) + header_len =H2C_HEADER_LEN ; + + skb =dev_alloc_skb (len + header_len +24 ); +if (!skb) +return NULL ; +skb_reserve (skb, header_len +24 ); +memset (skb->data,0 , len); + +return skb; +} + +struct sk_buff *rtw89_fw_h2c_alloc_skb_with_hdr (u32 len) +{ +return rtw89_fw_h2c_alloc_skb (len, true); +} + +struct sk_buff *rtw89_fw_h2c_alloc_skb_no_hdr (u32 len) +{ +return rtw89_fw_h2c_alloc_skb (len, false); +} + +static u8 _fw_get_rdy (struct rtw89_dev *rtwdev) +{ +u8 val =rtw89_read8 (rtwdev,R_AX_WCPU_FW_CTRL ); + +return FIELD_GET (B_AX_WCPU_FWDL_STS_MASK , val); +} + +#define FWDL_WAIT_CNT 400000 +int rtw89_fw_check_rdy (struct rtw89_dev *rtwdev) +{ +u8 val; +int ret; + + ret =read_poll_timeout_atomic (_fw_get_rdy, val, + val ==RTW89_FWDL_WCPU_FW_INIT_RDY , +1 ,FWDL_WAIT_CNT , false, rtwdev); +if (ret) { +switch (val) { +case RTW89_FWDL_CHECKSUM_FAIL : +rtw89_err (rtwdev,"fw checksum fail\n" ); +return -EINVAL ; + +case RTW89_FWDL_SECURITY_FAIL : +rtw89_err (rtwdev,"fw security fail\n" ); +return -EINVAL ; + +case RTW89_FWDL_CV_NOT_MATCH : +rtw89_err (rtwdev,"fw cv not match\n" ); +return -EINVAL ; + +default : +return -EBUSY ; + } + } + +set_bit (RTW89_FLAG_FW_RDY , rtwdev->flags); + +return 0 ; +} + +static int rtw89_fw_hdr_parser (struct rtw89_dev *rtwdev,const u8 *fw,u32 len, +struct rtw89_fw_bin_info *info) +{ +struct rtw89_fw_hdr_section_info *section_info; +const u8 *fw_end = fw + len; +const u8 *bin; +u32 i; + +if (!info) +return -EINVAL ; + + info->section_num =GET_FW_HDR_SEC_NUM (fw); + info->hdr_len =RTW89_FW_HDR_SIZE + + info->section_num *RTW89_FW_SECTION_HDR_SIZE ; +SET_FW_HDR_PART_SIZE (fw,FWDL_SECTION_PER_PKT_LEN ); + + bin = fw + info->hdr_len; + +/* jump to section header */ + fw +=RTW89_FW_HDR_SIZE ; + section_info = info->section_info; +for (i =0 ; i < info->section_num; i++) { + section_info->len =GET_FWSECTION_HDR_SEC_SIZE (fw); +if (GET_FWSECTION_HDR_CHECKSUM (fw)) + section_info->len +=FWDL_SECTION_CHKSUM_LEN ; + section_info->redl =GET_FWSECTION_HDR_REDL (fw); + section_info->dladdr = +GET_FWSECTION_HDR_DL_ADDR (fw) &0x1fffffff ; + section_info->addr = bin; + bin += section_info->len; + fw +=RTW89_FW_SECTION_HDR_SIZE ; + section_info++; + } + +if (fw_end != bin) { +rtw89_err (rtwdev,"[ERR]fw bin size\n" ); +return -EINVAL ; + } + +return 0 ; +} + +static +int rtw89_mfw_recognize (struct rtw89_dev *rtwdev,enum rtw89_fw_type type, +struct rtw89_fw_suit *fw_suit) +{ +struct rtw89_fw_info *fw_info = &rtwdev->fw; +const u8 *mfw = fw_info->firmware->data; +u32 mfw_len = fw_info->firmware->size; +const struct rtw89_mfw_hdr *mfw_hdr = (const struct rtw89_mfw_hdr *)mfw; +const struct rtw89_mfw_info *mfw_info; +int i; + +if (mfw_hdr->sig !=RTW89_MFW_SIG ) { +rtw89_debug (rtwdev,RTW89_DBG_FW ,"use legacy firmware\n" ); +/* legacy firmware support normal type only */ +if (type !=RTW89_FW_NORMAL ) +return -EINVAL ; + fw_suit->data = mfw; + fw_suit->size = mfw_len; +return 0 ; + } + +for (i =0 ; i < mfw_hdr->fw_nr; i++) { + mfw_info = &mfw_hdr->info[i]; +if (mfw_info->cv != rtwdev->hal.cv || + mfw_info->type != type || + mfw_info->mp) +continue ; + + fw_suit->data = mfw +le32_to_cpu (mfw_info->shift); + fw_suit->size =le32_to_cpu (mfw_info->size); +return 0 ; + } + +rtw89_err (rtwdev,"no suitable firmware found\n" ); +return -ENOENT ; +} + +static void rtw89_fw_update_ver (struct rtw89_dev *rtwdev, +enum rtw89_fw_type type, +struct rtw89_fw_suit *fw_suit) +{ +const u8 *hdr = fw_suit->data; + + fw_suit->major_ver =GET_FW_HDR_MAJOR_VERSION (hdr); + fw_suit->minor_ver =GET_FW_HDR_MINOR_VERSION (hdr); + fw_suit->sub_ver =GET_FW_HDR_SUBVERSION (hdr); + fw_suit->sub_idex =GET_FW_HDR_SUBINDEX (hdr); + fw_suit->build_year =GET_FW_HDR_YEAR (hdr); + fw_suit->build_mon =GET_FW_HDR_MONTH (hdr); + fw_suit->build_date =GET_FW_HDR_DATE (hdr); + fw_suit->build_hour =GET_FW_HDR_HOUR (hdr); + fw_suit->build_min =GET_FW_HDR_MIN (hdr); + fw_suit->cmd_ver =GET_FW_HDR_CMD_VERSERION (hdr); + +rtw89_info (rtwdev, +"Firmware version %u.%u.%u.%u, cmd version %u, type %u\n" , + fw_suit->major_ver, fw_suit->minor_ver, fw_suit->sub_ver, + fw_suit->sub_idex, fw_suit->cmd_ver, type); +} + +static +int __rtw89_fw_recognize (struct rtw89_dev *rtwdev,enum rtw89_fw_type type) +{ +struct rtw89_fw_suit *fw_suit =rtw89_fw_suit_get (rtwdev, type); +int ret; + + ret =rtw89_mfw_recognize (rtwdev, type, fw_suit); +if (ret) +return ret; + +rtw89_fw_update_ver (rtwdev, type, fw_suit); + +return 0 ; +} + +int rtw89_fw_recognize (struct rtw89_dev *rtwdev) +{ +int ret; + + ret =__rtw89_fw_recognize (rtwdev,RTW89_FW_NORMAL ); +if (ret) +return ret; + +/* It still works if wowlan firmware isn't existing. */ +__rtw89_fw_recognize (rtwdev,RTW89_FW_WOWLAN ); + +return 0 ; +} + +void rtw89_h2c_pkt_set_hdr (struct rtw89_dev *rtwdev,struct sk_buff *skb, +u8 type,u8 cat,u8 class,u8 func, +bool rack,bool dack,u32 len) +{ +struct fwcmd_hdr *hdr; + + hdr = (struct fwcmd_hdr *)skb_push (skb,8 ); + +if (!(rtwdev->fw.h2c_seq %4 )) + rack = true; + hdr->hdr0 =cpu_to_le32 (FIELD_PREP (H2C_HDR_DEL_TYPE , type) | +FIELD_PREP (H2C_HDR_CAT , cat) | +FIELD_PREP (H2C_HDR_CLASS , class) | +FIELD_PREP (H2C_HDR_FUNC , func) | +FIELD_PREP (H2C_HDR_H2C_SEQ , rtwdev->fw.h2c_seq)); + + hdr->hdr1 =cpu_to_le32 (FIELD_PREP (H2C_HDR_TOTAL_LEN , + len +H2C_HEADER_LEN ) | + (rack ?H2C_HDR_REC_ACK :0 ) | + (dack ?H2C_HDR_DONE_ACK :0 )); + + rtwdev->fw.h2c_seq++; +} + +static void rtw89_h2c_pkt_set_hdr_fwdl (struct rtw89_dev *rtwdev, +struct sk_buff *skb, +u8 type,u8 cat,u8 class,u8 func, +u32 len) +{ +struct fwcmd_hdr *hdr; + + hdr = (struct fwcmd_hdr *)skb_push (skb,8 ); + + hdr->hdr0 =cpu_to_le32 (FIELD_PREP (H2C_HDR_DEL_TYPE , type) | +FIELD_PREP (H2C_HDR_CAT , cat) | +FIELD_PREP (H2C_HDR_CLASS , class) | +FIELD_PREP (H2C_HDR_FUNC , func) | +FIELD_PREP (H2C_HDR_H2C_SEQ , rtwdev->fw.h2c_seq)); + + hdr->hdr1 =cpu_to_le32 (FIELD_PREP (H2C_HDR_TOTAL_LEN , + len +H2C_HEADER_LEN )); +} + +static int __rtw89_fw_download_hdr (struct rtw89_dev *rtwdev,const u8 *fw,u32 len) +{ +struct sk_buff *skb; +u32 ret =0 ; + + skb =rtw89_fw_h2c_alloc_skb_with_hdr (len); +if (!skb) { +rtw89_err (rtwdev,"failed to alloc skb for fw hdr dl\n" ); +return -ENOMEM ; + } + +skb_put_data (skb, fw, len); +rtw89_h2c_pkt_set_hdr_fwdl (rtwdev, skb,FWCMD_TYPE_H2C , +H2C_CAT_MAC ,H2C_CL_MAC_FWDL , +H2C_FUNC_MAC_FWHDR_DL , len); + + ret =rtw89_h2c_tx (rtwdev, skb, false); +if (ret) { +rtw89_err (rtwdev,"failed to send h2c\n" ); + ret =-1 ; + goto fail; + } + +return 0 ; +fail: +dev_kfree_skb_any (skb); + +return ret; +} + +static int rtw89_fw_download_hdr (struct rtw89_dev *rtwdev,const u8 *fw,u32 len) +{ +u8 val; +int ret; + + ret =__rtw89_fw_download_hdr (rtwdev, fw, len); +if (ret) { +rtw89_err (rtwdev,"[ERR]FW header download\n" ); +return ret; + } + + ret =read_poll_timeout_atomic (rtw89_read8, val, val &B_AX_FWDL_PATH_RDY , +1 ,FWDL_WAIT_CNT , false, + rtwdev,R_AX_WCPU_FW_CTRL ); +if (ret) { +rtw89_err (rtwdev,"[ERR]FWDL path ready\n" ); +return ret; + } + +rtw89_write32 (rtwdev,R_AX_HALT_H2C_CTRL ,0 ); +rtw89_write32 (rtwdev,R_AX_HALT_C2H_CTRL ,0 ); + +return 0 ; +} + +static int __rtw89_fw_download_main (struct rtw89_dev *rtwdev, +struct rtw89_fw_hdr_section_info *info) +{ +struct sk_buff *skb; +const u8 *section = info->addr; +u32 residue_len = info->len; +u32 pkt_len; +int ret; + +while (residue_len) { +if (residue_len >=FWDL_SECTION_PER_PKT_LEN ) + pkt_len =FWDL_SECTION_PER_PKT_LEN ; +else + pkt_len = residue_len; + + skb =rtw89_fw_h2c_alloc_skb_no_hdr (pkt_len); +if (!skb) { +rtw89_err (rtwdev,"failed to alloc skb for fw dl\n" ); +return -ENOMEM ; + } +skb_put_data (skb, section, pkt_len); + + ret =rtw89_h2c_tx (rtwdev, skb, true); +if (ret) { +rtw89_err (rtwdev,"failed to send h2c\n" ); + ret =-1 ; + goto fail; + } + + section += pkt_len; + residue_len -= pkt_len; + } + +return 0 ; +fail: +dev_kfree_skb_any (skb); + +return ret; +} + +static int rtw89_fw_download_main (struct rtw89_dev *rtwdev,const u8 *fw, +struct rtw89_fw_bin_info *info) +{ +struct rtw89_fw_hdr_section_info *section_info = info->section_info; +u8 section_num = info->section_num; +int ret; + +while (section_num--) { + ret =__rtw89_fw_download_main (rtwdev, section_info); +if (ret) +return ret; + section_info++; + } + +mdelay (5 ); + + ret =rtw89_fw_check_rdy (rtwdev); +if (ret) { +rtw89_warn (rtwdev,"download firmware fail\n" ); +return ret; + } + +return 0 ; +} + +static void rtw89_fw_prog_cnt_dump (struct rtw89_dev *rtwdev) +{ +u32 val32; +u16 index; + +rtw89_write32 (rtwdev,R_AX_DBG_CTRL , +FIELD_PREP (B_AX_DBG_SEL0 ,FW_PROG_CNTR_DBG_SEL ) | +FIELD_PREP (B_AX_DBG_SEL1 ,FW_PROG_CNTR_DBG_SEL )); +rtw89_write32_mask (rtwdev,R_AX_SYS_STATUS1 ,B_AX_SEL_0XC0_MASK ,MAC_DBG_SEL ); + +for (index =0 ; index <15 ; index++) { + val32 =rtw89_read32 (rtwdev,R_AX_DBG_PORT_SEL ); +rtw89_err (rtwdev,"[ERR]fw PC = 0x%x\n" , val32); +fsleep (10 ); + } +} + +static void rtw89_fw_dl_fail_dump (struct rtw89_dev *rtwdev) +{ +u32 val32; +u16 val16; + + val32 =rtw89_read32 (rtwdev,R_AX_WCPU_FW_CTRL ); +rtw89_err (rtwdev,"[ERR]fwdl 0x1E0 = 0x%x\n" , val32); + + val16 =rtw89_read16 (rtwdev,R_AX_BOOT_DBG +2 ); +rtw89_err (rtwdev,"[ERR]fwdl 0x83F2 = 0x%x\n" , val16); + +rtw89_fw_prog_cnt_dump (rtwdev); +} + +int rtw89_fw_download (struct rtw89_dev *rtwdev,enum rtw89_fw_type type) +{ +struct rtw89_fw_info *fw_info = &rtwdev->fw; +struct rtw89_fw_suit *fw_suit =rtw89_fw_suit_get (rtwdev, type); +struct rtw89_fw_bin_info info; +const u8 *fw = fw_suit->data; +u32 len = fw_suit->size; +u8 val; +int ret; + +if (!fw || !len) { +rtw89_err (rtwdev,"fw type %d isn't recognized\n" , type); +return -ENOENT ; + } + + ret =rtw89_fw_hdr_parser (rtwdev, fw, len, &info); +if (ret) { +rtw89_err (rtwdev,"parse fw header fail\n" ); + goto fwdl_err; + } + + ret =read_poll_timeout_atomic (rtw89_read8, val, val &B_AX_H2C_PATH_RDY , +1 ,FWDL_WAIT_CNT , false, + rtwdev,R_AX_WCPU_FW_CTRL ); +if (ret) { +rtw89_err (rtwdev,"[ERR]H2C path ready\n" ); + goto fwdl_err; + } + + ret =rtw89_fw_download_hdr (rtwdev, fw, info.hdr_len); +if (ret) { + ret = -EBUSY ; + goto fwdl_err; + } + + ret =rtw89_fw_download_main (rtwdev, fw, &info); +if (ret) { + ret = -EBUSY ; + goto fwdl_err; + } + + fw_info->h2c_seq =0 ; + fw_info->rec_seq =0 ; + rtwdev->mac.rpwm_seq_num =RPWM_SEQ_NUM_MAX ; + rtwdev->mac.cpwm_seq_num =CPWM_SEQ_NUM_MAX ; + +return ret; + +fwdl_err: +rtw89_fw_dl_fail_dump (rtwdev); +return ret; +} + +int rtw89_wait_firmware_completion (struct rtw89_dev *rtwdev) +{ +struct rtw89_fw_info *fw = &rtwdev->fw; + +wait_for_completion (&fw->completion); +if (!fw->firmware) +return -EINVAL ; + +return 0 ; +} + +static void rtw89_load_firmware_cb (const struct firmware *firmware,void *context) +{ +struct rtw89_fw_info *fw = context; +struct rtw89_dev *rtwdev = fw->rtwdev; + +if (!firmware || !firmware->data) { +rtw89_err (rtwdev,"failed to request firmware\n" ); +complete_all (&fw->completion); +return ; + } + + fw->firmware = firmware; +complete_all (&fw->completion); +} + +int rtw89_load_firmware (struct rtw89_dev *rtwdev) +{ +struct rtw89_fw_info *fw = &rtwdev->fw; +const char *fw_name = rtwdev->chip->fw_name; +int ret; + + fw->rtwdev = rtwdev; +init_completion (&fw->completion); + + ret =request_firmware_nowait (THIS_MODULE , true, fw_name, rtwdev->dev, +GFP_KERNEL , fw, rtw89_load_firmware_cb); +if (ret) { +rtw89_err (rtwdev,"failed to async firmware request\n" ); +return ret; + } + +return 0 ; +} + +void rtw89_unload_firmware (struct rtw89_dev *rtwdev) +{ +struct rtw89_fw_info *fw = &rtwdev->fw; + +rtw89_wait_firmware_completion (rtwdev); + +if (fw->firmware) +release_firmware (fw->firmware); +} + +#define H2C_CAM_LEN 60 +int rtw89_fw_h2c_cam (struct rtw89_dev *rtwdev,struct rtw89_vif *rtwvif) +{ +struct sk_buff *skb; + + skb =rtw89_fw_h2c_alloc_skb_with_hdr (H2C_CAM_LEN ); +if (!skb) { +rtw89_err (rtwdev,"failed to alloc skb for fw dl\n" ); +return -ENOMEM ; + } +skb_put (skb,H2C_CAM_LEN ); +rtw89_cam_fill_addr_cam_info (rtwdev, rtwvif, skb->data); +rtw89_cam_fill_bssid_cam_info (rtwdev, rtwvif, skb->data); + +rtw89_h2c_pkt_set_hdr (rtwdev, skb,FWCMD_TYPE_H2C , +H2C_CAT_MAC , +H2C_CL_MAC_ADDR_CAM_UPDATE , +H2C_FUNC_MAC_ADDR_CAM_UPD ,0 ,1 , +H2C_CAM_LEN ); + +if (rtw89_h2c_tx (rtwdev, skb, false)) { +rtw89_err (rtwdev,"failed to send h2c\n" ); + goto fail; + } + +return 0 ; +fail: +dev_kfree_skb_any (skb); + +return -EBUSY ; +} + +#define H2C_BA_CAM_LEN 4 +int rtw89_fw_h2c_ba_cam (struct rtw89_dev *rtwdev,bool valid,u8 macid, +struct ieee80211_ampdu_params *params) +{ +struct sk_buff *skb; + + skb =rtw89_fw_h2c_alloc_skb_with_hdr (H2C_BA_CAM_LEN ); +if (!skb) { +rtw89_err (rtwdev,"failed to alloc skb for h2c ba cam\n" ); +return -ENOMEM ; + } +skb_put (skb,H2C_BA_CAM_LEN ); +SET_BA_CAM_MACID (skb->data, macid); +if (!valid) + goto end; +SET_BA_CAM_VALID (skb->data, valid); +SET_BA_CAM_TID (skb->data, params->tid); +if (params->buf_size >64 ) +SET_BA_CAM_BMAP_SIZE (skb->data,4 ); +else +SET_BA_CAM_BMAP_SIZE (skb->data,0 ); +/* If init req is set, hw will set the ssn */ +SET_BA_CAM_INIT_REQ (skb->data,0 ); +SET_BA_CAM_SSN (skb->data, params->ssn); + +end: +rtw89_h2c_pkt_set_hdr (rtwdev, skb,FWCMD_TYPE_H2C , +H2C_CAT_MAC , +H2C_CL_BA_CAM , +H2C_FUNC_MAC_BA_CAM ,0 ,1 , +H2C_BA_CAM_LEN ); + +if (rtw89_h2c_tx (rtwdev, skb, false)) { +rtw89_err (rtwdev,"failed to send h2c\n" ); + goto fail; + } + +return 0 ; +fail: +dev_kfree_skb_any (skb); + +return -EBUSY ; +} + +#define H2C_LOG_CFG_LEN 12 +int rtw89_fw_h2c_fw_log (struct rtw89_dev *rtwdev,bool enable) +{ +struct sk_buff *skb; +u32 comp = enable ?BIT (RTW89_FW_LOG_COMP_INIT ) |BIT (RTW89_FW_LOG_COMP_TASK ) | +BIT (RTW89_FW_LOG_COMP_PS ) |BIT (RTW89_FW_LOG_COMP_ERROR ) :0 ; + + skb =rtw89_fw_h2c_alloc_skb_with_hdr (H2C_LOG_CFG_LEN ); +if (!skb) { +rtw89_err (rtwdev,"failed to alloc skb for fw log cfg\n" ); +return -ENOMEM ; + } + +skb_put (skb,H2C_LOG_CFG_LEN ); +SET_LOG_CFG_LEVEL (skb->data,RTW89_FW_LOG_LEVEL_SER ); +SET_LOG_CFG_PATH (skb->data,BIT (RTW89_FW_LOG_LEVEL_C2H )); +SET_LOG_CFG_COMP (skb->data, comp); +SET_LOG_CFG_COMP_EXT (skb->data,0 ); + +rtw89_h2c_pkt_set_hdr (rtwdev, skb,FWCMD_TYPE_H2C , +H2C_CAT_MAC , +H2C_CL_FW_INFO , +H2C_FUNC_LOG_CFG ,0 ,0 , +H2C_LOG_CFG_LEN ); + +if (rtw89_h2c_tx (rtwdev, skb, false)) { +rtw89_err (rtwdev,"failed to send h2c\n" ); + goto fail; + } + +return 0 ; +fail: +dev_kfree_skb_any (skb); + +return -EBUSY ; +} + +#define H2C_GENERAL_PKT_LEN 6 +#define H2C_GENERAL_PKT_ID_UND 0xff +int rtw89_fw_h2c_general_pkt (struct rtw89_dev *rtwdev,u8 macid) +{ +struct sk_buff *skb; + + skb =rtw89_fw_h2c_alloc_skb_with_hdr (H2C_GENERAL_PKT_LEN ); +if (!skb) { +rtw89_err (rtwdev,"failed to alloc skb for fw dl\n" ); +return -ENOMEM ; + } +skb_put (skb,H2C_GENERAL_PKT_LEN ); +SET_GENERAL_PKT_MACID (skb->data, macid); +SET_GENERAL_PKT_PROBRSP_ID (skb->data,H2C_GENERAL_PKT_ID_UND ); +SET_GENERAL_PKT_PSPOLL_ID (skb->data,H2C_GENERAL_PKT_ID_UND ); +SET_GENERAL_PKT_NULL_ID (skb->data,H2C_GENERAL_PKT_ID_UND ); +SET_GENERAL_PKT_QOS_NULL_ID (skb->data,H2C_GENERAL_PKT_ID_UND ); +SET_GENERAL_PKT_CTS2SELF_ID (skb->data,H2C_GENERAL_PKT_ID_UND ); + +rtw89_h2c_pkt_set_hdr (rtwdev, skb,FWCMD_TYPE_H2C , +H2C_CAT_MAC , +H2C_CL_FW_INFO , +H2C_FUNC_MAC_GENERAL_PKT ,0 ,1 , +H2C_GENERAL_PKT_LEN ); + +if (rtw89_h2c_tx (rtwdev, skb, false)) { +rtw89_err (rtwdev,"failed to send h2c\n" ); + goto fail; + } + +return 0 ; +fail: +dev_kfree_skb_any (skb); + +return -EBUSY ; +} + +#define H2C_LPS_PARM_LEN 8 +int rtw89_fw_h2c_lps_parm (struct rtw89_dev *rtwdev, +struct rtw89_lps_parm *lps_param) +{ +struct sk_buff *skb; + + skb =rtw89_fw_h2c_alloc_skb_with_hdr (H2C_LPS_PARM_LEN ); +if (!skb) { +rtw89_err (rtwdev,"failed to alloc skb for fw dl\n" ); +return -ENOMEM ; + } +skb_put (skb,H2C_LPS_PARM_LEN ); + +SET_LPS_PARM_MACID (skb->data, lps_param->macid); +SET_LPS_PARM_PSMODE (skb->data, lps_param->psmode); +SET_LPS_PARM_LASTRPWM (skb->data, lps_param->lastrpwm); +SET_LPS_PARM_RLBM (skb->data,1 ); +SET_LPS_PARM_SMARTPS (skb->data,1 ); +SET_LPS_PARM_AWAKEINTERVAL (skb->data,1 ); +SET_LPS_PARM_VOUAPSD (skb->data,0 ); +SET_LPS_PARM_VIUAPSD (skb->data,0 ); +SET_LPS_PARM_BEUAPSD (skb->data,0 ); +SET_LPS_PARM_BKUAPSD (skb->data,0 ); + +rtw89_h2c_pkt_set_hdr (rtwdev, skb,FWCMD_TYPE_H2C , +H2C_CAT_MAC , +H2C_CL_MAC_PS , +H2C_FUNC_MAC_LPS_PARM ,0 ,1 , +H2C_LPS_PARM_LEN ); + +if (rtw89_h2c_tx (rtwdev, skb, false)) { +rtw89_err (rtwdev,"failed to send h2c\n" ); + goto fail; + } + +return 0 ; +fail: +dev_kfree_skb_any (skb); + +return -EBUSY ; +} + +#define H2C_CMC_TBL_LEN 68 +int rtw89_fw_h2c_default_cmac_tbl (struct rtw89_dev *rtwdev,u8 macid) +{ +struct rtw89_hal *hal = &rtwdev->hal; +struct sk_buff *skb; +u8 ntx_path = hal->antenna_tx ? hal->antenna_tx :RF_B ; +u8 map_b = hal->antenna_tx ==RF_AB ?1 :0 ; + + skb =rtw89_fw_h2c_alloc_skb_with_hdr (H2C_CMC_TBL_LEN ); +if (!skb) { +rtw89_err (rtwdev,"failed to alloc skb for fw dl\n" ); +return -ENOMEM ; + } +skb_put (skb,H2C_CMC_TBL_LEN ); +SET_CTRL_INFO_MACID (skb->data, macid); +SET_CTRL_INFO_OPERATION (skb->data,1 ); +SET_CMC_TBL_TXPWR_MODE (skb->data,0 ); +SET_CMC_TBL_NTX_PATH_EN (skb->data, ntx_path); +SET_CMC_TBL_PATH_MAP_A (skb->data,0 ); +SET_CMC_TBL_PATH_MAP_B (skb->data, map_b); +SET_CMC_TBL_PATH_MAP_C (skb->data,0 ); +SET_CMC_TBL_PATH_MAP_D (skb->data,0 ); +SET_CMC_TBL_ANTSEL_A (skb->data,0 ); +SET_CMC_TBL_ANTSEL_B (skb->data,0 ); +SET_CMC_TBL_ANTSEL_C (skb->data,0 ); +SET_CMC_TBL_ANTSEL_D (skb->data,0 ); +SET_CMC_TBL_DOPPLER_CTRL (skb->data,0 ); +SET_CMC_TBL_TXPWR_TOLERENCE (skb->data,0 ); + +rtw89_h2c_pkt_set_hdr (rtwdev, skb,FWCMD_TYPE_H2C , +H2C_CAT_MAC ,H2C_CL_MAC_FR_EXCHG , +H2C_FUNC_MAC_CCTLINFO_UD ,0 ,1 , +H2C_CMC_TBL_LEN ); + +if (rtw89_h2c_tx (rtwdev, skb, false)) { +rtw89_err (rtwdev,"failed to send h2c\n" ); + goto fail; + } + +return 0 ; +fail: +dev_kfree_skb_any (skb); + +return -EBUSY ; +} + +static void __get_sta_he_pkt_padding (struct rtw89_dev *rtwdev, +struct ieee80211_sta *sta,u8 *pads) +{ +bool ppe_th; +u8 ppe16, ppe8; +u8 nss =min (sta->rx_nss, rtwdev->hal.tx_nss) -1 ; +u8 ppe_thres_hdr = sta->he_cap.ppe_thres[0 ]; +u8 ru_bitmap; +u8 n, idx, sh; +u16 ppe; +int i; + +if (!sta->he_cap.has_he) +return ; + + ppe_th =FIELD_GET (IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT , + sta->he_cap.he_cap_elem.phy_cap_info[6 ]); +if (!ppe_th) { +u8 pad; + + pad =FIELD_GET (IEEE80211_HE_PHY_CAP9_NOMIMAL_PKT_PADDING_MASK , + sta->he_cap.he_cap_elem.phy_cap_info[9 ]); + +for (i =0 ; i <RTW89_PPE_BW_NUM ; i++) + pads[i] = pad; + } + + ru_bitmap =FIELD_GET (IEEE80211_PPE_THRES_RU_INDEX_BITMASK_MASK , ppe_thres_hdr); + n =hweight8 (ru_bitmap); + n =7 + (n *IEEE80211_PPE_THRES_INFO_PPET_SIZE *2 ) * nss; + +for (i =0 ; i <RTW89_PPE_BW_NUM ; i++) { +if (!(ru_bitmap &BIT (i))) { + pads[i] =1 ; +continue ; + } + + idx = n >>3 ; + sh = n &7 ; + n +=IEEE80211_PPE_THRES_INFO_PPET_SIZE *2 ; + + ppe =le16_to_cpu (*((__le16 *)&sta->he_cap.ppe_thres[idx])); + ppe16 = (ppe >> sh) &IEEE80211_PPE_THRES_NSS_MASK ; + sh +=IEEE80211_PPE_THRES_INFO_PPET_SIZE ; + ppe8 = (ppe >> sh) &IEEE80211_PPE_THRES_NSS_MASK ; + +if (ppe16 !=7 && ppe8 ==7 ) + pads[i] =2 ; +else if (ppe8 !=7 ) + pads[i] =1 ; +else + pads[i] =0 ; + } +} + +int rtw89_fw_h2c_assoc_cmac_tbl (struct rtw89_dev *rtwdev, +struct ieee80211_vif *vif, +struct ieee80211_sta *sta) +{ +struct rtw89_sta *rtwsta = (struct rtw89_sta *)sta->drv_priv; +struct rtw89_vif *rtwvif = (struct rtw89_vif *)vif->drv_priv; +struct sk_buff *skb; +u8 pads[RTW89_PPE_BW_NUM ]; + +memset (pads,0 ,sizeof (pads)); +__get_sta_he_pkt_padding (rtwdev, sta, pads); + + skb =rtw89_fw_h2c_alloc_skb_with_hdr (H2C_CMC_TBL_LEN ); +if (!skb) { +rtw89_err (rtwdev,"failed to alloc skb for fw dl\n" ); +return -ENOMEM ; + } +skb_put (skb,H2C_CMC_TBL_LEN ); +SET_CTRL_INFO_MACID (skb->data, rtwsta->mac_id); +SET_CTRL_INFO_OPERATION (skb->data,1 ); +SET_CMC_TBL_DISRTSFB (skb->data,1 ); +SET_CMC_TBL_DISDATAFB (skb->data,1 ); +SET_CMC_TBL_RTS_TXCNT_LMT_SEL (skb->data,0 ); +SET_CMC_TBL_DATA_TXCNT_LMT_SEL (skb->data,0 ); +if (vif->type ==NL80211_IFTYPE_STATION ) +SET_CMC_TBL_ULDL (skb->data,1 ); +else +SET_CMC_TBL_ULDL (skb->data,0 ); +SET_CMC_TBL_MULTI_PORT_ID (skb->data, rtwvif->port); +SET_CMC_TBL_NOMINAL_PKT_PADDING (skb->data, pads[RTW89_CHANNEL_WIDTH_20 ]); +SET_CMC_TBL_NOMINAL_PKT_PADDING40 (skb->data, pads[RTW89_CHANNEL_WIDTH_40 ]); +SET_CMC_TBL_NOMINAL_PKT_PADDING80 (skb->data, pads[RTW89_CHANNEL_WIDTH_80 ]); +SET_CMC_TBL_BSR_QUEUE_SIZE_FORMAT (skb->data, sta->he_cap.has_he); + +rtw89_h2c_pkt_set_hdr (rtwdev, skb,FWCMD_TYPE_H2C , +H2C_CAT_MAC ,H2C_CL_MAC_FR_EXCHG , +H2C_FUNC_MAC_CCTLINFO_UD ,0 ,1 , +H2C_CMC_TBL_LEN ); + +if (rtw89_h2c_tx (rtwdev, skb, false)) { +rtw89_err (rtwdev,"failed to send h2c\n" ); + goto fail; + } + +return 0 ; +fail: +dev_kfree_skb_any (skb); + +return -EBUSY ; +} + +int rtw89_fw_h2c_txtime_cmac_tbl (struct rtw89_dev *rtwdev, +struct rtw89_sta *rtwsta) +{ +struct sk_buff *skb; + + skb =rtw89_fw_h2c_alloc_skb_with_hdr (H2C_CMC_TBL_LEN ); +if (!skb) { +rtw89_err (rtwdev,"failed to alloc skb for fw dl\n" ); +return -ENOMEM ; + } +skb_put (skb,H2C_CMC_TBL_LEN ); +SET_CTRL_INFO_MACID (skb->data, rtwsta->mac_id); +SET_CTRL_INFO_OPERATION (skb->data,1 ); +if (rtwsta->cctl_tx_time) { +SET_CMC_TBL_AMPDU_TIME_SEL (skb->data,1 ); +SET_CMC_TBL_AMPDU_MAX_TIME (skb->data, rtwsta->ampdu_max_time); + } +if (rtwsta->cctl_tx_retry_limit) { +SET_CMC_TBL_DATA_TXCNT_LMT_SEL (skb->data,1 ); +SET_CMC_TBL_DATA_TX_CNT_LMT (skb->data, rtwsta->data_tx_cnt_lmt); + } + +rtw89_h2c_pkt_set_hdr (rtwdev, skb,FWCMD_TYPE_H2C , +H2C_CAT_MAC ,H2C_CL_MAC_FR_EXCHG , +H2C_FUNC_MAC_CCTLINFO_UD ,0 ,1 , +H2C_CMC_TBL_LEN ); + +if (rtw89_h2c_tx (rtwdev, skb, false)) { +rtw89_err (rtwdev,"failed to send h2c\n" ); + goto fail; + } + +return 0 ; +fail: +dev_kfree_skb_any (skb); + +return -EBUSY ; +} + +#define H2C_VIF_MAINTAIN_LEN 4 +int rtw89_fw_h2c_vif_maintain (struct rtw89_dev *rtwdev, +struct rtw89_vif *rtwvif, +enum rtw89_upd_mode upd_mode) +{ +struct sk_buff *skb; + + skb =rtw89_fw_h2c_alloc_skb_with_hdr (H2C_VIF_MAINTAIN_LEN ); +if (!skb) { +rtw89_err (rtwdev,"failed to alloc skb for h2c join\n" ); +return -ENOMEM ; + } +skb_put (skb,H2C_VIF_MAINTAIN_LEN ); +SET_FWROLE_MAINTAIN_MACID (skb->data, rtwvif->mac_id); +SET_FWROLE_MAINTAIN_SELF_ROLE (skb->data, rtwvif->self_role); +SET_FWROLE_MAINTAIN_UPD_MODE (skb->data, upd_mode); +SET_FWROLE_MAINTAIN_WIFI_ROLE (skb->data, rtwvif->wifi_role); + +rtw89_h2c_pkt_set_hdr (rtwdev, skb,FWCMD_TYPE_H2C , +H2C_CAT_MAC ,H2C_CL_MAC_MEDIA_RPT , +H2C_FUNC_MAC_FWROLE_MAINTAIN ,0 ,1 , +H2C_VIF_MAINTAIN_LEN ); + +if (rtw89_h2c_tx (rtwdev, skb, false)) { +rtw89_err (rtwdev,"failed to send h2c\n" ); + goto fail; + } + +return 0 ; +fail: +dev_kfree_skb_any (skb); + +return -EBUSY ; +} + +#define H2C_JOIN_INFO_LEN 4 +int rtw89_fw_h2c_join_info (struct rtw89_dev *rtwdev,struct rtw89_vif *rtwvif, +u8 dis_conn) +{ +struct sk_buff *skb; + + skb =rtw89_fw_h2c_alloc_skb_with_hdr (H2C_JOIN_INFO_LEN ); +if (!skb) { +rtw89_err (rtwdev,"failed to alloc skb for h2c join\n" ); +return -ENOMEM ; + } +skb_put (skb,H2C_JOIN_INFO_LEN ); +SET_JOININFO_MACID (skb->data, rtwvif->mac_id); +SET_JOININFO_OP (skb->data, dis_conn); +SET_JOININFO_BAND (skb->data, rtwvif->mac_idx); +SET_JOININFO_WMM (skb->data, rtwvif->wmm); +SET_JOININFO_TGR (skb->data, rtwvif->trigger); +SET_JOININFO_ISHESTA (skb->data,0 ); +SET_JOININFO_DLBW (skb->data,0 ); +SET_JOININFO_TF_MAC_PAD (skb->data,0 ); +SET_JOININFO_DL_T_PE (skb->data,0 ); +SET_JOININFO_PORT_ID (skb->data, rtwvif->port); +SET_JOININFO_NET_TYPE (skb->data, rtwvif->net_type); +SET_JOININFO_WIFI_ROLE (skb->data, rtwvif->wifi_role); +SET_JOININFO_SELF_ROLE (skb->data, rtwvif->self_role); + +rtw89_h2c_pkt_set_hdr (rtwdev, skb,FWCMD_TYPE_H2C , +H2C_CAT_MAC ,H2C_CL_MAC_MEDIA_RPT , +H2C_FUNC_MAC_JOININFO ,0 ,1 , +H2C_JOIN_INFO_LEN ); + +if (rtw89_h2c_tx (rtwdev, skb, false)) { +rtw89_err (rtwdev,"failed to send h2c\n" ); + goto fail; + } + +return 0 ; +fail: +dev_kfree_skb_any (skb); + +return -EBUSY ; +} + +int rtw89_fw_h2c_macid_pause (struct rtw89_dev *rtwdev,u8 sh,u8 grp, +bool pause) +{ +struct rtw89_fw_macid_pause_grp h2c = {{0 }}; +u8 len =sizeof (struct rtw89_fw_macid_pause_grp ); +struct sk_buff *skb; + + skb =rtw89_fw_h2c_alloc_skb_with_hdr (H2C_JOIN_INFO_LEN ); +if (!skb) { +rtw89_err (rtwdev,"failed to alloc skb for h2c join\n" ); +return -ENOMEM ; + } + h2c.mask_grp[grp] =cpu_to_le32 (BIT (sh)); +if (pause) + h2c.pause_grp[grp] =cpu_to_le32 (BIT (sh)); +skb_put_data (skb, &h2c, len); + +rtw89_h2c_pkt_set_hdr (rtwdev, skb,FWCMD_TYPE_H2C , +H2C_CAT_MAC ,H2C_CL_MAC_FW_OFLD , +H2C_FUNC_MAC_MACID_PAUSE ,1 ,0 , + len); + +if (rtw89_h2c_tx (rtwdev, skb, false)) { +rtw89_err (rtwdev,"failed to send h2c\n" ); + goto fail; + } + +return 0 ; +fail: +dev_kfree_skb_any (skb); + +return -EBUSY ; +} + +#define H2C_EDCA_LEN 12 +int rtw89_fw_h2c_set_edca (struct rtw89_dev *rtwdev,struct rtw89_vif *rtwvif, +u8 ac,u32 val) +{ +struct sk_buff *skb; + + skb =rtw89_fw_h2c_alloc_skb_with_hdr (H2C_EDCA_LEN ); +if (!skb) { +rtw89_err (rtwdev,"failed to alloc skb for h2c edca\n" ); +return -ENOMEM ; + } +skb_put (skb,H2C_EDCA_LEN ); +RTW89_SET_EDCA_SEL (skb->data,0 ); +RTW89_SET_EDCA_BAND (skb->data, rtwvif->mac_idx); +RTW89_SET_EDCA_WMM (skb->data,0 ); +RTW89_SET_EDCA_AC (skb->data, ac); +RTW89_SET_EDCA_PARAM (skb->data, val); + +rtw89_h2c_pkt_set_hdr (rtwdev, skb,FWCMD_TYPE_H2C , +H2C_CAT_MAC ,H2C_CL_MAC_FW_OFLD , +H2C_FUNC_USR_EDCA ,0 ,1 , +H2C_EDCA_LEN ); + +if (rtw89_h2c_tx (rtwdev, skb, false)) { +rtw89_err (rtwdev,"failed to send h2c\n" ); + goto fail; + } + +return 0 ; +fail: +dev_kfree_skb_any (skb); + +return -EBUSY ; +} + +#define H2C_OFLD_CFG_LEN 8 +int rtw89_fw_h2c_set_ofld_cfg (struct rtw89_dev *rtwdev) +{ +static const u8 cfg[] = {0x09 ,0x00 ,0x00 ,0x00 ,0x5e ,0x00 ,0x00 ,0x00 }; +struct sk_buff *skb; + + skb =rtw89_fw_h2c_alloc_skb_with_hdr (H2C_OFLD_CFG_LEN ); +if (!skb) { +rtw89_err (rtwdev,"failed to alloc skb for h2c ofld\n" ); +return -ENOMEM ; + } +skb_put_data (skb, cfg,H2C_OFLD_CFG_LEN ); + +rtw89_h2c_pkt_set_hdr (rtwdev, skb,FWCMD_TYPE_H2C , +H2C_CAT_MAC ,H2C_CL_MAC_FW_OFLD , +H2C_FUNC_OFLD_CFG ,0 ,1 , +H2C_OFLD_CFG_LEN ); + +if (rtw89_h2c_tx (rtwdev, skb, false)) { +rtw89_err (rtwdev,"failed to send h2c\n" ); + goto fail; + } + +return 0 ; +fail: +dev_kfree_skb_any (skb); + +return -EBUSY ; +} + +#define H2C_RA_LEN 16 +int rtw89_fw_h2c_ra (struct rtw89_dev *rtwdev,struct rtw89_ra_info *ra,bool csi) +{ +struct sk_buff *skb; +u8 *cmd; + + skb =rtw89_fw_h2c_alloc_skb_with_hdr (H2C_RA_LEN ); +if (!skb) { +rtw89_err (rtwdev,"failed to alloc skb for h2c join\n" ); +return -ENOMEM ; + } +skb_put (skb,H2C_RA_LEN ); + cmd = skb->data; +rtw89_debug (rtwdev,RTW89_DBG_RA , +"ra cmd msk: %llx " , ra->ra_mask); + +RTW89_SET_FWCMD_RA_MODE (cmd, ra->mode_ctrl); +RTW89_SET_FWCMD_RA_BW_CAP (cmd, ra->bw_cap); +RTW89_SET_FWCMD_RA_MACID (cmd, ra->macid); +RTW89_SET_FWCMD_RA_DCM (cmd, ra->dcm_cap); +RTW89_SET_FWCMD_RA_ER (cmd, ra->er_cap); +RTW89_SET_FWCMD_RA_INIT_RATE_LV (cmd, ra->init_rate_lv); +RTW89_SET_FWCMD_RA_UPD_ALL (cmd, ra->upd_all); +RTW89_SET_FWCMD_RA_SGI (cmd, ra->en_sgi); +RTW89_SET_FWCMD_RA_LDPC (cmd, ra->ldpc_cap); +RTW89_SET_FWCMD_RA_STBC (cmd, ra->stbc_cap); +RTW89_SET_FWCMD_RA_SS_NUM (cmd, ra->ss_num); +RTW89_SET_FWCMD_RA_GILTF (cmd, ra->giltf); +RTW89_SET_FWCMD_RA_UPD_BW_NSS_MASK (cmd, ra->upd_bw_nss_mask); +RTW89_SET_FWCMD_RA_UPD_MASK (cmd, ra->upd_mask); +RTW89_SET_FWCMD_RA_MASK_0 (cmd,FIELD_GET (MASKBYTE0 , ra->ra_mask)); +RTW89_SET_FWCMD_RA_MASK_1 (cmd,FIELD_GET (MASKBYTE1 , ra->ra_mask)); +RTW89_SET_FWCMD_RA_MASK_2 (cmd,FIELD_GET (MASKBYTE2 , ra->ra_mask)); +RTW89_SET_FWCMD_RA_MASK_3 (cmd,FIELD_GET (MASKBYTE3 , ra->ra_mask)); +RTW89_SET_FWCMD_RA_MASK_4 (cmd,FIELD_GET (MASKBYTE4 , ra->ra_mask)); + +if (csi) { +RTW89_SET_FWCMD_RA_BFEE_CSI_CTL (cmd,1 ); +RTW89_SET_FWCMD_RA_BAND_NUM (cmd, ra->band_num); +RTW89_SET_FWCMD_RA_CR_TBL_SEL (cmd, ra->cr_tbl_sel); +RTW89_SET_FWCMD_RA_FIXED_CSI_RATE_EN (cmd, ra->fixed_csi_rate_en); +RTW89_SET_FWCMD_RA_RA_CSI_RATE_EN (cmd, ra->ra_csi_rate_en); +RTW89_SET_FWCMD_RA_FIXED_CSI_MCS_SS_IDX (cmd, ra->csi_mcs_ss_idx); +RTW89_SET_FWCMD_RA_FIXED_CSI_MODE (cmd, ra->csi_mode); +RTW89_SET_FWCMD_RA_FIXED_CSI_GI_LTF (cmd, ra->csi_gi_ltf); +RTW89_SET_FWCMD_RA_FIXED_CSI_BW (cmd, ra->csi_bw); + } + +rtw89_h2c_pkt_set_hdr (rtwdev, skb,FWCMD_TYPE_H2C , +H2C_CAT_OUTSRC ,H2C_CL_OUTSRC_RA , +H2C_FUNC_OUTSRC_RA_MACIDCFG ,0 ,0 , +H2C_RA_LEN ); + +if (rtw89_h2c_tx (rtwdev, skb, false)) { +rtw89_err (rtwdev,"failed to send h2c\n" ); + goto fail; + } + +return 0 ; +fail: +dev_kfree_skb_any (skb); + +return -EBUSY ; +} + +#define H2C_LEN_CXDRVHDR 2 +#define H2C_LEN_CXDRVINFO_INIT (12 + H2C_LEN_CXDRVHDR) +int rtw89_fw_h2c_cxdrv_init (struct rtw89_dev *rtwdev) +{ +struct rtw89_btc *btc = &rtwdev->btc; +struct rtw89_btc_dm *dm = &btc->dm; +struct rtw89_btc_init_info *init_info = &dm->init_info; +struct rtw89_btc_module *module = &init_info->module; +struct rtw89_btc_ant_info *ant = &module->ant; +struct sk_buff *skb; +u8 *cmd; + + skb =rtw89_fw_h2c_alloc_skb_with_hdr (H2C_LEN_CXDRVINFO_INIT ); +if (!skb) { +rtw89_err (rtwdev,"failed to alloc skb for h2c cxdrv_init\n" ); +return -ENOMEM ; + } +skb_put (skb,H2C_LEN_CXDRVINFO_INIT ); + cmd = skb->data; + +RTW89_SET_FWCMD_CXHDR_TYPE (cmd,CXDRVINFO_INIT ); +RTW89_SET_FWCMD_CXHDR_LEN (cmd,H2C_LEN_CXDRVINFO_INIT -H2C_LEN_CXDRVHDR ); + +RTW89_SET_FWCMD_CXINIT_ANT_TYPE (cmd, ant->type); +RTW89_SET_FWCMD_CXINIT_ANT_NUM (cmd, ant->num); +RTW89_SET_FWCMD_CXINIT_ANT_ISO (cmd, ant->isolation); +RTW89_SET_FWCMD_CXINIT_ANT_POS (cmd, ant->single_pos); +RTW89_SET_FWCMD_CXINIT_ANT_DIVERSITY (cmd, ant->diversity); + +RTW89_SET_FWCMD_CXINIT_MOD_RFE (cmd, module->rfe_type); +RTW89_SET_FWCMD_CXINIT_MOD_CV (cmd, module->cv); +RTW89_SET_FWCMD_CXINIT_MOD_BT_SOLO (cmd, module->bt_solo); +RTW89_SET_FWCMD_CXINIT_MOD_BT_POS (cmd, module->bt_pos); +RTW89_SET_FWCMD_CXINIT_MOD_SW_TYPE (cmd, module->switch_type); + +RTW89_SET_FWCMD_CXINIT_WL_GCH (cmd, init_info->wl_guard_ch); +RTW89_SET_FWCMD_CXINIT_WL_ONLY (cmd, init_info->wl_only); +RTW89_SET_FWCMD_CXINIT_WL_INITOK (cmd, init_info->wl_init_ok); +RTW89_SET_FWCMD_CXINIT_DBCC_EN (cmd, init_info->dbcc_en); +RTW89_SET_FWCMD_CXINIT_CX_OTHER (cmd, init_info->cx_other); +RTW89_SET_FWCMD_CXINIT_BT_ONLY (cmd, init_info->bt_only); + +rtw89_h2c_pkt_set_hdr (rtwdev, skb,FWCMD_TYPE_H2C , +H2C_CAT_OUTSRC ,BTFC_SET , +SET_DRV_INFO ,0 ,0 , +H2C_LEN_CXDRVINFO_INIT ); + +if (rtw89_h2c_tx (rtwdev, skb, false)) { +rtw89_err (rtwdev,"failed to send h2c\n" ); + goto fail; + } + +return 0 ; +fail: +dev_kfree_skb_any (skb); + +return -EBUSY ; +} + +#define H2C_LEN_CXDRVINFO_ROLE (4 + 12 * RTW89_MAX_HW_PORT_NUM + H2C_LEN_CXDRVHDR) +int rtw89_fw_h2c_cxdrv_role (struct rtw89_dev *rtwdev) +{ +struct rtw89_btc *btc = &rtwdev->btc; +struct rtw89_btc_wl_info *wl = &btc->cx.wl; +struct rtw89_btc_wl_role_info *role_info = &wl->role_info; +struct rtw89_btc_wl_role_info_bpos *bpos = &role_info->role_map.role; +struct rtw89_btc_wl_active_role *active = role_info->active_role; +struct sk_buff *skb; +u8 *cmd; +int i; + + skb =rtw89_fw_h2c_alloc_skb_with_hdr (H2C_LEN_CXDRVINFO_ROLE ); +if (!skb) { +rtw89_err (rtwdev,"failed to alloc skb for h2c cxdrv_role\n" ); +return -ENOMEM ; + } +skb_put (skb,H2C_LEN_CXDRVINFO_ROLE ); + cmd = skb->data; + +RTW89_SET_FWCMD_CXHDR_TYPE (cmd,CXDRVINFO_ROLE ); +RTW89_SET_FWCMD_CXHDR_LEN (cmd,H2C_LEN_CXDRVINFO_ROLE -H2C_LEN_CXDRVHDR ); + +RTW89_SET_FWCMD_CXROLE_CONNECT_CNT (cmd, role_info->connect_cnt); +RTW89_SET_FWCMD_CXROLE_LINK_MODE (cmd, role_info->link_mode); + +RTW89_SET_FWCMD_CXROLE_ROLE_NONE (cmd, bpos->none); +RTW89_SET_FWCMD_CXROLE_ROLE_STA (cmd, bpos->station); +RTW89_SET_FWCMD_CXROLE_ROLE_AP (cmd, bpos->ap); +RTW89_SET_FWCMD_CXROLE_ROLE_VAP (cmd, bpos->vap); +RTW89_SET_FWCMD_CXROLE_ROLE_ADHOC (cmd, bpos->adhoc); +RTW89_SET_FWCMD_CXROLE_ROLE_ADHOC_MASTER (cmd, bpos->adhoc_master); +RTW89_SET_FWCMD_CXROLE_ROLE_MESH (cmd, bpos->mesh); +RTW89_SET_FWCMD_CXROLE_ROLE_MONITOR (cmd, bpos->moniter); +RTW89_SET_FWCMD_CXROLE_ROLE_P2P_DEV (cmd, bpos->p2p_device); +RTW89_SET_FWCMD_CXROLE_ROLE_P2P_GC (cmd, bpos->p2p_gc); +RTW89_SET_FWCMD_CXROLE_ROLE_P2P_GO (cmd, bpos->p2p_go); +RTW89_SET_FWCMD_CXROLE_ROLE_NAN (cmd, bpos->nan); + +for (i =0 ; i <RTW89_MAX_HW_PORT_NUM ; i++, active++) { +RTW89_SET_FWCMD_CXROLE_ACT_CONNECTED (cmd, active->connected, i); +RTW89_SET_FWCMD_CXROLE_ACT_PID (cmd, active->pid, i); +RTW89_SET_FWCMD_CXROLE_ACT_PHY (cmd, active->phy, i); +RTW89_SET_FWCMD_CXROLE_ACT_NOA (cmd, active->noa, i); +RTW89_SET_FWCMD_CXROLE_ACT_BAND (cmd, active->band, i); +RTW89_SET_FWCMD_CXROLE_ACT_CLIENT_PS (cmd, active->client_ps, i); +RTW89_SET_FWCMD_CXROLE_ACT_BW (cmd, active->bw, i); +RTW89_SET_FWCMD_CXROLE_ACT_ROLE (cmd, active->role, i); +RTW89_SET_FWCMD_CXROLE_ACT_CH (cmd, active->ch, i); +RTW89_SET_FWCMD_CXROLE_ACT_TX_LVL (cmd, active->tx_lvl, i); +RTW89_SET_FWCMD_CXROLE_ACT_RX_LVL (cmd, active->rx_lvl, i); +RTW89_SET_FWCMD_CXROLE_ACT_TX_RATE (cmd, active->tx_rate, i); +RTW89_SET_FWCMD_CXROLE_ACT_RX_RATE (cmd, active->rx_rate, i); + } + +rtw89_h2c_pkt_set_hdr (rtwdev, skb,FWCMD_TYPE_H2C , +H2C_CAT_OUTSRC ,BTFC_SET , +SET_DRV_INFO ,0 ,0 , +H2C_LEN_CXDRVINFO_ROLE ); + +if (rtw89_h2c_tx (rtwdev, skb, false)) { +rtw89_err (rtwdev,"failed to send h2c\n" ); + goto fail; + } + +return 0 ; +fail: +dev_kfree_skb_any (skb); + +return -EBUSY ; +} + +#define H2C_LEN_CXDRVINFO_CTRL (4 + H2C_LEN_CXDRVHDR) +int rtw89_fw_h2c_cxdrv_ctrl (struct rtw89_dev *rtwdev) +{ +struct rtw89_btc *btc = &rtwdev->btc; +struct rtw89_btc_ctrl *ctrl = &btc->ctrl; +struct sk_buff *skb; +u8 *cmd; + + skb =rtw89_fw_h2c_alloc_skb_with_hdr (H2C_LEN_CXDRVINFO_CTRL ); +if (!skb) { +rtw89_err (rtwdev,"failed to alloc skb for h2c cxdrv_ctrl\n" ); +return -ENOMEM ; + } +skb_put (skb,H2C_LEN_CXDRVINFO_CTRL ); + cmd = skb->data; + +RTW89_SET_FWCMD_CXHDR_TYPE (cmd,CXDRVINFO_CTRL ); +RTW89_SET_FWCMD_CXHDR_LEN (cmd,H2C_LEN_CXDRVINFO_CTRL -H2C_LEN_CXDRVHDR ); + +RTW89_SET_FWCMD_CXCTRL_MANUAL (cmd, ctrl->manual); +RTW89_SET_FWCMD_CXCTRL_IGNORE_BT (cmd, ctrl->igno_bt); +RTW89_SET_FWCMD_CXCTRL_ALWAYS_FREERUN (cmd, ctrl->always_freerun); +RTW89_SET_FWCMD_CXCTRL_TRACE_STEP (cmd, ctrl->trace_step); + +rtw89_h2c_pkt_set_hdr (rtwdev, skb,FWCMD_TYPE_H2C , +H2C_CAT_OUTSRC ,BTFC_SET , +SET_DRV_INFO ,0 ,0 , +H2C_LEN_CXDRVINFO_CTRL ); + +if (rtw89_h2c_tx (rtwdev, skb, false)) { +rtw89_err (rtwdev,"failed to send h2c\n" ); + goto fail; + } + +return 0 ; +fail: +dev_kfree_skb_any (skb); + +return -EBUSY ; +} + +#define H2C_LEN_CXDRVINFO_RFK (4 + H2C_LEN_CXDRVHDR) +int rtw89_fw_h2c_cxdrv_rfk (struct rtw89_dev *rtwdev) +{ +struct rtw89_btc *btc = &rtwdev->btc; +struct rtw89_btc_wl_info *wl = &btc->cx.wl; +struct rtw89_btc_wl_rfk_info *rfk_info = &wl->rfk_info; +struct sk_buff *skb; +u8 *cmd; + + skb =rtw89_fw_h2c_alloc_skb_with_hdr (H2C_LEN_CXDRVINFO_RFK ); +if (!skb) { +rtw89_err (rtwdev,"failed to alloc skb for h2c cxdrv_ctrl\n" ); +return -ENOMEM ; + } +skb_put (skb,H2C_LEN_CXDRVINFO_RFK ); + cmd = skb->data; + +RTW89_SET_FWCMD_CXHDR_TYPE (cmd,CXDRVINFO_RFK ); +RTW89_SET_FWCMD_CXHDR_LEN (cmd,H2C_LEN_CXDRVINFO_RFK -H2C_LEN_CXDRVHDR ); + +RTW89_SET_FWCMD_CXRFK_STATE (cmd, rfk_info->state); +RTW89_SET_FWCMD_CXRFK_PATH_MAP (cmd, rfk_info->path_map); +RTW89_SET_FWCMD_CXRFK_PHY_MAP (cmd, rfk_info->phy_map); +RTW89_SET_FWCMD_CXRFK_BAND (cmd, rfk_info->band); +RTW89_SET_FWCMD_CXRFK_TYPE (cmd, rfk_info->type); + +rtw89_h2c_pkt_set_hdr (rtwdev, skb,FWCMD_TYPE_H2C , +H2C_CAT_OUTSRC ,BTFC_SET , +SET_DRV_INFO ,0 ,0 , +H2C_LEN_CXDRVINFO_RFK ); + +if (rtw89_h2c_tx (rtwdev, skb, false)) { +rtw89_err (rtwdev,"failed to send h2c\n" ); + goto fail; + } + +return 0 ; +fail: +dev_kfree_skb_any (skb); + +return -EBUSY ; +} + +int rtw89_fw_h2c_rf_reg (struct rtw89_dev *rtwdev, +struct rtw89_fw_h2c_rf_reg_info *info, +u16 len,u8 page) +{ +struct sk_buff *skb; +u8 class = info->rf_path ==RF_PATH_A ? +H2C_CL_OUTSRC_RF_REG_A :H2C_CL_OUTSRC_RF_REG_B ; + + skb =rtw89_fw_h2c_alloc_skb_with_hdr (len); +if (!skb) { +rtw89_err (rtwdev,"failed to alloc skb for h2c rf reg\n" ); +return -ENOMEM ; + } +skb_put_data (skb, info->rtw89_phy_config_rf_h2c[page], len); + +rtw89_h2c_pkt_set_hdr (rtwdev, skb,FWCMD_TYPE_H2C , +H2C_CAT_OUTSRC , class, page,0 ,0 , + len); + +if (rtw89_h2c_tx (rtwdev, skb, false)) { +rtw89_err (rtwdev,"failed to send h2c\n" ); + goto fail; + } + +return 0 ; +fail: +dev_kfree_skb_any (skb); + +return -EBUSY ; +} + +int rtw89_fw_h2c_raw_with_hdr (struct rtw89_dev *rtwdev, +u8 h2c_class,u8 h2c_func,u8 *buf,u16 len, +bool rack,bool dack) +{ +struct sk_buff *skb; + + skb =rtw89_fw_h2c_alloc_skb_with_hdr (len); +if (!skb) { +rtw89_err (rtwdev,"failed to alloc skb for raw with hdr\n" ); +return -ENOMEM ; + } +skb_put_data (skb, buf, len); + +rtw89_h2c_pkt_set_hdr (rtwdev, skb,FWCMD_TYPE_H2C , +H2C_CAT_OUTSRC , h2c_class, h2c_func, rack, dack, + len); + +if (rtw89_h2c_tx (rtwdev, skb, false)) { +rtw89_err (rtwdev,"failed to send h2c\n" ); + goto fail; + } + +return 0 ; +fail: +dev_kfree_skb_any (skb); + +return -EBUSY ; +} + +int rtw89_fw_h2c_raw (struct rtw89_dev *rtwdev,const u8 *buf,u16 len) +{ +struct sk_buff *skb; + + skb =rtw89_fw_h2c_alloc_skb_no_hdr (len); +if (!skb) { +rtw89_err (rtwdev,"failed to alloc skb for h2c raw\n" ); +return -ENOMEM ; + } +skb_put_data (skb, buf, len); + +if (rtw89_h2c_tx (rtwdev, skb, false)) { +rtw89_err (rtwdev,"failed to send h2c\n" ); + goto fail; + } + +return 0 ; +fail: +dev_kfree_skb_any (skb); + +return -EBUSY ; +} + +void rtw89_fw_c2h_irqsafe (struct rtw89_dev *rtwdev,struct sk_buff *c2h) +{ +skb_queue_tail (&rtwdev->c2h_queue, c2h); +ieee80211_queue_work (rtwdev->hw, &rtwdev->c2h_work); +} + +static void rtw89_fw_c2h_cmd_handle (struct rtw89_dev *rtwdev, +struct sk_buff *skb) +{ +u8 category =RTW89_GET_C2H_CATEGORY (skb->data); +u8 class =RTW89_GET_C2H_CLASS (skb->data); +u8 func =RTW89_GET_C2H_FUNC (skb->data); +u16 len =RTW89_GET_C2H_LEN (skb->data); +bool dump = true; + +if (!test_bit (RTW89_FLAG_RUNNING , rtwdev->flags)) +return ; + +switch (category) { +case RTW89_C2H_CAT_TEST : +break ; +case RTW89_C2H_CAT_MAC : +rtw89_mac_c2h_handle (rtwdev, skb, len, class, func); +if (class ==RTW89_MAC_C2H_CLASS_INFO && + func ==RTW89_MAC_C2H_FUNC_C2H_LOG ) + dump = false; +break ; +case RTW89_C2H_CAT_OUTSRC : +if (class >=RTW89_PHY_C2H_CLASS_BTC_MIN && + class <=RTW89_PHY_C2H_CLASS_BTC_MAX ) +rtw89_btc_c2h_handle (rtwdev, skb, len, class, func); +else +rtw89_phy_c2h_handle (rtwdev, skb, len, class, func); +break ; + } + +if (dump) +rtw89_hex_dump (rtwdev,RTW89_DBG_FW ,"C2H: " , skb->data, skb->len); +} + +void rtw89_fw_c2h_work (struct work_struct *work) +{ + struct rtw89_dev *rtwdev = container_of(work, struct rtw89_dev, + c2h_work); +struct sk_buff *skb, *tmp; + + skb_queue_walk_safe(&rtwdev->c2h_queue, skb, tmp) { +skb_unlink (skb, &rtwdev->c2h_queue); +mutex_lock (&rtwdev->mutex); +rtw89_fw_c2h_cmd_handle (rtwdev, skb); +mutex_unlock (&rtwdev->mutex); +dev_kfree_skb_any (skb); + } +} + +static int rtw89_fw_write_h2c_reg (struct rtw89_dev *rtwdev, +struct rtw89_mac_h2c_info *info) +{ +static const u32 h2c_reg[RTW89_H2CREG_MAX ] = { +R_AX_H2CREG_DATA0 ,R_AX_H2CREG_DATA1 , +R_AX_H2CREG_DATA2 ,R_AX_H2CREG_DATA3 + }; +u8 i, val, len; +int ret; + + ret =read_poll_timeout (rtw89_read8, val, val ==0 ,1000 ,5000 , false, + rtwdev,R_AX_H2CREG_CTRL ); +if (ret) { +rtw89_warn (rtwdev,"FW does not process h2c registers\n" ); +return ret; + } + + len =DIV_ROUND_UP (info->content_len +RTW89_H2CREG_HDR_LEN , +sizeof (info->h2creg[0 ])); + +RTW89_SET_H2CREG_HDR_FUNC (&info->h2creg[0 ], info->id); +RTW89_SET_H2CREG_HDR_LEN (&info->h2creg[0 ], len); +for (i =0 ; i <RTW89_H2CREG_MAX ; i++) +rtw89_write32 (rtwdev, h2c_reg[i], info->h2creg[i]); + +rtw89_write8 (rtwdev,R_AX_H2CREG_CTRL ,B_AX_H2CREG_TRIGGER ); + +return 0 ; +} + +static int rtw89_fw_read_c2h_reg (struct rtw89_dev *rtwdev, +struct rtw89_mac_c2h_info *info) +{ +static const u32 c2h_reg[RTW89_C2HREG_MAX ] = { +R_AX_C2HREG_DATA0 ,R_AX_C2HREG_DATA1 , +R_AX_C2HREG_DATA2 ,R_AX_C2HREG_DATA3 + }; +u32 ret; +u8 i, val; + + info->id =RTW89_FWCMD_C2HREG_FUNC_NULL ; + + ret =read_poll_timeout_atomic (rtw89_read8, val, val,1 , +RTW89_C2H_TIMEOUT , false, rtwdev, +R_AX_C2HREG_CTRL ); +if (ret) { +rtw89_warn (rtwdev,"c2h reg timeout\n" ); +return ret; + } + +for (i =0 ; i <RTW89_C2HREG_MAX ; i++) + info->c2hreg[i] =rtw89_read32 (rtwdev, c2h_reg[i]); + +rtw89_write8 (rtwdev,R_AX_C2HREG_CTRL ,0 ); + + info->id =RTW89_GET_C2H_HDR_FUNC (*info->c2hreg); + info->content_len = (RTW89_GET_C2H_HDR_LEN (*info->c2hreg) <<2 ) - +RTW89_C2HREG_HDR_LEN ; + +return 0 ; +} + +int rtw89_fw_msg_reg (struct rtw89_dev *rtwdev, +struct rtw89_mac_h2c_info *h2c_info, +struct rtw89_mac_c2h_info *c2h_info) +{ +u32 ret; + +if (h2c_info && h2c_info->id !=RTW89_FWCMD_H2CREG_FUNC_GET_FEATURE ) +lockdep_assert_held (&rtwdev->mutex); + +if (!h2c_info && !c2h_info) +return -EINVAL ; + +if (!h2c_info) + goto recv_c2h; + + ret =rtw89_fw_write_h2c_reg (rtwdev, h2c_info); +if (ret) +return ret; + +recv_c2h: +if (!c2h_info) +return 0 ; + + ret =rtw89_fw_read_c2h_reg (rtwdev, c2h_info); +if (ret) +return ret; + +return 0 ; +} + +void rtw89_fw_st_dbg_dump (struct rtw89_dev *rtwdev) +{ +if (!test_bit (RTW89_FLAG_POWERON , rtwdev->flags)) { +rtw89_err (rtwdev,"[ERR]pwr is off\n" ); +return ; + } + +rtw89_info (rtwdev,"FW status = 0x%x\n" ,rtw89_read32 (rtwdev,R_AX_UDM0 )); +rtw89_info (rtwdev,"FW BADADDR = 0x%x\n" ,rtw89_read32 (rtwdev,R_AX_UDM1 )); +rtw89_info (rtwdev,"FW EPC/RA = 0x%x\n" ,rtw89_read32 (rtwdev,R_AX_UDM2 )); +rtw89_info (rtwdev,"FW MISC = 0x%x\n" ,rtw89_read32 (rtwdev,R_AX_UDM3 )); +rtw89_info (rtwdev,"R_AX_HALT_C2H = 0x%x\n" , +rtw89_read32 (rtwdev,R_AX_HALT_C2H )); +rtw89_info (rtwdev,"R_AX_SER_DBG_INFO = 0x%x\n" , +rtw89_read32 (rtwdev,R_AX_SER_DBG_INFO )); + +rtw89_fw_prog_cnt_dump (rtwdev); +}
drivers/net/wireless/realtek/rtw89/fw.h new file +1394 -0
1,394 lines not on this page · raw message
Signature · 1 line
2.25.1
Re: [PATCH v6 06/24] rtw89: add files to download and communicate with firmware
Quoted 21 lines · Ping-Ke Shih
The firmware must be downloaded right after HCI basic initialization, and then we can obtain hardware capabilities that are used to do mac80211 register hw.
To download firmware, we need to parse the header to know how many sections the firmware has, and then download each section to proper location.
We introduce H2C and C2H to do bi-direction communication with firmware, and both support packet-based and register-based methods. Normally, we use packet-based H2C/C2H, because it has no number and size limits. In contrast, register-based H2C/C2H has only one message and fix-four-dword in size.
Header size of packet-based H2C/C2H is eight bytes (two dwords), which uses a hierarchical IDs, containing type, category, class and function, to classify a H2C or C2H command.
When a C2H is received in interrupt context, we don't process it right there, but queue the skb and wake up a ieee80211 work to handle the skb.
Signed-off-by Ping-Ke Shih <[email protected]>
[...]
Quoted 20 lines
drivers/net/wireless/realtek/rtw89/fw.h new file +13
-0+static __always_inline void RTW89_SET_FWCMD(u8 *cmd, u32 val, u8 offset, u32 mask) +{ +u32 *cmd32 = (u32 *)cmd; + +le32p_replace_bits ((__le32 *)(cmd32 + offset), val, mask); +} + +static __always_inline void RTW89_SET_FWCMD_UA8(u8 *cmd, u8 val, u8 offset, u32 mask) +{ +u8p_replace_bits (cmd + offset, val, mask); +} + +static __always_inline void RTW89_SET_FWCMD_UA16(u8 *cmd, u16 val, u8offset, u32 mask)
drivers/net/wireless/realtek/rtw89/fw.h new file +5
-0+{ +le16p_replace_bits ((__le16 *)(cmd + offset), val, mask); +} + +static __always_inline void RTW89_SET_FWCMD_UA32(u8 *cmd, u32 val, u8offset, u32 mask)
drivers/net/wireless/realtek/rtw89/fw.h line 1392
Quoted 3 lines
drivers/net/wireless/realtek/rtw89/fw.h new file +3
-0+{ +le32p_replace_bits ((__le32 *)(cmd + offset), val, mask); +}
Is __always_inline really necessary? I would say just use the regular inline keyword and let the compiler optimise as it sees fit.
Signature · 3 lines
https://patchwork.kernel.org/project/linux-wireless/list/ https://wireless.wiki.kernel.org/en/developers/documentation/submittingpatches
Ping-Ke Shih
maintainerPATCH v6 7/24 rtw89: add MAC files
In reply to Ping-Ke ShihProvide interfaces to access MAC function blocks including power-on/off configuration.
MAC initialization does power-on, and then enable HCI to download firmware. The BB, RF, DMAC and CMAC function blocks are enabled, and then configure TXRX settings that contains DMAC, CMAC, interrupt mask, and RPQ parameters. DMAC is short for data MAC, including data link engine (DLE), HCI function control (HFC), MPDU processor, security engine and so on. CMAC is short for control MAC, including scheduler, address CAM, RX filter, CCA control, TMAC, RMAC, protocol component and so on.
The full MAC initialization is done for normal use case when user does network interface up. When device is probing, driver does partial initialization to do power-on and download firmware, because we need to read hardware capabilities from efuse and firmware.
MAC supports five ports, so we can have five VIFs at most. To control MAC port, we control a set of registers, and the most important one among them is port_cfg register named R_AX_PORT_CFG_P0. We can turn on/off the port function and configure network type (STA or AP mode) by this register. The address and BSSID corresponding to this port are given by address and BSSID CAM that is set by firmware via H2C command.
Since BT coexistence code needs to access coex registers, some help functions are provided to make WiFi-only case work.
To access MAC registers, normally we use rtw89_writeXX/rtw89_readYY, but we use rtw89_mac_txpwr_write32_ZZ to access TX power register with proper address range checking.
--- drivers/net/wireless/realtek/rtw89/mac.c | 3833 ++++++++++++++++++++++ drivers/net/wireless/realtek/rtw89/mac.h | 860 +++++ 2 files changed, 4693 insertions(+) create mode 100644 drivers/net/wireless/realtek/rtw89/mac.c create mode 100644 drivers/net/wireless/realtek/rtw89/mac.h
drivers/net/wireless/realtek/rtw89/mac.c new file +3833 -0
@@ -0,0 +1,3833 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* Copyright(c) 2019-2020 Realtek Corporation +*/ + +#include "cam.h" +#include "debug.h" +#include "fw.h" +#include "mac.h" +#include "ps.h" +#include "reg.h" +#include "util.h" + +int rtw89_mac_check_mac_en (struct rtw89_dev *rtwdev,u8 mac_idx, +enum rtw89_mac_hwmod_sel sel) +{ +u32 val, r_val; + +if (sel ==RTW89_DMAC_SEL ) { + r_val =rtw89_read32 (rtwdev,R_AX_DMAC_FUNC_EN ); + val = (B_AX_MAC_FUNC_EN |B_AX_DMAC_FUNC_EN ); + }else if (sel ==RTW89_CMAC_SEL && mac_idx ==0 ) { + r_val =rtw89_read32 (rtwdev,R_AX_CMAC_FUNC_EN ); + val =B_AX_CMAC_EN ; + }else if (sel ==RTW89_CMAC_SEL && mac_idx ==1 ) { + r_val =rtw89_read32 (rtwdev,R_AX_SYS_ISO_CTRL_EXTEND ); + val =B_AX_CMAC1_FEN ; + }else { +return -EINVAL ; + } +if (r_val ==RTW89_R32_EA || r_val ==RTW89_R32_DEAD || + (val & r_val) != val) +return -EFAULT ; + +return 0 ; +} + +int rtw89_mac_write_lte (struct rtw89_dev *rtwdev,const u32 offset,u32 val) +{ +u8 lte_ctrl; +int ret; + + ret =read_poll_timeout (rtw89_read8, lte_ctrl, (lte_ctrl &BIT (5 )) !=0 , +50 ,50000 , false, rtwdev,R_AX_LTE_CTRL +3 ); +if (ret) +rtw89_err (rtwdev,"[ERR]lte not ready(W)\n" ); + +rtw89_write32 (rtwdev,R_AX_LTE_WDATA , val); +rtw89_write32 (rtwdev,R_AX_LTE_CTRL ,0xC00F0000 | offset); + +return ret; +} + +int rtw89_mac_read_lte (struct rtw89_dev *rtwdev,const u32 offset,u32 *val) +{ +u8 lte_ctrl; +int ret; + + ret =read_poll_timeout (rtw89_read8, lte_ctrl, (lte_ctrl &BIT (5 )) !=0 , +50 ,50000 , false, rtwdev,R_AX_LTE_CTRL +3 ); +if (ret) +rtw89_err (rtwdev,"[ERR]lte not ready(W)\n" ); + +rtw89_write32 (rtwdev,R_AX_LTE_CTRL ,0x800F0000 | offset); + *val =rtw89_read32 (rtwdev,R_AX_LTE_RDATA ); + +return ret; +} + +static +int dle_dfi_ctrl (struct rtw89_dev *rtwdev,struct rtw89_mac_dle_dfi_ctrl *ctrl) +{ +u32 ctrl_reg, data_reg, ctrl_data; +u32 val; +int ret; + +switch (ctrl->type) { +case DLE_CTRL_TYPE_WDE : + ctrl_reg =R_AX_WDE_DBG_FUN_INTF_CTL ; + data_reg =R_AX_WDE_DBG_FUN_INTF_DATA ; + ctrl_data =FIELD_PREP (B_AX_WDE_DFI_TRGSEL_MASK , ctrl->target) | +FIELD_PREP (B_AX_WDE_DFI_ADDR_MASK , ctrl->addr) | +B_AX_WDE_DFI_ACTIVE ; +break ; +case DLE_CTRL_TYPE_PLE : + ctrl_reg =R_AX_PLE_DBG_FUN_INTF_CTL ; + data_reg =R_AX_PLE_DBG_FUN_INTF_DATA ; + ctrl_data =FIELD_PREP (B_AX_PLE_DFI_TRGSEL_MASK , ctrl->target) | +FIELD_PREP (B_AX_PLE_DFI_ADDR_MASK , ctrl->addr) | +B_AX_PLE_DFI_ACTIVE ; +break ; +default : +rtw89_warn (rtwdev,"[ERR] dfi ctrl type %d\n" , ctrl->type); +return -EINVAL ; + } + +rtw89_write32 (rtwdev, ctrl_reg, ctrl_data); + + ret =read_poll_timeout_atomic (rtw89_read32, val, !(val &B_AX_WDE_DFI_ACTIVE ), +1 ,1000 , false, rtwdev, ctrl_reg); +if (ret) { +rtw89_warn (rtwdev,"[ERR] dle dfi ctrl 0x%X set 0x%X timeout\n" , + ctrl_reg, ctrl_data); +return ret; + } + + ctrl->out_data =rtw89_read32 (rtwdev, data_reg); +return 0 ; +} + +static int dle_dfi_quota (struct rtw89_dev *rtwdev, +struct rtw89_mac_dle_dfi_quota *quota) +{ +struct rtw89_mac_dle_dfi_ctrl ctrl; +int ret; + + ctrl.type = quota->dle_type; + ctrl.target =DLE_DFI_TYPE_QUOTA ; + ctrl.addr = quota->qtaid; + ret =dle_dfi_ctrl (rtwdev, &ctrl); +if (ret) { +rtw89_warn (rtwdev,"[ERR]dle_dfi_ctrl %d\n" , ret); +return ret; + } + + quota->rsv_pgnum =FIELD_GET (B_AX_DLE_RSV_PGNUM , ctrl.out_data); + quota->use_pgnum =FIELD_GET (B_AX_DLE_USE_PGNUM , ctrl.out_data); +return 0 ; +} + +static int dle_dfi_qempty (struct rtw89_dev *rtwdev, +struct rtw89_mac_dle_dfi_qempty *qempty) +{ +struct rtw89_mac_dle_dfi_ctrl ctrl; +u32 ret; + + ctrl.type = qempty->dle_type; + ctrl.target =DLE_DFI_TYPE_QEMPTY ; + ctrl.addr = qempty->grpsel; + ret =dle_dfi_ctrl (rtwdev, &ctrl); +if (ret) { +rtw89_warn (rtwdev,"[ERR]dle_dfi_ctrl %d\n" , ret); +return ret; + } + + qempty->qempty =FIELD_GET (B_AX_DLE_QEMPTY_GRP , ctrl.out_data); +return 0 ; +} + +static void dump_err_status_dispatcher (struct rtw89_dev *rtwdev) +{ +rtw89_info (rtwdev,"R_AX_HOST_DISPATCHER_ALWAYS_IMR=0x%08x " , +rtw89_read32 (rtwdev,R_AX_HOST_DISPATCHER_ERR_IMR )); +rtw89_info (rtwdev,"R_AX_HOST_DISPATCHER_ALWAYS_ISR=0x%08x\n" , +rtw89_read32 (rtwdev,R_AX_HOST_DISPATCHER_ERR_ISR )); +rtw89_info (rtwdev,"R_AX_CPU_DISPATCHER_ALWAYS_IMR=0x%08x " , +rtw89_read32 (rtwdev,R_AX_CPU_DISPATCHER_ERR_IMR )); +rtw89_info (rtwdev,"R_AX_CPU_DISPATCHER_ALWAYS_ISR=0x%08x\n" , +rtw89_read32 (rtwdev,R_AX_CPU_DISPATCHER_ERR_ISR )); +rtw89_info (rtwdev,"R_AX_OTHER_DISPATCHER_ALWAYS_IMR=0x%08x " , +rtw89_read32 (rtwdev,R_AX_OTHER_DISPATCHER_ERR_IMR )); +rtw89_info (rtwdev,"R_AX_OTHER_DISPATCHER_ALWAYS_ISR=0x%08x\n" , +rtw89_read32 (rtwdev,R_AX_OTHER_DISPATCHER_ERR_ISR )); +} + +static void rtw89_mac_dump_qta_lost (struct rtw89_dev *rtwdev) +{ +struct rtw89_mac_dle_dfi_qempty qempty; +struct rtw89_mac_dle_dfi_quota quota; +struct rtw89_mac_dle_dfi_ctrl ctrl; +u32 val, not_empty, i; +int ret; + + qempty.dle_type =DLE_CTRL_TYPE_PLE ; + qempty.grpsel =0 ; + ret =dle_dfi_qempty (rtwdev, &qempty); +if (ret) +rtw89_warn (rtwdev,"%s: query DLE fail\n" , __func__); +else +rtw89_info (rtwdev,"DLE group0 empty: 0x%x\n" , qempty.qempty); + +for (not_empty = ~qempty.qempty, i =0 ; not_empty !=0 ; not_empty >>=1 , i++) { +if (!(not_empty &BIT (0 ))) +continue ; + ctrl.type =DLE_CTRL_TYPE_PLE ; + ctrl.target =DLE_DFI_TYPE_QLNKTBL ; + ctrl.addr = (QLNKTBL_ADDR_INFO_SEL_0 ?QLNKTBL_ADDR_INFO_SEL :0 ) | +FIELD_PREP (QLNKTBL_ADDR_TBL_IDX_MASK , i); + ret =dle_dfi_ctrl (rtwdev, &ctrl); +if (ret) +rtw89_warn (rtwdev,"%s: query DLE fail\n" , __func__); +else +rtw89_info (rtwdev,"qidx%d pktcnt = %ld\n" , i, +FIELD_GET (QLNKTBL_DATA_SEL1_PKT_CNT_MASK , + ctrl.out_data)); + } + + quota.dle_type =DLE_CTRL_TYPE_PLE ; + quota.qtaid =6 ; + ret =dle_dfi_quota (rtwdev, "a); +if (ret) +rtw89_warn (rtwdev,"%s: query DLE fail\n" , __func__); +else +rtw89_info (rtwdev,"quota6 rsv/use: 0x%x/0x%x\n" , + quota.rsv_pgnum, quota.use_pgnum); + + val =rtw89_read32 (rtwdev,R_AX_PLE_QTA6_CFG ); +rtw89_info (rtwdev,"[PLE][CMAC0_RX]min_pgnum=0x%lx\n" , +FIELD_GET (B_AX_PLE_Q6_MIN_SIZE_MASK , val)); +rtw89_info (rtwdev,"[PLE][CMAC0_RX]max_pgnum=0x%lx\n" , +FIELD_GET (B_AX_PLE_Q6_MAX_SIZE_MASK , val)); + +dump_err_status_dispatcher (rtwdev); +} + +static void rtw89_mac_dump_l0_to_l1 (struct rtw89_dev *rtwdev, +enum mac_ax_err_info err) +{ +u32 dbg, event; + + dbg =rtw89_read32 (rtwdev,R_AX_SER_DBG_INFO ); + event =FIELD_GET (B_AX_L0_TO_L1_EVENT_MASK , dbg); + +switch (event) { +case MAC_AX_L0_TO_L1_RX_QTA_LOST : +rtw89_info (rtwdev,"quota lost!\n" ); +rtw89_mac_dump_qta_lost (rtwdev); +break ; +default : +break ; + } +} + +static void rtw89_mac_dump_err_status (struct rtw89_dev *rtwdev, +enum mac_ax_err_info err) +{ +u32 dmac_err, cmac_err; + +if (err !=MAC_AX_ERR_L1_ERR_DMAC && + err !=MAC_AX_ERR_L0_PROMOTE_TO_L1 ) +return ; + +rtw89_info (rtwdev,"--->\nerr=0x%x\n" , err); +rtw89_info (rtwdev,"R_AX_SER_DBG_INFO =0x%08x\n" , +rtw89_read32 (rtwdev,R_AX_SER_DBG_INFO )); + + cmac_err =rtw89_read32 (rtwdev,R_AX_CMAC_ERR_ISR ); +rtw89_info (rtwdev,"R_AX_CMAC_ERR_ISR =0x%08x\n" , cmac_err); + dmac_err =rtw89_read32 (rtwdev,R_AX_DMAC_ERR_ISR ); +rtw89_info (rtwdev,"R_AX_DMAC_ERR_ISR =0x%08x\n" , dmac_err); + +if (dmac_err) { +rtw89_info (rtwdev,"R_AX_WDE_ERR_FLAG_CFG =0x%08x " , +rtw89_read32 (rtwdev,R_AX_WDE_ERR_FLAG_CFG )); +rtw89_info (rtwdev,"R_AX_PLE_ERR_FLAG_CFG =0x%08x\n" , +rtw89_read32 (rtwdev,R_AX_PLE_ERR_FLAG_CFG )); + } + +if (dmac_err &B_AX_WDRLS_ERR_FLAG ) { +rtw89_info (rtwdev,"R_AX_WDRLS_ERR_IMR =0x%08x " , +rtw89_read32 (rtwdev,R_AX_WDRLS_ERR_IMR )); +rtw89_info (rtwdev,"R_AX_WDRLS_ERR_ISR =0x%08x\n" , +rtw89_read32 (rtwdev,R_AX_WDRLS_ERR_ISR )); + } + +if (dmac_err &B_AX_WSEC_ERR_FLAG ) { +rtw89_info (rtwdev,"R_AX_SEC_ERR_IMR_ISR =0x%08x\n" , +rtw89_read32 (rtwdev,R_AX_SEC_DEBUG )); +rtw89_info (rtwdev,"SEC_local_Register 0x9D00 =0x%08x\n" , +rtw89_read32 (rtwdev,R_AX_SEC_ENG_CTRL )); +rtw89_info (rtwdev,"SEC_local_Register 0x9D04 =0x%08x\n" , +rtw89_read32 (rtwdev,R_AX_SEC_MPDU_PROC )); +rtw89_info (rtwdev,"SEC_local_Register 0x9D10 =0x%08x\n" , +rtw89_read32 (rtwdev,R_AX_SEC_CAM_ACCESS )); +rtw89_info (rtwdev,"SEC_local_Register 0x9D14 =0x%08x\n" , +rtw89_read32 (rtwdev,R_AX_SEC_CAM_RDATA )); +rtw89_info (rtwdev,"SEC_local_Register 0x9D18 =0x%08x\n" , +rtw89_read32 (rtwdev,R_AX_SEC_CAM_WDATA )); +rtw89_info (rtwdev,"SEC_local_Register 0x9D20 =0x%08x\n" , +rtw89_read32 (rtwdev,R_AX_SEC_TX_DEBUG )); +rtw89_info (rtwdev,"SEC_local_Register 0x9D24 =0x%08x\n" , +rtw89_read32 (rtwdev,R_AX_SEC_RX_DEBUG )); +rtw89_info (rtwdev,"SEC_local_Register 0x9D28 =0x%08x\n" , +rtw89_read32 (rtwdev,R_AX_SEC_TRX_PKT_CNT )); +rtw89_info (rtwdev,"SEC_local_Register 0x9D2C =0x%08x\n" , +rtw89_read32 (rtwdev,R_AX_SEC_TRX_BLK_CNT )); + } + +if (dmac_err &B_AX_MPDU_ERR_FLAG ) { +rtw89_info (rtwdev,"R_AX_MPDU_TX_ERR_IMR =0x%08x " , +rtw89_read32 (rtwdev,R_AX_MPDU_TX_ERR_IMR )); +rtw89_info (rtwdev,"R_AX_MPDU_TX_ERR_ISR =0x%08x\n" , +rtw89_read32 (rtwdev,R_AX_MPDU_TX_ERR_ISR )); +rtw89_info (rtwdev,"R_AX_MPDU_RX_ERR_IMR =0x%08x " , +rtw89_read32 (rtwdev,R_AX_MPDU_RX_ERR_IMR )); +rtw89_info (rtwdev,"R_AX_MPDU_RX_ERR_ISR =0x%08x\n" , +rtw89_read32 (rtwdev,R_AX_MPDU_RX_ERR_ISR )); + } + +if (dmac_err &B_AX_STA_SCHEDULER_ERR_FLAG ) { +rtw89_info (rtwdev,"R_AX_STA_SCHEDULER_ERR_IMR =0x%08x " , +rtw89_read32 (rtwdev,R_AX_STA_SCHEDULER_ERR_IMR )); +rtw89_info (rtwdev,"R_AX_STA_SCHEDULER_ERR_ISR= 0x%08x\n" , +rtw89_read32 (rtwdev,R_AX_STA_SCHEDULER_ERR_ISR )); + } + +if (dmac_err &B_AX_WDE_DLE_ERR_FLAG ) { +rtw89_info (rtwdev,"R_AX_WDE_ERR_IMR=0x%08x " , +rtw89_read32 (rtwdev,R_AX_WDE_ERR_IMR )); +rtw89_info (rtwdev,"R_AX_WDE_ERR_ISR=0x%08x\n" , +rtw89_read32 (rtwdev,R_AX_WDE_ERR_ISR )); +rtw89_info (rtwdev,"R_AX_PLE_ERR_IMR=0x%08x " , +rtw89_read32 (rtwdev,R_AX_PLE_ERR_IMR )); +rtw89_info (rtwdev,"R_AX_PLE_ERR_FLAG_ISR=0x%08x\n" , +rtw89_read32 (rtwdev,R_AX_PLE_ERR_FLAG_ISR )); +dump_err_status_dispatcher (rtwdev); + } + +if (dmac_err &B_AX_TXPKTCTRL_ERR_FLAG ) { +rtw89_info (rtwdev,"R_AX_TXPKTCTL_ERR_IMR_ISR=0x%08x\n" , +rtw89_read32 (rtwdev,R_AX_TXPKTCTL_ERR_IMR_ISR )); +rtw89_info (rtwdev,"R_AX_TXPKTCTL_ERR_IMR_ISR_B1=0x%08x\n" , +rtw89_read32 (rtwdev,R_AX_TXPKTCTL_ERR_IMR_ISR_B1 )); + } + +if (dmac_err &B_AX_PLE_DLE_ERR_FLAG ) { +rtw89_info (rtwdev,"R_AX_WDE_ERR_IMR=0x%08x " , +rtw89_read32 (rtwdev,R_AX_WDE_ERR_IMR )); +rtw89_info (rtwdev,"R_AX_WDE_ERR_ISR=0x%08x\n" , +rtw89_read32 (rtwdev,R_AX_WDE_ERR_ISR )); +rtw89_info (rtwdev,"R_AX_PLE_ERR_IMR=0x%08x " , +rtw89_read32 (rtwdev,R_AX_PLE_ERR_IMR )); +rtw89_info (rtwdev,"R_AX_PLE_ERR_FLAG_ISR=0x%08x\n" , +rtw89_read32 (rtwdev,R_AX_PLE_ERR_FLAG_ISR )); +rtw89_info (rtwdev,"R_AX_WD_CPUQ_OP_0=0x%08x\n" , +rtw89_read32 (rtwdev,R_AX_WD_CPUQ_OP_0 )); +rtw89_info (rtwdev,"R_AX_WD_CPUQ_OP_1=0x%08x\n" , +rtw89_read32 (rtwdev,R_AX_WD_CPUQ_OP_1 )); +rtw89_info (rtwdev,"R_AX_WD_CPUQ_OP_2=0x%08x\n" , +rtw89_read32 (rtwdev,R_AX_WD_CPUQ_OP_2 )); +rtw89_info (rtwdev,"R_AX_WD_CPUQ_OP_STATUS=0x%08x\n" , +rtw89_read32 (rtwdev,R_AX_WD_CPUQ_OP_STATUS )); +rtw89_info (rtwdev,"R_AX_PL_CPUQ_OP_0=0x%08x\n" , +rtw89_read32 (rtwdev,R_AX_PL_CPUQ_OP_0 )); +rtw89_info (rtwdev,"R_AX_PL_CPUQ_OP_1=0x%08x\n" , +rtw89_read32 (rtwdev,R_AX_PL_CPUQ_OP_1 )); +rtw89_info (rtwdev,"R_AX_PL_CPUQ_OP_2=0x%08x\n" , +rtw89_read32 (rtwdev,R_AX_PL_CPUQ_OP_2 )); +rtw89_info (rtwdev,"R_AX_PL_CPUQ_OP_STATUS=0x%08x\n" , +rtw89_read32 (rtwdev,R_AX_PL_CPUQ_OP_STATUS )); +rtw89_info (rtwdev,"R_AX_RXDMA_PKT_INFO_0=0x%08x\n" , +rtw89_read32 (rtwdev,R_AX_RXDMA_PKT_INFO_0 )); +rtw89_info (rtwdev,"R_AX_RXDMA_PKT_INFO_1=0x%08x\n" , +rtw89_read32 (rtwdev,R_AX_RXDMA_PKT_INFO_1 )); +rtw89_info (rtwdev,"R_AX_RXDMA_PKT_INFO_2=0x%08x\n" , +rtw89_read32 (rtwdev,R_AX_RXDMA_PKT_INFO_2 )); +dump_err_status_dispatcher (rtwdev); + } + +if (dmac_err &B_AX_PKTIN_ERR_FLAG ) { +rtw89_info (rtwdev,"R_AX_PKTIN_ERR_IMR =0x%08x " , +rtw89_read32 (rtwdev,R_AX_PKTIN_ERR_IMR )); +rtw89_info (rtwdev,"R_AX_PKTIN_ERR_ISR =0x%08x\n" , +rtw89_read32 (rtwdev,R_AX_PKTIN_ERR_ISR )); +rtw89_info (rtwdev,"R_AX_PKTIN_ERR_IMR =0x%08x " , +rtw89_read32 (rtwdev,R_AX_PKTIN_ERR_IMR )); +rtw89_info (rtwdev,"R_AX_PKTIN_ERR_ISR =0x%08x\n" , +rtw89_read32 (rtwdev,R_AX_PKTIN_ERR_ISR )); + } + +if (dmac_err &B_AX_DISPATCH_ERR_FLAG ) +dump_err_status_dispatcher (rtwdev); + +if (dmac_err &B_AX_DLE_CPUIO_ERR_FLAG ) { +rtw89_info (rtwdev,"R_AX_CPUIO_ERR_IMR=0x%08x " , +rtw89_read32 (rtwdev,R_AX_CPUIO_ERR_IMR )); +rtw89_info (rtwdev,"R_AX_CPUIO_ERR_ISR=0x%08x\n" , +rtw89_read32 (rtwdev,R_AX_CPUIO_ERR_ISR )); + } + +if (dmac_err &BIT (11 )) { +rtw89_info (rtwdev,"R_AX_BBRPT_COM_ERR_IMR_ISR=0x%08x\n" , +rtw89_read32 (rtwdev,R_AX_BBRPT_COM_ERR_IMR_ISR )); + } + +if (cmac_err &B_AX_SCHEDULE_TOP_ERR_IND ) { +rtw89_info (rtwdev,"R_AX_SCHEDULE_ERR_IMR=0x%08x " , +rtw89_read32 (rtwdev,R_AX_SCHEDULE_ERR_IMR )); +rtw89_info (rtwdev,"R_AX_SCHEDULE_ERR_ISR=0x%04x\n" , +rtw89_read16 (rtwdev,R_AX_SCHEDULE_ERR_ISR )); + } + +if (cmac_err &B_AX_PTCL_TOP_ERR_IND ) { +rtw89_info (rtwdev,"R_AX_PTCL_IMR0=0x%08x " , +rtw89_read32 (rtwdev,R_AX_PTCL_IMR0 )); +rtw89_info (rtwdev,"R_AX_PTCL_ISR0=0x%08x\n" , +rtw89_read32 (rtwdev,R_AX_PTCL_ISR0 )); + } + +if (cmac_err &B_AX_DMA_TOP_ERR_IND ) { +rtw89_info (rtwdev,"R_AX_DLE_CTRL=0x%08x\n" , +rtw89_read32 (rtwdev,R_AX_DLE_CTRL )); + } + +if (cmac_err &B_AX_PHYINTF_ERR_IND ) { +rtw89_info (rtwdev,"R_AX_PHYINFO_ERR_IMR=0x%08x\n" , +rtw89_read32 (rtwdev,R_AX_PHYINFO_ERR_IMR )); + } + +if (cmac_err &B_AX_TXPWR_CTRL_ERR_IND ) { +rtw89_info (rtwdev,"R_AX_TXPWR_IMR=0x%08x " , +rtw89_read32 (rtwdev,R_AX_TXPWR_IMR )); +rtw89_info (rtwdev,"R_AX_TXPWR_ISR=0x%08x\n" , +rtw89_read32 (rtwdev,R_AX_TXPWR_ISR )); + } + +if (cmac_err &B_AX_WMAC_RX_ERR_IND ) { +rtw89_info (rtwdev,"R_AX_DBGSEL_TRXPTCL=0x%08x " , +rtw89_read32 (rtwdev,R_AX_DBGSEL_TRXPTCL )); +rtw89_info (rtwdev,"R_AX_PHYINFO_ERR_ISR=0x%08x\n" , +rtw89_read32 (rtwdev,R_AX_PHYINFO_ERR_ISR )); + } + +if (cmac_err &B_AX_WMAC_TX_ERR_IND ) { +rtw89_info (rtwdev,"R_AX_TMAC_ERR_IMR_ISR=0x%08x " , +rtw89_read32 (rtwdev,R_AX_TMAC_ERR_IMR_ISR )); +rtw89_info (rtwdev,"R_AX_DBGSEL_TRXPTCL=0x%08x\n" , +rtw89_read32 (rtwdev,R_AX_DBGSEL_TRXPTCL )); + } + + rtwdev->hci.ops->dump_err_status (rtwdev); + +if (err ==MAC_AX_ERR_L0_PROMOTE_TO_L1 ) +rtw89_mac_dump_l0_to_l1 (rtwdev, err); + +rtw89_info (rtwdev,"<---\n" ); +} + +u32 rtw89_mac_get_err_status (struct rtw89_dev *rtwdev) +{ +u32 err; +int ret; + + ret =read_poll_timeout (rtw89_read32, err, (err !=0 ),1000 ,100000 , + false, rtwdev,R_AX_HALT_C2H_CTRL ); +if (ret) { +rtw89_warn (rtwdev,"Polling FW err status fail\n" ); +return ret; + } + + err =rtw89_read32 (rtwdev,R_AX_HALT_C2H ); +rtw89_write32 (rtwdev,R_AX_HALT_C2H_CTRL ,0 ); + +rtw89_fw_st_dbg_dump (rtwdev); +rtw89_mac_dump_err_status (rtwdev, err); + +return err; +} +EXPORT_SYMBOL (rtw89_mac_get_err_status); + +int rtw89_mac_set_err_status (struct rtw89_dev *rtwdev,u32 err) +{ +u32 halt; +int ret =0 ; + +if (err >MAC_AX_SET_ERR_MAX ) { +rtw89_err (rtwdev,"Bad set-err-status value 0x%08x\n" , err); +return -EINVAL ; + } + + ret =read_poll_timeout (rtw89_read32, halt, (halt ==0x0 ),1000 , +100000 , false, rtwdev,R_AX_HALT_H2C_CTRL ); +if (ret) { +rtw89_err (rtwdev,"FW doesn't receive previous msg\n" ); +return -EFAULT ; + } + +rtw89_write32 (rtwdev,R_AX_HALT_H2C , err); +rtw89_write32 (rtwdev,R_AX_HALT_H2C_CTRL ,B_AX_HALT_H2C_TRIGGER ); + +return 0 ; +} +EXPORT_SYMBOL (rtw89_mac_set_err_status); + +struct rtw89_hfc_prec_cfg rtw_hfc_preccfg_pcie = { +2 ,40 ,0 ,0 ,1 ,0 ,0 ,0 +}; + +static int hfc_reset_param (struct rtw89_dev *rtwdev) +{ +struct rtw89_hfc_param *param = &rtwdev->mac.hfc_param; +struct rtw89_hfc_param_ini param_ini = {NULL }; +u8 qta_mode = rtwdev->mac.dle_info.qta_mode; + +switch (rtwdev->hci.type) { +case RTW89_HCI_TYPE_PCIE : + param_ini = rtwdev->chip->hfc_param_ini[qta_mode]; + param->en =0 ; +break ; +default : +return -EINVAL ; + } + +if (param_ini.pub_cfg) + param->pub_cfg = param_ini.pub_cfg; + +if (param_ini.prec_cfg) { + param->prec_cfg = param_ini.prec_cfg; + rtwdev->hal.sw_amsdu_max_size = + param->prec_cfg->wp_ch07_prec *HFC_PAGE_UNIT ; + } + +if (param_ini.ch_cfg) + param->ch_cfg = param_ini.ch_cfg; + +memset (¶m->ch_info,0 ,sizeof (param->ch_info)); +memset (¶m->pub_info,0 ,sizeof (param->pub_info)); + param->mode = param_ini.mode; + +return 0 ; +} + +static int hfc_ch_cfg_chk (struct rtw89_dev *rtwdev,u8 ch) +{ +struct rtw89_hfc_param *param = &rtwdev->mac.hfc_param; +struct rtw89_hfc_ch_cfg *ch_cfg = param->ch_cfg; +struct rtw89_hfc_pub_cfg *pub_cfg = param->pub_cfg; +struct rtw89_hfc_prec_cfg *prec_cfg = param->prec_cfg; + +if (ch >=RTW89_DMA_CH_NUM ) +return -EINVAL ; + +if ((ch_cfg[ch].min && ch_cfg[ch].min < prec_cfg->ch011_prec) || + ch_cfg[ch].max > pub_cfg->pub_max) +return -EINVAL ; +if (ch_cfg[ch].grp >= grp_num) +return -EINVAL ; + +return 0 ; +} + +static int hfc_pub_info_chk (struct rtw89_dev *rtwdev) +{ +struct rtw89_hfc_param *param = &rtwdev->mac.hfc_param; +struct rtw89_hfc_pub_cfg *cfg = param->pub_cfg; +struct rtw89_hfc_pub_info *info = ¶m->pub_info; + +if (info->g0_used + info->g1_used + info->pub_aval != cfg->pub_max) { +if (rtwdev->chip->chip_id ==RTL8852A ) +return 0 ; +else +return -EFAULT ; + } + +return 0 ; +} + +static int hfc_pub_cfg_chk (struct rtw89_dev *rtwdev) +{ +struct rtw89_hfc_param *param = &rtwdev->mac.hfc_param; +struct rtw89_hfc_pub_cfg *pub_cfg = param->pub_cfg; + +if (pub_cfg->grp0 + pub_cfg->grp1 != pub_cfg->pub_max) +return 0 ; + +return 0 ; +} + +static int hfc_ch_ctrl (struct rtw89_dev *rtwdev,u8 ch) +{ +struct rtw89_hfc_param *param = &rtwdev->mac.hfc_param; +struct rtw89_hfc_ch_cfg *cfg = param->ch_cfg; +int ret =0 ; +u32 val =0 ; + + ret =rtw89_mac_check_mac_en (rtwdev,RTW89_MAC_0 ,RTW89_DMAC_SEL ); +if (ret) +return ret; + + ret =hfc_ch_cfg_chk (rtwdev, ch); +if (ret) +return ret; + +if (ch >RTW89_DMA_B1HI ) +return -EINVAL ; + + val =u32_encode_bits (cfg[ch].min,B_AX_MIN_PG_MASK ) | +u32_encode_bits (cfg[ch].max,B_AX_MAX_PG_MASK ) | + (cfg[ch].grp ?B_AX_GRP :0 ); +rtw89_write32 (rtwdev,R_AX_ACH0_PAGE_CTRL + ch *4 , val); + +return 0 ; +} + +static int hfc_upd_ch_info (struct rtw89_dev *rtwdev,u8 ch) +{ +struct rtw89_hfc_param *param = &rtwdev->mac.hfc_param; +struct rtw89_hfc_ch_info *info = param->ch_info; +struct rtw89_hfc_ch_cfg *cfg = param->ch_cfg; +u32 val; +u32 ret; + + ret =rtw89_mac_check_mac_en (rtwdev,RTW89_MAC_0 ,RTW89_DMAC_SEL ); +if (ret) +return ret; + +if (ch >RTW89_DMA_H2C ) +return -EINVAL ; + + val =rtw89_read32 (rtwdev,R_AX_ACH0_PAGE_INFO + ch *4 ); + info[ch].aval =u32_get_bits (val,B_AX_AVAL_PG_MASK ); +if (ch <RTW89_DMA_H2C ) + info[ch].used =u32_get_bits (val,B_AX_USE_PG_MASK ); +else + info[ch].used = cfg[ch].min - info[ch].aval; + +return 0 ; +} + +static int hfc_pub_ctrl (struct rtw89_dev *rtwdev) +{ +struct rtw89_hfc_pub_cfg *cfg = rtwdev->mac.hfc_param.pub_cfg; +u32 val; +int ret; + + ret =rtw89_mac_check_mac_en (rtwdev,RTW89_MAC_0 ,RTW89_DMAC_SEL ); +if (ret) +return ret; + + ret =hfc_pub_cfg_chk (rtwdev); +if (ret) +return ret; + + val =u32_encode_bits (cfg->grp0,B_AX_PUBPG_G0_MASK ) | +u32_encode_bits (cfg->grp1,B_AX_PUBPG_G1_MASK ); +rtw89_write32 (rtwdev,R_AX_PUB_PAGE_CTRL1 , val); + + val =u32_encode_bits (cfg->wp_thrd,B_AX_WP_THRD_MASK ); +rtw89_write32 (rtwdev,R_AX_WP_PAGE_CTRL2 , val); + +return 0 ; +} + +static int hfc_upd_mix_info (struct rtw89_dev *rtwdev) +{ +struct rtw89_hfc_param *param = &rtwdev->mac.hfc_param; +struct rtw89_hfc_pub_cfg *pub_cfg = param->pub_cfg; +struct rtw89_hfc_prec_cfg *prec_cfg = param->prec_cfg; +struct rtw89_hfc_pub_info *info = ¶m->pub_info; +u32 val; +int ret; + + ret =rtw89_mac_check_mac_en (rtwdev,RTW89_MAC_0 ,RTW89_DMAC_SEL ); +if (ret) +return ret; + + val =rtw89_read32 (rtwdev,R_AX_PUB_PAGE_INFO1 ); + info->g0_used =u32_get_bits (val,B_AX_G0_USE_PG_MASK ); + info->g1_used =u32_get_bits (val,B_AX_G1_USE_PG_MASK ); + val =rtw89_read32 (rtwdev,R_AX_PUB_PAGE_INFO3 ); + info->g0_aval =u32_get_bits (val,B_AX_G0_AVAL_PG_MASK ); + info->g1_aval =u32_get_bits (val,B_AX_G1_AVAL_PG_MASK ); + info->pub_aval = +u32_get_bits (rtw89_read32 (rtwdev,R_AX_PUB_PAGE_INFO2 ), +B_AX_PUB_AVAL_PG_MASK ); + info->wp_aval = +u32_get_bits (rtw89_read32 (rtwdev,R_AX_WP_PAGE_INFO1 ), +B_AX_WP_AVAL_PG_MASK ); + + val =rtw89_read32 (rtwdev,R_AX_HCI_FC_CTRL ); + param->en = val &B_AX_HCI_FC_EN ?1 :0 ; + param->h2c_en = val &B_AX_HCI_FC_CH12_EN ?1 :0 ; + param->mode =u32_get_bits (val,B_AX_HCI_FC_MODE_MASK ); + prec_cfg->ch011_full_cond = +u32_get_bits (val,B_AX_HCI_FC_WD_FULL_COND_MASK ); + prec_cfg->h2c_full_cond = +u32_get_bits (val,B_AX_HCI_FC_CH12_FULL_COND_MASK ); + prec_cfg->wp_ch07_full_cond = +u32_get_bits (val,B_AX_HCI_FC_WP_CH07_FULL_COND_MASK ); + prec_cfg->wp_ch811_full_cond = +u32_get_bits (val,B_AX_HCI_FC_WP_CH811_FULL_COND_MASK ); + + val =rtw89_read32 (rtwdev,R_AX_CH_PAGE_CTRL ); + prec_cfg->ch011_prec =u32_get_bits (val,B_AX_PREC_PAGE_CH011_MASK ); + prec_cfg->h2c_prec =u32_get_bits (val,B_AX_PREC_PAGE_CH12_MASK ); + + val =rtw89_read32 (rtwdev,R_AX_PUB_PAGE_CTRL2 ); + pub_cfg->pub_max =u32_get_bits (val,B_AX_PUBPG_ALL_MASK ); + + val =rtw89_read32 (rtwdev,R_AX_WP_PAGE_CTRL1 ); + prec_cfg->wp_ch07_prec =u32_get_bits (val,B_AX_PREC_PAGE_WP_CH07_MASK ); + prec_cfg->wp_ch811_prec =u32_get_bits (val,B_AX_PREC_PAGE_WP_CH811_MASK ); + + val =rtw89_read32 (rtwdev,R_AX_WP_PAGE_CTRL2 ); + pub_cfg->wp_thrd =u32_get_bits (val,B_AX_WP_THRD_MASK ); + + val =rtw89_read32 (rtwdev,R_AX_PUB_PAGE_CTRL1 ); + pub_cfg->grp0 =u32_get_bits (val,B_AX_PUBPG_G0_MASK ); + pub_cfg->grp1 =u32_get_bits (val,B_AX_PUBPG_G1_MASK ); + + ret =hfc_pub_info_chk (rtwdev); +if (param->en && ret) +return ret; + +return 0 ; +} + +static void hfc_h2c_cfg (struct rtw89_dev *rtwdev) +{ +struct rtw89_hfc_param *param = &rtwdev->mac.hfc_param; +struct rtw89_hfc_prec_cfg *prec_cfg = param->prec_cfg; +u32 val; + + val =u32_encode_bits (prec_cfg->h2c_prec,B_AX_PREC_PAGE_CH12_MASK ); +rtw89_write32 (rtwdev,R_AX_CH_PAGE_CTRL , val); + +rtw89_write32_mask (rtwdev,R_AX_HCI_FC_CTRL , +B_AX_HCI_FC_CH12_FULL_COND_MASK , + prec_cfg->h2c_full_cond); +} + +static void hfc_mix_cfg (struct rtw89_dev *rtwdev) +{ +struct rtw89_hfc_param *param = &rtwdev->mac.hfc_param; +struct rtw89_hfc_pub_cfg *pub_cfg = param->pub_cfg; +struct rtw89_hfc_prec_cfg *prec_cfg = param->prec_cfg; +u32 val; + + val =u32_encode_bits (prec_cfg->ch011_prec,B_AX_PREC_PAGE_CH011_MASK ) | +u32_encode_bits (prec_cfg->h2c_prec,B_AX_PREC_PAGE_CH12_MASK ); +rtw89_write32 (rtwdev,R_AX_CH_PAGE_CTRL , val); + + val =u32_encode_bits (pub_cfg->pub_max,B_AX_PUBPG_ALL_MASK ); +rtw89_write32 (rtwdev,R_AX_PUB_PAGE_CTRL2 , val); + + val =u32_encode_bits (prec_cfg->wp_ch07_prec, +B_AX_PREC_PAGE_WP_CH07_MASK ) | +u32_encode_bits (prec_cfg->wp_ch811_prec, +B_AX_PREC_PAGE_WP_CH811_MASK ); +rtw89_write32 (rtwdev,R_AX_WP_PAGE_CTRL1 , val); + + val =u32_replace_bits (rtw89_read32 (rtwdev,R_AX_HCI_FC_CTRL ), + param->mode,B_AX_HCI_FC_MODE_MASK ); + val =u32_replace_bits (val, prec_cfg->ch011_full_cond, +B_AX_HCI_FC_WD_FULL_COND_MASK ); + val =u32_replace_bits (val, prec_cfg->h2c_full_cond, +B_AX_HCI_FC_CH12_FULL_COND_MASK ); + val =u32_replace_bits (val, prec_cfg->wp_ch07_full_cond, +B_AX_HCI_FC_WP_CH07_FULL_COND_MASK ); + val =u32_replace_bits (val, prec_cfg->wp_ch811_full_cond, +B_AX_HCI_FC_WP_CH811_FULL_COND_MASK ); +rtw89_write32 (rtwdev,R_AX_HCI_FC_CTRL , val); +} + +static void hfc_func_en (struct rtw89_dev *rtwdev,bool en,bool h2c_en) +{ +struct rtw89_hfc_param *param = &rtwdev->mac.hfc_param; +u32 val; + + val =rtw89_read32 (rtwdev,R_AX_HCI_FC_CTRL ); + param->en = en; + param->h2c_en = h2c_en; + val = en ? (val |B_AX_HCI_FC_EN ) : (val & ~B_AX_HCI_FC_EN ); + val = h2c_en ? (val |B_AX_HCI_FC_CH12_EN ) : + (val & ~B_AX_HCI_FC_CH12_EN ); +rtw89_write32 (rtwdev,R_AX_HCI_FC_CTRL , val); +} + +static int hfc_init (struct rtw89_dev *rtwdev,bool reset,bool en,bool h2c_en) +{ +u8 ch; +u32 ret =0 ; + +if (reset) + ret =hfc_reset_param (rtwdev); +if (ret) +return ret; + + ret =rtw89_mac_check_mac_en (rtwdev,RTW89_MAC_0 ,RTW89_DMAC_SEL ); +if (ret) +return ret; + +hfc_func_en (rtwdev, false, false); + +if (!en && h2c_en) { +hfc_h2c_cfg (rtwdev); +hfc_func_en (rtwdev, en, h2c_en); +return ret; + } + +for (ch =RTW89_DMA_ACH0 ; ch <RTW89_DMA_H2C ; ch++) { + ret =hfc_ch_ctrl (rtwdev, ch); +if (ret) +return ret; + } + + ret =hfc_pub_ctrl (rtwdev); +if (ret) +return ret; + +hfc_mix_cfg (rtwdev); +if (en || h2c_en) { +hfc_func_en (rtwdev, en, h2c_en); +udelay (10 ); + } +for (ch =RTW89_DMA_ACH0 ; ch <RTW89_DMA_H2C ; ch++) { + ret =hfc_upd_ch_info (rtwdev, ch); +if (ret) +return ret; + } + ret =hfc_upd_mix_info (rtwdev); + +return ret; +} + +#define PWR_POLL_CNT 2000 +static int pwr_cmd_poll (struct rtw89_dev *rtwdev, +const struct rtw89_pwr_cfg *cfg) +{ +u8 val =0 ; +int ret; +u32 addr = cfg->base ==PWR_INTF_MSK_SDIO ? + cfg->addr |SDIO_LOCAL_BASE_ADDR : cfg->addr; + + ret =read_poll_timeout (rtw89_read8, val, !((val ^ cfg->val) & cfg->msk), +1000 ,1000 *PWR_POLL_CNT , false, rtwdev, addr); + +if (!ret) +return 0 ; + +rtw89_warn (rtwdev,"[ERR] Polling timeout\n" ); +rtw89_warn (rtwdev,"[ERR] addr: %X, %X\n" , addr, cfg->addr); +rtw89_warn (rtwdev,"[ERR] val: %X, %X\n" , val, cfg->val); + +return -EBUSY ; +} + +static int rtw89_mac_sub_pwr_seq (struct rtw89_dev *rtwdev,u8 cv_msk, +u8 intf_msk,const struct rtw89_pwr_cfg *cfg) +{ +const struct rtw89_pwr_cfg *cur_cfg; +u32 addr; +u8 val; + +for (cur_cfg = cfg; cur_cfg->cmd !=PWR_CMD_END ; cur_cfg++) { +if (!(cur_cfg->intf_msk & intf_msk) || + !(cur_cfg->cv_msk & cv_msk)) +continue ; + +switch (cur_cfg->cmd) { +case PWR_CMD_WRITE : + addr = cur_cfg->addr; + +if (cur_cfg->base ==PWR_BASE_SDIO ) + addr |=SDIO_LOCAL_BASE_ADDR ; + + val =rtw89_read8 (rtwdev, addr); + val &= ~(cur_cfg->msk); + val |= (cur_cfg->val & cur_cfg->msk); + +rtw89_write8 (rtwdev, addr, val); +break ; +case PWR_CMD_POLL : +if (pwr_cmd_poll (rtwdev, cur_cfg)) +return -EBUSY ; +break ; +case PWR_CMD_DELAY : +if (cur_cfg->val ==PWR_DELAY_US ) +udelay (cur_cfg->addr); +else +fsleep (cur_cfg->addr *1000 ); +break ; +default : +return -EINVAL ; + } + } + +return 0 ; +} + +static int rtw89_mac_pwr_seq (struct rtw89_dev *rtwdev, +const struct rtw89_pwr_cfg *const *cfg_seq) +{ +u32 idx =0 ; +const struct rtw89_pwr_cfg *cfg; +int ret; + +do { + cfg = cfg_seq[idx]; +if (!cfg) +break ; + + ret =rtw89_mac_sub_pwr_seq (rtwdev,BIT (rtwdev->hal.cv), +PWR_INTF_MSK_PCIE , cfg); +if (ret) +return -EBUSY ; + + idx++; + }while (1 ); + +return 0 ; +} + +static enum rtw89_rpwm_req_pwr_state +rtw89_mac_get_req_pwr_state (struct rtw89_dev *rtwdev) +{ +enum rtw89_rpwm_req_pwr_state state; + +switch (rtwdev->ps_mode) { +case RTW89_PS_MODE_RFOFF : + state =RTW89_MAC_RPWM_REQ_PWR_STATE_BAND0_RFOFF ; +break ; +case RTW89_PS_MODE_CLK_GATED : + state =RTW89_MAC_RPWM_REQ_PWR_STATE_CLK_GATED ; +break ; +case RTW89_PS_MODE_PWR_GATED : + state =RTW89_MAC_RPWM_REQ_PWR_STATE_PWR_GATED ; +break ; +default : + state =RTW89_MAC_RPWM_REQ_PWR_STATE_ACTIVE ; +break ; + } +return state; +} + +static void rtw89_mac_send_rpwm (struct rtw89_dev *rtwdev, +enum rtw89_rpwm_req_pwr_state req_pwr_state) +{ +u16 request; + + request =rtw89_read16 (rtwdev,R_AX_RPWM ); + request ^= request |PS_RPWM_TOGGLE ; + + rtwdev->mac.rpwm_seq_num = (rtwdev->mac.rpwm_seq_num +1 ) & +RPWM_SEQ_NUM_MAX ; + request |=FIELD_PREP (PS_RPWM_SEQ_NUM , rtwdev->mac.rpwm_seq_num); + + request |= req_pwr_state; + +if (req_pwr_state <RTW89_MAC_RPWM_REQ_PWR_STATE_CLK_GATED ) + request |=PS_RPWM_ACK ; + +rtw89_write16 (rtwdev, rtwdev->hci.rpwm_addr, request); +} + +static int rtw89_mac_check_cpwm_state (struct rtw89_dev *rtwdev, +enum rtw89_rpwm_req_pwr_state req_pwr_state) +{ +bool request_deep_mode; +bool in_deep_mode; +u8 rpwm_req_num; +u8 cpwm_rsp_seq; +u8 cpwm_seq; +u8 cpwm_status; + +if (req_pwr_state >=RTW89_MAC_RPWM_REQ_PWR_STATE_CLK_GATED ) + request_deep_mode = true; +else + request_deep_mode = false; + +if (rtw89_read32_mask (rtwdev,R_AX_LDM ,B_AX_EN_32K )) + in_deep_mode = true; +else + in_deep_mode = false; + +if (request_deep_mode != in_deep_mode) +return -EPERM ; + +if (request_deep_mode) +return 0 ; + + rpwm_req_num = rtwdev->mac.rpwm_seq_num; + cpwm_rsp_seq =rtw89_read16_mask (rtwdev,R_AX_CPWM , +PS_CPWM_RSP_SEQ_NUM ); + +if (rpwm_req_num != cpwm_rsp_seq) +return -EPERM ; + + rtwdev->mac.cpwm_seq_num = (rtwdev->mac.cpwm_seq_num +1 ) & +CPWM_SEQ_NUM_MAX ; + + cpwm_seq =rtw89_read16_mask (rtwdev,R_AX_CPWM ,PS_CPWM_SEQ_NUM ); +if (cpwm_seq != rtwdev->mac.cpwm_seq_num) +return -EPERM ; + + cpwm_status =rtw89_read16_mask (rtwdev,R_AX_CPWM ,PS_CPWM_STATE ); +if (cpwm_status != req_pwr_state) +return -EPERM ; + +return 0 ; +} + +void rtw89_mac_power_mode_change (struct rtw89_dev *rtwdev,bool enter) +{ +enum rtw89_rpwm_req_pwr_state state; +int ret; + +if (enter) + state =rtw89_mac_get_req_pwr_state (rtwdev); +else + state =RTW89_MAC_RPWM_REQ_PWR_STATE_ACTIVE ; + +rtw89_mac_send_rpwm (rtwdev, state); + ret =read_poll_timeout_atomic (rtw89_mac_check_cpwm_state, ret, !ret, +1000 ,15000 , false, rtwdev, state); +if (ret) +rtw89_err (rtwdev,"firmware failed to ack for %s ps mode\n" , + enter ?"entering" :"leaving" ); +} + +static int rtw89_mac_power_switch (struct rtw89_dev *rtwdev,bool on) +{ +#define PWR_ACT 1 +const struct rtw89_chip_info *chip = rtwdev->chip; +const struct rtw89_pwr_cfg *const *cfg_seq; +struct rtw89_hal *hal = &rtwdev->hal; +int ret; +u8 val; + +if (on) + cfg_seq = chip->pwr_on_seq; +else + cfg_seq = chip->pwr_off_seq; + +if (test_bit (RTW89_FLAG_FW_RDY , rtwdev->flags)) +__rtw89_leave_ps_mode (rtwdev); + + val =rtw89_read8 (rtwdev,0x3F1 ) &0x3 ; +if (on && val ==PWR_ACT ) { +rtw89_err (rtwdev,"MAC has already powered on\n" ); +return -EBUSY ; + } + + ret =rtw89_mac_pwr_seq (rtwdev, cfg_seq); +if (ret) +return ret; + +if (on) { +set_bit (RTW89_FLAG_POWERON , rtwdev->flags); +rtw89_write8 (rtwdev,R_AX_SCOREBOARD +3 ,MAC_AX_NOTIFY_TP_MAJOR ); + }else { +clear_bit (RTW89_FLAG_POWERON , rtwdev->flags); +clear_bit (RTW89_FLAG_FW_RDY , rtwdev->flags); +rtw89_write8 (rtwdev,R_AX_SCOREBOARD +3 ,MAC_AX_NOTIFY_PWR_MAJOR ); + hal->current_channel =0 ; + } + +return 0 ; +#undef PWR_ACT +} + +void rtw89_mac_pwr_off (struct rtw89_dev *rtwdev) +{ +rtw89_mac_power_switch (rtwdev, false); +} + +static int cmac_func_en (struct rtw89_dev *rtwdev,u8 mac_idx,bool en) +{ +u32 func_en =0 ; +u32 ck_en =0 ; +u32 c1pc_en =0 ; +u32 addrl_func_en[] = {R_AX_CMAC_FUNC_EN ,R_AX_CMAC_FUNC_EN_C1 }; +u32 addrl_ck_en[] = {R_AX_CK_EN ,R_AX_CK_EN_C1 }; + + func_en =B_AX_CMAC_EN |B_AX_CMAC_TXEN |B_AX_CMAC_RXEN | +B_AX_PHYINTF_EN |B_AX_CMAC_DMA_EN |B_AX_PTCLTOP_EN | +B_AX_SCHEDULER_EN |B_AX_TMAC_EN |B_AX_RMAC_EN ; + ck_en =B_AX_CMAC_CKEN |B_AX_PHYINTF_CKEN |B_AX_CMAC_DMA_CKEN | +B_AX_PTCLTOP_CKEN |B_AX_SCHEDULER_CKEN |B_AX_TMAC_CKEN | +B_AX_RMAC_CKEN ; + c1pc_en =B_AX_R_SYM_WLCMAC1_PC_EN | +B_AX_R_SYM_WLCMAC1_P1_PC_EN | +B_AX_R_SYM_WLCMAC1_P2_PC_EN | +B_AX_R_SYM_WLCMAC1_P3_PC_EN | +B_AX_R_SYM_WLCMAC1_P4_PC_EN ; + +if (en) { +if (mac_idx ==RTW89_MAC_1 ) { +rtw89_write32_set (rtwdev,R_AX_AFE_CTRL1 , c1pc_en); +rtw89_write32_clr (rtwdev,R_AX_SYS_ISO_CTRL_EXTEND , +B_AX_R_SYM_ISO_CMAC12PP ); +rtw89_write32_set (rtwdev,R_AX_SYS_ISO_CTRL_EXTEND , +B_AX_CMAC1_FEN ); + } +rtw89_write32_set (rtwdev, addrl_ck_en[mac_idx], ck_en); +rtw89_write32_set (rtwdev, addrl_func_en[mac_idx], func_en); + }else { +rtw89_write32_clr (rtwdev, addrl_func_en[mac_idx], func_en); +rtw89_write32_clr (rtwdev, addrl_ck_en[mac_idx], ck_en); +if (mac_idx ==RTW89_MAC_1 ) { +rtw89_write32_clr (rtwdev,R_AX_SYS_ISO_CTRL_EXTEND , +B_AX_CMAC1_FEN ); +rtw89_write32_set (rtwdev,R_AX_SYS_ISO_CTRL_EXTEND , +B_AX_R_SYM_ISO_CMAC12PP ); +rtw89_write32_clr (rtwdev,R_AX_AFE_CTRL1 , c1pc_en); + } + } + +return 0 ; +} + +static int dmac_func_en (struct rtw89_dev *rtwdev) +{ +u32 val32; +u32 ret =0 ; + + val32 = (B_AX_MAC_FUNC_EN |B_AX_DMAC_FUNC_EN |B_AX_MAC_SEC_EN | +B_AX_DISPATCHER_EN |B_AX_DLE_CPUIO_EN |B_AX_PKT_IN_EN | +B_AX_DMAC_TBL_EN |B_AX_PKT_BUF_EN |B_AX_STA_SCH_EN | +B_AX_TXPKT_CTRL_EN |B_AX_WD_RLS_EN |B_AX_MPDU_PROC_EN ); +rtw89_write32 (rtwdev,R_AX_DMAC_FUNC_EN , val32); + + val32 = (B_AX_MAC_SEC_CLK_EN |B_AX_DISPATCHER_CLK_EN | +B_AX_DLE_CPUIO_CLK_EN |B_AX_PKT_IN_CLK_EN | +B_AX_STA_SCH_CLK_EN |B_AX_TXPKT_CTRL_CLK_EN | +B_AX_WD_RLS_CLK_EN ); +rtw89_write32 (rtwdev,R_AX_DMAC_CLK_EN , val32); + +return ret; +} + +static int chip_func_en (struct rtw89_dev *rtwdev) +{ +rtw89_write32_set (rtwdev,R_AX_SPSLDO_ON_CTRL0 ,B_AX_OCP_L1_MASK ); + +return 0 ; +} + +static int rtw89_mac_sys_init (struct rtw89_dev *rtwdev) +{ +int ret; + + ret =dmac_func_en (rtwdev); +if (ret) +return ret; + + ret =cmac_func_en (rtwdev,0 , true); +if (ret) +return ret; + + ret =chip_func_en (rtwdev); +if (ret) +return ret; + +return ret; +} + +/* PCIE 64 */ +struct rtw89_dle_size wde_size0 = { +RTW89_WDE_PG_64 ,4095 ,1 , +}; + +/* DLFW */ +struct rtw89_dle_size wde_size4 = { +RTW89_WDE_PG_64 ,0 ,4096 , +}; + +/* PCIE */ +struct rtw89_dle_size ple_size0 = { +RTW89_PLE_PG_128 ,1520 ,16 , +}; + +/* DLFW */ +struct rtw89_dle_size ple_size4 = { +RTW89_PLE_PG_128 ,64 ,1472 , +}; + +/* PCIE 64 */ +struct rtw89_wde_quota wde_qt0 = { +3792 ,196 ,0 ,107 , +}; + +/* DLFW */ +struct rtw89_wde_quota wde_qt4 = { +0 ,0 ,0 ,0 , +}; + +/* PCIE SCC */ +struct rtw89_ple_quota ple_qt4 = { +264 ,0 ,16 ,20 ,26 ,13 ,356 ,0 ,32 ,40 ,8 , +}; + +/* PCIE SCC */ +struct rtw89_ple_quota ple_qt5 = { +264 ,0 ,32 ,20 ,64 ,13 ,1101 ,0 ,64 ,128 ,120 , +}; + +/* DLFW */ +struct rtw89_ple_quota ple_qt13 = { +0 ,0 ,16 ,48 ,0 ,0 ,0 ,0 ,0 ,0 ,0 +}; + +static struct rtw89_dle_mem *get_dle_mem_cfg (struct rtw89_dev *rtwdev, +enum rtw89_qta_mode mode) +{ +struct rtw89_mac_info *mac = &rtwdev->mac; +struct rtw89_dle_mem *cfg; + + cfg = &rtwdev->chip->dle_mem[mode]; +if (!cfg) +return NULL ; + +if (cfg->mode != mode) { +rtw89_warn (rtwdev,"qta mode unmatch!\n" ); +return NULL ; + } + + mac->dle_info.wde_pg_size = cfg->wde_size->pge_size; + mac->dle_info.ple_pg_size = cfg->ple_size->pge_size; + mac->dle_info.qta_mode = mode; + mac->dle_info.c0_rx_qta = cfg->ple_min_qt->cma0_dma; + mac->dle_info.c1_rx_qta = cfg->ple_min_qt->cma1_dma; + +return cfg; +} + +static inline u32 dle_used_size (struct rtw89_dle_size *wde, +struct rtw89_dle_size *ple) +{ +return wde->pge_size * (wde->lnk_pge_num + wde->unlnk_pge_num) + + ple->pge_size * (ple->lnk_pge_num + ple->unlnk_pge_num); +} + +static void dle_func_en (struct rtw89_dev *rtwdev,bool enable) +{ +if (enable) +rtw89_write32_set (rtwdev,R_AX_DMAC_FUNC_EN , +B_AX_DLE_WDE_EN |B_AX_DLE_PLE_EN ); +else +rtw89_write32_clr (rtwdev,R_AX_DMAC_FUNC_EN , +B_AX_DLE_WDE_EN |B_AX_DLE_PLE_EN ); +} + +static void dle_clk_en (struct rtw89_dev *rtwdev,bool enable) +{ +if (enable) +rtw89_write32_set (rtwdev,R_AX_DMAC_CLK_EN , +B_AX_DLE_WDE_CLK_EN |B_AX_DLE_PLE_CLK_EN ); +else +rtw89_write32_clr (rtwdev,R_AX_DMAC_CLK_EN , +B_AX_DLE_WDE_CLK_EN |B_AX_DLE_PLE_CLK_EN ); +} + +static int dle_mix_cfg (struct rtw89_dev *rtwdev,struct rtw89_dle_mem *cfg) +{ +struct rtw89_dle_size *size_cfg; +u32 val; +u8 bound =0 ; + + val =rtw89_read32 (rtwdev,R_AX_WDE_PKTBUF_CFG ); + size_cfg = cfg->wde_size; + +switch (size_cfg->pge_size) { +default : +case RTW89_WDE_PG_64 : + val =u32_replace_bits (val,S_AX_WDE_PAGE_SEL_64 , +B_AX_WDE_PAGE_SEL_MASK ); +break ; +case RTW89_WDE_PG_128 : + val =u32_replace_bits (val,S_AX_WDE_PAGE_SEL_128 , +B_AX_WDE_PAGE_SEL_MASK ); +break ; +case RTW89_WDE_PG_256 : +rtw89_err (rtwdev,"[ERR]WDE DLE doesn't support 256 byte!\n" ); +return -EINVAL ; + } + + val =u32_replace_bits (val, bound,B_AX_WDE_START_BOUND_MASK ); + val =u32_replace_bits (val, size_cfg->lnk_pge_num, +B_AX_WDE_FREE_PAGE_NUM_MASK ); +rtw89_write32 (rtwdev,R_AX_WDE_PKTBUF_CFG , val); + + val =rtw89_read32 (rtwdev,R_AX_PLE_PKTBUF_CFG ); + bound = (size_cfg->lnk_pge_num + size_cfg->unlnk_pge_num) + * size_cfg->pge_size /DLE_BOUND_UNIT ; + size_cfg = cfg->ple_size; + +switch (size_cfg->pge_size) { +default : +case RTW89_PLE_PG_64 : +rtw89_err (rtwdev,"[ERR]PLE DLE doesn't support 64 byte!\n" ); +return -EINVAL ; +case RTW89_PLE_PG_128 : + val =u32_replace_bits (val,S_AX_PLE_PAGE_SEL_128 , +B_AX_PLE_PAGE_SEL_MASK ); +break ; +case RTW89_PLE_PG_256 : + val =u32_replace_bits (val,S_AX_PLE_PAGE_SEL_256 , +B_AX_PLE_PAGE_SEL_MASK ); +break ; + } + + val =u32_replace_bits (val, bound,B_AX_PLE_START_BOUND_MASK ); + val =u32_replace_bits (val, size_cfg->lnk_pge_num, +B_AX_PLE_FREE_PAGE_NUM_MASK ); +rtw89_write32 (rtwdev,R_AX_PLE_PKTBUF_CFG , val); + +return 0 ; +} + +#define SET_QUOTA (_x, _module, _idx) \ + do { \ + val = (min_cfg->_x & \ + B_AX_ ## _module ## _MIN_SIZE_MASK) | \ + ((max_cfg->_x << 16) & \ + B_AX_ ## _module ## _MAX_SIZE_MASK); \ + rtw89_write32(rtwdev, \ + R_AX_ ## _module ## _QTA ## _idx ## _CFG, \ + val); \ + } while (0) + +static void wde_quota_cfg (struct rtw89_dev *rtwdev, +struct rtw89_wde_quota *min_cfg, +struct rtw89_wde_quota *max_cfg) +{ +u32 val; + +SET_QUOTA (hif,WDE ,0 ); +SET_QUOTA (wcpu,WDE ,1 ); +SET_QUOTA (pkt_in,WDE ,3 ); +SET_QUOTA (cpu_io,WDE ,4 ); +} + +static void ple_quota_cfg (struct rtw89_dev *rtwdev, +struct rtw89_ple_quota *min_cfg, +struct rtw89_ple_quota *max_cfg) +{ +u32 val; + +SET_QUOTA (cma0_tx,PLE ,0 ); +SET_QUOTA (cma1_tx,PLE ,1 ); +SET_QUOTA (c2h,PLE ,2 ); +SET_QUOTA (h2c,PLE ,3 ); +SET_QUOTA (wcpu,PLE ,4 ); +SET_QUOTA (mpdu_proc,PLE ,5 ); +SET_QUOTA (cma0_dma,PLE ,6 ); +SET_QUOTA (cma1_dma,PLE ,7 ); +SET_QUOTA (bb_rpt,PLE ,8 ); +SET_QUOTA (wd_rel,PLE ,9 ); +SET_QUOTA (cpu_io,PLE ,10 ); +} + +#undef SET_QUOTA + +static void dle_quota_cfg (struct rtw89_dev *rtwdev,struct rtw89_dle_mem *cfg) +{ +wde_quota_cfg (rtwdev, cfg->wde_min_qt, cfg->wde_max_qt); +ple_quota_cfg (rtwdev, cfg->ple_min_qt, cfg->ple_max_qt); +} + +static int dle_init (struct rtw89_dev *rtwdev,enum rtw89_qta_mode mode, +enum rtw89_qta_mode ext_mode) +{ +struct rtw89_dle_mem *cfg, *ext_cfg; +int ret =0 ; +u32 ini; + + ret =rtw89_mac_check_mac_en (rtwdev,RTW89_MAC_0 ,RTW89_DMAC_SEL ); +if (ret) +return ret; + + cfg =get_dle_mem_cfg (rtwdev, mode); +if (!cfg) { +rtw89_err (rtwdev,"[ERR]get_dle_mem_cfg\n" ); + ret = -EINVAL ; + goto error; + } + +if (mode ==RTW89_QTA_DLFW ) { + ext_cfg =get_dle_mem_cfg (rtwdev, ext_mode); +if (!ext_cfg) { +rtw89_err (rtwdev,"[ERR]get_dle_ext_mem_cfg %d\n" , + ext_mode); + ret = -EINVAL ; + goto error; + } + cfg->wde_min_qt->wcpu = ext_cfg->wde_min_qt->wcpu; + } + +if (dle_used_size (cfg->wde_size, cfg->ple_size) != rtwdev->chip->fifo_size) { +rtw89_err (rtwdev,"[ERR]wd/dle mem cfg\n" ); + ret = -EINVAL ; + goto error; + } + +dle_func_en (rtwdev, false); +dle_clk_en (rtwdev, true); + + ret =dle_mix_cfg (rtwdev, cfg); +if (ret) { +rtw89_err (rtwdev,"[ERR] dle mix cfg\n" ); + goto error; + } +dle_quota_cfg (rtwdev, cfg); + +dle_func_en (rtwdev, true); + + ret =read_poll_timeout (rtw89_read32, ini, + (ini &WDE_MGN_INI_RDY ) ==WDE_MGN_INI_RDY ,1 , +2000 , false, rtwdev,R_AX_WDE_INI_STATUS ); +if (ret) { +rtw89_err (rtwdev,"[ERR]WDE cfg ready\n" ); +return ret; + } + + ret =read_poll_timeout (rtw89_read32, ini, + (ini &WDE_MGN_INI_RDY ) ==WDE_MGN_INI_RDY ,1 , +2000 , false, rtwdev,R_AX_PLE_INI_STATUS ); +if (ret) { +rtw89_err (rtwdev,"[ERR]PLE cfg ready\n" ); +return ret; + } + +return 0 ; +error: +dle_func_en (rtwdev, false); +rtw89_err (rtwdev,"[ERR]trxcfg wde 0x8900 = %x\n" , +rtw89_read32 (rtwdev,R_AX_WDE_INI_STATUS )); +rtw89_err (rtwdev,"[ERR]trxcfg ple 0x8D00 = %x\n" , +rtw89_read32 (rtwdev,R_AX_PLE_INI_STATUS )); + +return ret; +} + +static bool dle_is_txq_empty (struct rtw89_dev *rtwdev) +{ +u32 msk32; +u32 val32; + + msk32 =B_AX_WDE_EMPTY_QUE_CMAC0_ALL_AC |B_AX_WDE_EMPTY_QUE_CMAC0_MBH | +B_AX_WDE_EMPTY_QUE_CMAC1_MBH |B_AX_WDE_EMPTY_QUE_CMAC0_WMM0 | +B_AX_WDE_EMPTY_QUE_CMAC0_WMM1 |B_AX_WDE_EMPTY_QUE_OTHERS | +B_AX_PLE_EMPTY_QUE_DMAC_MPDU_TX |B_AX_PLE_EMPTY_QTA_DMAC_H2C | +B_AX_PLE_EMPTY_QUE_DMAC_SEC_TX |B_AX_WDE_EMPTY_QUE_DMAC_PKTIN | +B_AX_WDE_EMPTY_QTA_DMAC_HIF |B_AX_WDE_EMPTY_QTA_DMAC_WLAN_CPU | +B_AX_WDE_EMPTY_QTA_DMAC_PKTIN |B_AX_WDE_EMPTY_QTA_DMAC_CPUIO | +B_AX_PLE_EMPTY_QTA_DMAC_B0_TXPL | +B_AX_PLE_EMPTY_QTA_DMAC_B1_TXPL | +B_AX_PLE_EMPTY_QTA_DMAC_MPDU_TX | +B_AX_PLE_EMPTY_QTA_DMAC_CPUIO | +B_AX_WDE_EMPTY_QTA_DMAC_DATA_CPU | +B_AX_PLE_EMPTY_QTA_DMAC_WLAN_CPU ; + val32 =rtw89_read32 (rtwdev,R_AX_DLE_EMPTY0 ); + +if ((val32 & msk32) == msk32) +return true; + +return false; +} + +static int sta_sch_init (struct rtw89_dev *rtwdev) +{ +u32 p_val; +u8 val; +int ret; + + ret =rtw89_mac_check_mac_en (rtwdev,RTW89_MAC_0 ,RTW89_DMAC_SEL ); +if (ret) +return ret; + + val =rtw89_read8 (rtwdev,R_AX_SS_CTRL ); + val |=B_AX_SS_EN ; +rtw89_write8 (rtwdev,R_AX_SS_CTRL , val); + + ret =read_poll_timeout (rtw89_read32, p_val, p_val &B_AX_SS_INIT_DONE_1 , +1 ,TRXCFG_WAIT_CNT , false, rtwdev,R_AX_SS_CTRL ); +if (ret) { +rtw89_err (rtwdev,"[ERR]STA scheduler init\n" ); +return ret; + } + +rtw89_write32_set (rtwdev,R_AX_SS_CTRL ,B_AX_SS_WARM_INIT_FLG ); + +return 0 ; +} + +static int mpdu_proc_init (struct rtw89_dev *rtwdev) +{ +int ret; + + ret =rtw89_mac_check_mac_en (rtwdev,RTW89_MAC_0 ,RTW89_DMAC_SEL ); +if (ret) +return ret; + +rtw89_write32 (rtwdev,R_AX_ACTION_FWD0 ,TRXCFG_MPDU_PROC_ACT_FRWD ); +rtw89_write32 (rtwdev,R_AX_TF_FWD ,TRXCFG_MPDU_PROC_TF_FRWD ); +rtw89_write32_set (rtwdev,R_AX_MPDU_PROC , +B_AX_APPEND_FCS |B_AX_A_ICV_ERR ); +rtw89_write32 (rtwdev,R_AX_CUT_AMSDU_CTRL ,TRXCFG_MPDU_PROC_CUT_CTRL ); + +return 0 ; +} + +static int sec_eng_init (struct rtw89_dev *rtwdev) +{ +u32 val =0 ; +int ret; + + ret =rtw89_mac_check_mac_en (rtwdev,RTW89_MAC_0 ,RTW89_DMAC_SEL ); +if (ret) +return ret; + + val =rtw89_read32 (rtwdev,R_AX_SEC_ENG_CTRL ); +/* init clock */ + val |= (B_AX_CLK_EN_CGCMP |B_AX_CLK_EN_WAPI |B_AX_CLK_EN_WEP_TKIP ); +/* init TX encryption */ + val |= (B_AX_SEC_TX_ENC |B_AX_SEC_RX_DEC ); + val |= (B_AX_MC_DEC |B_AX_BC_DEC ); + val &= ~B_AX_TX_PARTIAL_MODE ; +rtw89_write32 (rtwdev,R_AX_SEC_ENG_CTRL , val); + +/* init MIC ICV append */ + val =rtw89_read32 (rtwdev,R_AX_SEC_MPDU_PROC ); + val |= (B_AX_APPEND_ICV |B_AX_APPEND_MIC ); + +/* option init */ +rtw89_write32 (rtwdev,R_AX_SEC_MPDU_PROC , val); + +return 0 ; +} + +static int dmac_init (struct rtw89_dev *rtwdev,u8 mac_idx) +{ +int ret; + + ret =dle_init (rtwdev, rtwdev->mac.qta_mode,RTW89_QTA_INVALID ); +if (ret) { +rtw89_err (rtwdev,"[ERR]DLE init %d\n" , ret); +return ret; + } + + ret =hfc_init (rtwdev, true, true, true); +if (ret) { +rtw89_err (rtwdev,"[ERR]HCI FC init %d\n" , ret); +return ret; + } + + ret =sta_sch_init (rtwdev); +if (ret) { +rtw89_err (rtwdev,"[ERR]STA SCH init %d\n" , ret); +return ret; + } + + ret =mpdu_proc_init (rtwdev); +if (ret) { +rtw89_err (rtwdev,"[ERR]MPDU Proc init %d\n" , ret); +return ret; + } + + ret =sec_eng_init (rtwdev); +if (ret) { +rtw89_err (rtwdev,"[ERR]Security Engine init %d\n" , ret); +return ret; + } + +return ret; +} + +static int addr_cam_init (struct rtw89_dev *rtwdev,u8 mac_idx) +{ +u32 val, reg; +u16 p_val; +int ret; + + ret =rtw89_mac_check_mac_en (rtwdev, mac_idx,RTW89_CMAC_SEL ); +if (ret) +return ret; + + reg =rtw89_mac_reg_by_idx (R_AX_ADDR_CAM_CTRL , mac_idx); + + val =rtw89_read32 (rtwdev, reg); + val |=u32_encode_bits (0x7f ,B_AX_ADDR_CAM_RANGE_MASK ) | +B_AX_ADDR_CAM_CLR |B_AX_ADDR_CAM_EN ; +rtw89_write32 (rtwdev, reg, val); + + ret =read_poll_timeout (rtw89_read16, p_val, !(p_val &B_AX_ADDR_CAM_CLR ), +1 ,TRXCFG_WAIT_CNT , false, rtwdev,B_AX_ADDR_CAM_CLR ); +if (ret) { +rtw89_err (rtwdev,"[ERR]ADDR_CAM reset\n" ); +return ret; + } + +return 0 ; +} + +static int scheduler_init (struct rtw89_dev *rtwdev,u8 mac_idx) +{ +u32 ret; +u32 reg; + + ret =rtw89_mac_check_mac_en (rtwdev, mac_idx,RTW89_CMAC_SEL ); +if (ret) +return ret; + + reg =rtw89_mac_reg_by_idx (R_AX_PREBKF_CFG_0 , mac_idx); +rtw89_write32_mask (rtwdev, reg,B_AX_PREBKF_TIME_MASK ,SCH_PREBKF_24US ); + +return 0 ; +} + +static int rtw89_mac_typ_fltr_opt (struct rtw89_dev *rtwdev, +enum rtw89_machdr_frame_type type, +enum rtw89_mac_fwd_target fwd_target, +u8 mac_idx) +{ +u32 reg; +u32 val; + +switch (fwd_target) { +case RTW89_FWD_DONT_CARE : + val =RX_FLTR_FRAME_DROP ; +break ; +case RTW89_FWD_TO_HOST : + val =RX_FLTR_FRAME_TO_HOST ; +break ; +case RTW89_FWD_TO_WLAN_CPU : + val =RX_FLTR_FRAME_TO_WLCPU ; +break ; +default : +rtw89_err (rtwdev,"[ERR]set rx filter fwd target err\n" ); +return -EINVAL ; + } + +switch (type) { +case RTW89_MGNT : + reg =rtw89_mac_reg_by_idx (R_AX_MGNT_FLTR , mac_idx); +break ; +case RTW89_CTRL : + reg =rtw89_mac_reg_by_idx (R_AX_CTRL_FLTR , mac_idx); +break ; +case RTW89_DATA : + reg =rtw89_mac_reg_by_idx (R_AX_DATA_FLTR , mac_idx); +break ; +default : +rtw89_err (rtwdev,"[ERR]set rx filter type err\n" ); +return -EINVAL ; + } +rtw89_write32 (rtwdev, reg, val); + +return 0 ; +} + +static int rx_fltr_init (struct rtw89_dev *rtwdev,u8 mac_idx) +{ +int ret, i; +u32 mac_ftlr, plcp_ftlr; + + ret =rtw89_mac_check_mac_en (rtwdev, mac_idx,RTW89_CMAC_SEL ); +if (ret) +return ret; + +for (i =RTW89_MGNT ; i <=RTW89_DATA ; i++) { + ret =rtw89_mac_typ_fltr_opt (rtwdev, i,RTW89_FWD_TO_HOST , + mac_idx); +if (ret) +return ret; + } + mac_ftlr = rtwdev->hal.rx_fltr; + plcp_ftlr =B_AX_CCK_CRC_CHK |B_AX_CCK_SIG_CHK | +B_AX_LSIG_PARITY_CHK_EN |B_AX_SIGA_CRC_CHK | +B_AX_VHT_SU_SIGB_CRC_CHK |B_AX_VHT_MU_SIGB_CRC_CHK | +B_AX_HE_SIGB_CRC_CHK ; +rtw89_write32 (rtwdev,rtw89_mac_reg_by_idx (R_AX_RX_FLTR_OPT , mac_idx), + mac_ftlr); +rtw89_write16 (rtwdev,rtw89_mac_reg_by_idx (R_AX_PLCP_HDR_FLTR , mac_idx), + plcp_ftlr); + +return 0 ; +} + +static void _patch_dis_resp_chk (struct rtw89_dev *rtwdev,u8 mac_idx) +{ +u32 reg, val32; +u32 b_rsp_chk_nav, b_rsp_chk_cca; + + b_rsp_chk_nav =B_AX_RSP_CHK_TXNAV |B_AX_RSP_CHK_INTRA_NAV | +B_AX_RSP_CHK_BASIC_NAV ; + b_rsp_chk_cca =B_AX_RSP_CHK_SEC_CCA_80 |B_AX_RSP_CHK_SEC_CCA_40 | +B_AX_RSP_CHK_SEC_CCA_20 |B_AX_RSP_CHK_BTCCA | +B_AX_RSP_CHK_EDCCA |B_AX_RSP_CHK_CCA ; + +switch (rtwdev->chip->chip_id) { +case RTL8852A : +case RTL8852B : + reg =rtw89_mac_reg_by_idx (R_AX_RSP_CHK_SIG , mac_idx); + val32 =rtw89_read32 (rtwdev, reg) & ~b_rsp_chk_nav; +rtw89_write32 (rtwdev, reg, val32); + + reg =rtw89_mac_reg_by_idx (R_AX_TRXPTCL_RESP_0 , mac_idx); + val32 =rtw89_read32 (rtwdev, reg) & ~b_rsp_chk_cca; +rtw89_write32 (rtwdev, reg, val32); +break ; +default : + reg =rtw89_mac_reg_by_idx (R_AX_RSP_CHK_SIG , mac_idx); + val32 =rtw89_read32 (rtwdev, reg) | b_rsp_chk_nav; +rtw89_write32 (rtwdev, reg, val32); + + reg =rtw89_mac_reg_by_idx (R_AX_TRXPTCL_RESP_0 , mac_idx); + val32 =rtw89_read32 (rtwdev, reg) | b_rsp_chk_cca; +rtw89_write32 (rtwdev, reg, val32); +break ; + } +} + +static int cca_ctrl_init (struct rtw89_dev *rtwdev,u8 mac_idx) +{ +u32 val, reg; +int ret; + + ret =rtw89_mac_check_mac_en (rtwdev, mac_idx,RTW89_CMAC_SEL ); +if (ret) +return ret; + + reg =rtw89_mac_reg_by_idx (R_AX_CCA_CONTROL , mac_idx); + val =rtw89_read32 (rtwdev, reg); + val |= (B_AX_TB_CHK_BASIC_NAV |B_AX_TB_CHK_BTCCA | +B_AX_TB_CHK_EDCCA |B_AX_TB_CHK_CCA_P20 | +B_AX_SIFS_CHK_BTCCA |B_AX_SIFS_CHK_CCA_P20 | +B_AX_CTN_CHK_INTRA_NAV | +B_AX_CTN_CHK_BASIC_NAV |B_AX_CTN_CHK_BTCCA | +B_AX_CTN_CHK_EDCCA |B_AX_CTN_CHK_CCA_S80 | +B_AX_CTN_CHK_CCA_S40 |B_AX_CTN_CHK_CCA_S20 | +B_AX_CTN_CHK_CCA_P20 |B_AX_SIFS_CHK_EDCCA ); + val &= ~(B_AX_TB_CHK_TX_NAV |B_AX_TB_CHK_CCA_S80 | +B_AX_TB_CHK_CCA_S40 |B_AX_TB_CHK_CCA_S20 | +B_AX_SIFS_CHK_CCA_S80 |B_AX_SIFS_CHK_CCA_S40 | +B_AX_SIFS_CHK_CCA_S20 |B_AX_CTN_CHK_TXNAV ); + +rtw89_write32 (rtwdev, reg, val); + +_patch_dis_resp_chk (rtwdev, mac_idx); + +return 0 ; +} + +static int spatial_reuse_init (struct rtw89_dev *rtwdev,u8 mac_idx) +{ +u32 reg; +int ret; + + ret =rtw89_mac_check_mac_en (rtwdev, mac_idx,RTW89_CMAC_SEL ); +if (ret) +return ret; + reg =rtw89_mac_reg_by_idx (R_AX_RX_SR_CTRL , mac_idx); +rtw89_write8_clr (rtwdev, reg,B_AX_SR_EN ); + +return 0 ; +} + +static int tmac_init (struct rtw89_dev *rtwdev,u8 mac_idx) +{ +u32 reg; +int ret; + + ret =rtw89_mac_check_mac_en (rtwdev, mac_idx,RTW89_CMAC_SEL ); +if (ret) +return ret; + + reg =rtw89_mac_reg_by_idx (R_AX_MAC_LOOPBACK , mac_idx); +rtw89_write32_clr (rtwdev, reg,B_AX_MACLBK_EN ); + +return 0 ; +} + +static int trxptcl_init (struct rtw89_dev *rtwdev,u8 mac_idx) +{ +u32 reg, val, sifs; +int ret; + + ret =rtw89_mac_check_mac_en (rtwdev, mac_idx,RTW89_CMAC_SEL ); +if (ret) +return ret; + + reg =rtw89_mac_reg_by_idx (R_AX_TRXPTCL_RESP_0 , mac_idx); + val =rtw89_read32 (rtwdev, reg); + val &= ~B_AX_WMAC_SPEC_SIFS_CCK_MASK ; + val |=FIELD_PREP (B_AX_WMAC_SPEC_SIFS_CCK_MASK ,WMAC_SPEC_SIFS_CCK ); + +switch (rtwdev->chip->chip_id) { +case RTL8852A : + sifs =WMAC_SPEC_SIFS_OFDM_52A ; +break ; +case RTL8852B : + sifs =WMAC_SPEC_SIFS_OFDM_52B ; +break ; +default : + sifs =WMAC_SPEC_SIFS_OFDM_52C ; +break ; + } + val &= ~B_AX_WMAC_SPEC_SIFS_OFDM_MASK ; + val |=FIELD_PREP (B_AX_WMAC_SPEC_SIFS_OFDM_MASK , sifs); +rtw89_write32 (rtwdev, reg, val); + + reg =rtw89_mac_reg_by_idx (R_AX_RXTRIG_TEST_USER_2 , mac_idx); +rtw89_write32_set (rtwdev, reg,B_AX_RXTRIG_FCSCHK_EN ); + +return 0 ; +} + +static int rmac_init (struct rtw89_dev *rtwdev,u8 mac_idx) +{ +#define TRXCFG_RMAC_CCA_TO 32 +#define TRXCFG_RMAC_DATA_TO 15 +#define RX_MAX_LEN_UNIT 512 +#define PLD_RLS_MAX_PG 127 +int ret; +u32 reg, rx_max_len, rx_qta; +u16 val; + + ret =rtw89_mac_check_mac_en (rtwdev, mac_idx,RTW89_CMAC_SEL ); +if (ret) +return ret; + + reg =rtw89_mac_reg_by_idx (R_AX_RESPBA_CAM_CTRL , mac_idx); +rtw89_write8_set (rtwdev, reg,B_AX_SSN_SEL ); + + reg =rtw89_mac_reg_by_idx (R_AX_DLK_PROTECT_CTL , mac_idx); + val =rtw89_read16 (rtwdev, reg); + val =u16_replace_bits (val,TRXCFG_RMAC_DATA_TO , +B_AX_RX_DLK_DATA_TIME_MASK ); + val =u16_replace_bits (val,TRXCFG_RMAC_CCA_TO , +B_AX_RX_DLK_CCA_TIME_MASK ); +rtw89_write16 (rtwdev, reg, val); + + reg =rtw89_mac_reg_by_idx (R_AX_RCR , mac_idx); +rtw89_write8_mask (rtwdev, reg,B_AX_CH_EN_MASK ,0x1 ); + + reg =rtw89_mac_reg_by_idx (R_AX_RX_FLTR_OPT , mac_idx); +if (mac_idx ==RTW89_MAC_0 ) + rx_qta = rtwdev->mac.dle_info.c0_rx_qta; +else + rx_qta = rtwdev->mac.dle_info.c1_rx_qta; + rx_qta = rx_qta >PLD_RLS_MAX_PG ?PLD_RLS_MAX_PG : rx_qta; + rx_max_len = (rx_qta -1 ) * rtwdev->mac.dle_info.ple_pg_size / +RX_MAX_LEN_UNIT ; + rx_max_len = rx_max_len >B_AX_RX_MPDU_MAX_LEN_SIZE ? +B_AX_RX_MPDU_MAX_LEN_SIZE : rx_max_len; +rtw89_write32_mask (rtwdev, reg,B_AX_RX_MPDU_MAX_LEN_MASK , rx_max_len); + +if (rtwdev->chip->chip_id ==RTL8852A && + rtwdev->hal.cv ==CHIP_CBV ) { +rtw89_write16_mask (rtwdev, +rtw89_mac_reg_by_idx (R_AX_DLK_PROTECT_CTL , mac_idx), +B_AX_RX_DLK_CCA_TIME_MASK ,0 ); +rtw89_write16_set (rtwdev,rtw89_mac_reg_by_idx (R_AX_RCR , mac_idx), +BIT (12 )); + } + + reg =rtw89_mac_reg_by_idx (R_AX_PLCP_HDR_FLTR , mac_idx); +rtw89_write8_clr (rtwdev, reg,B_AX_VHT_SU_SIGB_CRC_CHK ); + +return ret; +} + +static int cmac_com_init (struct rtw89_dev *rtwdev,u8 mac_idx) +{ +u32 val, reg; +int ret; + + ret =rtw89_mac_check_mac_en (rtwdev, mac_idx,RTW89_CMAC_SEL ); +if (ret) +return ret; + + reg =rtw89_mac_reg_by_idx (R_AX_TX_SUB_CARRIER_VALUE , mac_idx); + val =rtw89_read32 (rtwdev, reg); + val =u32_replace_bits (val,0 ,B_AX_TXSC_20M_MASK ); + val =u32_replace_bits (val,0 ,B_AX_TXSC_40M_MASK ); + val =u32_replace_bits (val,0 ,B_AX_TXSC_80M_MASK ); +rtw89_write32 (rtwdev, reg, val); + +return 0 ; +} + +static bool is_qta_dbcc (struct rtw89_dev *rtwdev,enum rtw89_qta_mode mode) +{ +struct rtw89_dle_mem *cfg; + + cfg =get_dle_mem_cfg (rtwdev, mode); +if (!cfg) { +rtw89_err (rtwdev,"[ERR]get_dle_mem_cfg\n" ); +return false; + } + +return (cfg->ple_min_qt->cma1_dma && cfg->ple_max_qt->cma1_dma); +} + +static int ptcl_init (struct rtw89_dev *rtwdev,u8 mac_idx) +{ +u32 val, reg; +int ret; + + ret =rtw89_mac_check_mac_en (rtwdev, mac_idx,RTW89_CMAC_SEL ); +if (ret) +return ret; + +if (rtwdev->hci.type ==RTW89_HCI_TYPE_PCIE ) { + reg =rtw89_mac_reg_by_idx (R_AX_SIFS_SETTING , mac_idx); + val =rtw89_read32 (rtwdev, reg); + val =u32_replace_bits (val,S_AX_CTS2S_TH_1K , +B_AX_HW_CTS2SELF_PKT_LEN_TH_MASK ); + val |=B_AX_HW_CTS2SELF_EN ; +rtw89_write32 (rtwdev, reg, val); + + reg =rtw89_mac_reg_by_idx (R_AX_PTCL_FSM_MON , mac_idx); + val =rtw89_read32 (rtwdev, reg); + val =u32_replace_bits (val,S_AX_PTCL_TO_2MS ,B_AX_PTCL_TX_ARB_TO_THR_MASK ); + val &= ~B_AX_PTCL_TX_ARB_TO_MODE ; +rtw89_write32 (rtwdev, reg, val); + } + + reg =rtw89_mac_reg_by_idx (R_AX_SIFS_SETTING , mac_idx); + val =rtw89_read32 (rtwdev, reg); + val =u32_replace_bits (val,S_AX_CTS2S_TH_SEC_256B ,B_AX_HW_CTS2SELF_PKT_LEN_TH_TWW_MASK ); + val |=B_AX_HW_CTS2SELF_EN ; +rtw89_write32 (rtwdev, reg, val); + +return 0 ; +} + +static int cmac_init (struct rtw89_dev *rtwdev,u8 mac_idx) +{ +int ret; + + ret =scheduler_init (rtwdev, mac_idx); +if (ret) { +rtw89_err (rtwdev,"[ERR]CMAC%d SCH init %d\n" , mac_idx, ret); +return ret; + } + + ret =addr_cam_init (rtwdev, mac_idx); +if (ret) { +rtw89_err (rtwdev,"[ERR]CMAC%d ADDR_CAM reset %d\n" , mac_idx, + ret); +return ret; + } + + ret =rx_fltr_init (rtwdev, mac_idx); +if (ret) { +rtw89_err (rtwdev,"[ERR]CMAC%d RX filter init %d\n" , mac_idx, + ret); +return ret; + } + + ret =cca_ctrl_init (rtwdev, mac_idx); +if (ret) { +rtw89_err (rtwdev,"[ERR]CMAC%d CCA CTRL init %d\n" , mac_idx, + ret); +return ret; + } + + ret =spatial_reuse_init (rtwdev, mac_idx); +if (ret) { +rtw89_err (rtwdev,"[ERR]CMAC%d Spatial Reuse init %d\n" , + mac_idx, ret); +return ret; + } + + ret =tmac_init (rtwdev, mac_idx); +if (ret) { +rtw89_err (rtwdev,"[ERR]CMAC%d TMAC init %d\n" , mac_idx, ret); +return ret; + } + + ret =trxptcl_init (rtwdev, mac_idx); +if (ret) { +rtw89_err (rtwdev,"[ERR]CMAC%d TRXPTCL init %d\n" , mac_idx, ret); +return ret; + } + + ret =rmac_init (rtwdev, mac_idx); +if (ret) { +rtw89_err (rtwdev,"[ERR]CMAC%d RMAC init %d\n" , mac_idx, ret); +return ret; + } + + ret =cmac_com_init (rtwdev, mac_idx); +if (ret) { +rtw89_err (rtwdev,"[ERR]CMAC%d Com init %d\n" , mac_idx, ret); +return ret; + } + + ret =ptcl_init (rtwdev, mac_idx); +if (ret) { +rtw89_err (rtwdev,"[ERR]CMAC%d PTCL init %d\n" , mac_idx, ret); +return ret; + } + +return ret; +} + +static int rtw89_mac_read_phycap (struct rtw89_dev *rtwdev, +struct rtw89_mac_c2h_info *c2h_info) +{ +struct rtw89_mac_h2c_info h2c_info = {0 }; +u32 ret; + + h2c_info.id =RTW89_FWCMD_H2CREG_FUNC_GET_FEATURE ; + h2c_info.content_len =0 ; + + ret =rtw89_fw_msg_reg (rtwdev, &h2c_info, c2h_info); +if (ret) +return ret; + +if (c2h_info->id !=RTW89_FWCMD_C2HREG_FUNC_PHY_CAP ) +return -EINVAL ; + +return 0 ; +} + +int rtw89_mac_setup_phycap (struct rtw89_dev *rtwdev) +{ +struct rtw89_hal *hal = &rtwdev->hal; +const struct rtw89_chip_info *chip = rtwdev->chip; +struct rtw89_mac_c2h_info c2h_info = {0 }; +struct rtw89_c2h_phy_cap *cap = + (struct rtw89_c2h_phy_cap *)&c2h_info.c2hreg[0 ]; +u32 ret; + + ret =rtw89_mac_read_phycap (rtwdev, &c2h_info); +if (ret) +return ret; + + hal->tx_nss = cap->tx_nss ? +min_t (u8, cap->tx_nss, chip->tx_nss) : chip->tx_nss; + hal->rx_nss = cap->rx_nss ? +min_t (u8, cap->rx_nss, chip->rx_nss) : chip->rx_nss; + +rtw89_debug (rtwdev,RTW89_DBG_FW , +"phycap hal/phy/chip: tx_nss=0x%x/0x%x/0x%x rx_nss=0x%x/0x%x/0x%x\n" , + hal->tx_nss, cap->tx_nss, chip->tx_nss, + hal->rx_nss, cap->rx_nss, chip->rx_nss); + +return 0 ; +} + +static int rtw89_hw_sch_tx_en_h2c (struct rtw89_dev *rtwdev,u8 band, +u16 tx_en_u16,u16 mask_u16) +{ +u32 ret; +struct rtw89_mac_c2h_info c2h_info = {0 }; +struct rtw89_mac_h2c_info h2c_info = {0 }; +struct rtw89_h2creg_sch_tx_en *h2creg = + (struct rtw89_h2creg_sch_tx_en *)h2c_info.h2creg; + + h2c_info.id =RTW89_FWCMD_H2CREG_FUNC_SCH_TX_EN ; + h2c_info.content_len =sizeof (*h2creg) -RTW89_H2CREG_HDR_LEN ; + h2creg->tx_en = tx_en_u16; + h2creg->mask = mask_u16; + h2creg->band = band; + + ret =rtw89_fw_msg_reg (rtwdev, &h2c_info, &c2h_info); +if (ret) +return ret; + +if (c2h_info.id !=RTW89_FWCMD_C2HREG_FUNC_TX_PAUSE_RPT ) +return -EINVAL ; + +return 0 ; +} + +static int rtw89_set_hw_sch_tx_en (struct rtw89_dev *rtwdev,u8 mac_idx, +u16 tx_en,u16 tx_en_mask) +{ +u32 reg =rtw89_mac_reg_by_idx (R_AX_CTN_TXEN , mac_idx); +u16 val; +int ret; + + ret =rtw89_mac_check_mac_en (rtwdev, mac_idx,RTW89_CMAC_SEL ); +if (ret) +return ret; + +if (test_bit (RTW89_FLAG_FW_RDY , rtwdev->flags)) +return rtw89_hw_sch_tx_en_h2c (rtwdev, mac_idx, + tx_en, tx_en_mask); + + val =rtw89_read16 (rtwdev, reg); + val = (val & ~tx_en_mask) | (tx_en & tx_en_mask); +rtw89_write16 (rtwdev, reg, val); + +return 0 ; +} + +int rtw89_mac_stop_sch_tx (struct rtw89_dev *rtwdev,u8 mac_idx, +u16 *tx_en,enum rtw89_sch_tx_sel sel) +{ +int ret; + + *tx_en =rtw89_read16 (rtwdev, +rtw89_mac_reg_by_idx (R_AX_CTN_TXEN , mac_idx)); + +switch (sel) { +case RTW89_SCH_TX_SEL_ALL : + ret =rtw89_set_hw_sch_tx_en (rtwdev, mac_idx,0 ,0xffff ); +if (ret) +return ret; +break ; +case RTW89_SCH_TX_SEL_HIQ : + ret =rtw89_set_hw_sch_tx_en (rtwdev, mac_idx, +0 ,B_AX_CTN_TXEN_HGQ ); +if (ret) +return ret; +break ; +case RTW89_SCH_TX_SEL_MG0 : + ret =rtw89_set_hw_sch_tx_en (rtwdev, mac_idx, +0 ,B_AX_CTN_TXEN_MGQ ); +if (ret) +return ret; +break ; +case RTW89_SCH_TX_SEL_MACID : + ret =rtw89_set_hw_sch_tx_en (rtwdev, mac_idx,0 ,0xffff ); +if (ret) +return ret; +break ; +default : +return 0 ; + } + +return 0 ; +} + +int rtw89_mac_resume_sch_tx (struct rtw89_dev *rtwdev,u8 mac_idx,u16 tx_en) +{ +int ret; + + ret =rtw89_set_hw_sch_tx_en (rtwdev, mac_idx, tx_en,0xffff ); +if (ret) +return ret; + +return 0 ; +} + +static u16 rtw89_mac_dle_buf_req (struct rtw89_dev *rtwdev,u16 buf_len, +bool wd) +{ +u32 val, reg; +int ret; + + reg = wd ?R_AX_WD_BUF_REQ :R_AX_PL_BUF_REQ ; + val = buf_len; + val |=B_AX_WD_BUF_REQ_EXEC ; +rtw89_write32 (rtwdev, reg, val); + + reg = wd ?R_AX_WD_BUF_STATUS :R_AX_PL_BUF_STATUS ; + + ret =read_poll_timeout (rtw89_read32, val, val &B_AX_WD_BUF_STAT_DONE , +1 ,2000 , false, rtwdev, reg); +if (ret) +return 0xffff ; + +return FIELD_GET (B_AX_WD_BUF_STAT_PKTID_MASK , val); +} + +static int rtw89_mac_set_cpuio (struct rtw89_dev *rtwdev, +struct rtw89_cpuio_ctrl *ctrl_para, +bool wd) +{ +u32 val, cmd_type, reg; +int ret; + + cmd_type = ctrl_para->cmd_type; + + reg = wd ?R_AX_WD_CPUQ_OP_2 :R_AX_PL_CPUQ_OP_2 ; + val =0 ; + val =u32_replace_bits (val, ctrl_para->start_pktid, +B_AX_WD_CPUQ_OP_STRT_PKTID_MASK ); + val =u32_replace_bits (val, ctrl_para->end_pktid, +B_AX_WD_CPUQ_OP_END_PKTID_MASK ); +rtw89_write32 (rtwdev, reg, val); + + reg = wd ?R_AX_WD_CPUQ_OP_1 :R_AX_PL_CPUQ_OP_1 ; + val =0 ; + val =u32_replace_bits (val, ctrl_para->src_pid, +B_AX_CPUQ_OP_SRC_PID_MASK ); + val =u32_replace_bits (val, ctrl_para->src_qid, +B_AX_CPUQ_OP_SRC_QID_MASK ); + val =u32_replace_bits (val, ctrl_para->dst_pid, +B_AX_CPUQ_OP_DST_PID_MASK ); + val =u32_replace_bits (val, ctrl_para->dst_qid, +B_AX_CPUQ_OP_DST_QID_MASK ); +rtw89_write32 (rtwdev, reg, val); + + reg = wd ?R_AX_WD_CPUQ_OP_0 :R_AX_PL_CPUQ_OP_0 ; + val =0 ; + val =u32_replace_bits (val, cmd_type, +B_AX_CPUQ_OP_CMD_TYPE_MASK ); + val =u32_replace_bits (val, ctrl_para->macid, +B_AX_CPUQ_OP_MACID_MASK ); + val =u32_replace_bits (val, ctrl_para->pkt_num, +B_AX_CPUQ_OP_PKTNUM_MASK ); + val |=B_AX_WD_CPUQ_OP_EXEC ; +rtw89_write32 (rtwdev, reg, val); + + reg = wd ?R_AX_WD_CPUQ_OP_STATUS :R_AX_PL_CPUQ_OP_STATUS ; + + ret =read_poll_timeout (rtw89_read32, val, val &B_AX_WD_CPUQ_OP_STAT_DONE , +1 ,2000 , false, rtwdev, reg); +if (ret) +return ret; + +if (cmd_type ==CPUIO_OP_CMD_GET_1ST_PID || + cmd_type ==CPUIO_OP_CMD_GET_NEXT_PID ) + ctrl_para->pktid =FIELD_GET (B_AX_WD_CPUQ_OP_PKTID_MASK , val); + +return 0 ; +} + +static int dle_quota_change (struct rtw89_dev *rtwdev,enum rtw89_qta_mode mode) +{ +struct rtw89_dle_mem *cfg; +struct rtw89_cpuio_ctrl ctrl_para = {0 }; +u16 pkt_id; +int ret; + + cfg =get_dle_mem_cfg (rtwdev, mode); +if (!cfg) { +rtw89_err (rtwdev,"[ERR]wd/dle mem cfg\n" ); +return -EINVAL ; + } + +if (dle_used_size (cfg->wde_size, cfg->ple_size) != rtwdev->chip->fifo_size) { +rtw89_err (rtwdev,"[ERR]wd/dle mem cfg\n" ); +return -EINVAL ; + } + +dle_quota_cfg (rtwdev, cfg); + + pkt_id =rtw89_mac_dle_buf_req (rtwdev,0x20 , true); +if (pkt_id ==0xffff ) { +rtw89_err (rtwdev,"[ERR]WDE DLE buf req\n" ); +return -ENOMEM ; + } + + ctrl_para.cmd_type =CPUIO_OP_CMD_ENQ_TO_HEAD ; + ctrl_para.start_pktid = pkt_id; + ctrl_para.end_pktid = pkt_id; + ctrl_para.pkt_num =0 ; + ctrl_para.dst_pid =WDE_DLE_PORT_ID_WDRLS ; + ctrl_para.dst_qid =WDE_DLE_QUEID_NO_REPORT ; + ret =rtw89_mac_set_cpuio (rtwdev, &ctrl_para, true); +if (ret) { +rtw89_err (rtwdev,"[ERR]WDE DLE enqueue to head\n" ); +return -EFAULT ; + } + + pkt_id =rtw89_mac_dle_buf_req (rtwdev,0x20 , false); +if (pkt_id ==0xffff ) { +rtw89_err (rtwdev,"[ERR]PLE DLE buf req\n" ); +return -ENOMEM ; + } + + ctrl_para.cmd_type =CPUIO_OP_CMD_ENQ_TO_HEAD ; + ctrl_para.start_pktid = pkt_id; + ctrl_para.end_pktid = pkt_id; + ctrl_para.pkt_num =0 ; + ctrl_para.dst_pid =PLE_DLE_PORT_ID_PLRLS ; + ctrl_para.dst_qid =PLE_DLE_QUEID_NO_REPORT ; + ret =rtw89_mac_set_cpuio (rtwdev, &ctrl_para, false); +if (ret) { +rtw89_err (rtwdev,"[ERR]PLE DLE enqueue to head\n" ); +return -EFAULT ; + } + +return 0 ; +} + +static int band_idle_ck_b (struct rtw89_dev *rtwdev,u8 mac_idx) +{ +int ret; +u32 reg; +u8 val; + + ret =rtw89_mac_check_mac_en (rtwdev, mac_idx,RTW89_CMAC_SEL ); +if (ret) +return ret; + + reg =rtw89_mac_reg_by_idx (R_AX_PTCL_TX_CTN_SEL , mac_idx); + + ret =read_poll_timeout (rtw89_read8, val, + (val &B_AX_PTCL_TX_ON_STAT ) ==0 , +SW_CVR_DUR_US , +SW_CVR_DUR_US *PTCL_IDLE_POLL_CNT , + false, rtwdev, reg); +if (ret) +return ret; + +return 0 ; +} + +static int band1_enable (struct rtw89_dev *rtwdev) +{ +int ret, i; +u32 sleep_bak[4 ] = {0 }; +u32 pause_bak[4 ] = {0 }; +u16 tx_en; + + ret =rtw89_mac_stop_sch_tx (rtwdev,0 , &tx_en,RTW89_SCH_TX_SEL_ALL ); +if (ret) { +rtw89_err (rtwdev,"[ERR]stop sch tx %d\n" , ret); +return ret; + } + +for (i =0 ; i <4 ; i++) { + sleep_bak[i] =rtw89_read32 (rtwdev,R_AX_MACID_SLEEP_0 + i *4 ); + pause_bak[i] =rtw89_read32 (rtwdev,R_AX_SS_MACID_PAUSE_0 + i *4 ); +rtw89_write32 (rtwdev,R_AX_MACID_SLEEP_0 + i *4 ,U32_MAX ); +rtw89_write32 (rtwdev,R_AX_SS_MACID_PAUSE_0 + i *4 ,U32_MAX ); + } + + ret =band_idle_ck_b (rtwdev,0 ); +if (ret) { +rtw89_err (rtwdev,"[ERR]tx idle poll %d\n" , ret); +return ret; + } + + ret =dle_quota_change (rtwdev, rtwdev->mac.qta_mode); +if (ret) { +rtw89_err (rtwdev,"[ERR]DLE quota change %d\n" , ret); +return ret; + } + +for (i =0 ; i <4 ; i++) { +rtw89_write32 (rtwdev,R_AX_MACID_SLEEP_0 + i *4 , sleep_bak[i]); +rtw89_write32 (rtwdev,R_AX_SS_MACID_PAUSE_0 + i *4 , pause_bak[i]); + } + + ret =rtw89_mac_resume_sch_tx (rtwdev,0 , tx_en); +if (ret) { +rtw89_err (rtwdev,"[ERR]CMAC1 resume sch tx %d\n" , ret); +return ret; + } + + ret =cmac_func_en (rtwdev,1 , true); +if (ret) { +rtw89_err (rtwdev,"[ERR]CMAC1 func en %d\n" , ret); +return ret; + } + + ret =cmac_init (rtwdev,1 ); +if (ret) { +rtw89_err (rtwdev,"[ERR]CMAC1 init %d\n" , ret); +return ret; + } + +rtw89_write32_set (rtwdev,R_AX_SYS_ISO_CTRL_EXTEND , +B_AX_R_SYM_FEN_WLBBFUN_1 |B_AX_R_SYM_FEN_WLBBGLB_1 ); + +return 0 ; +} + +static int rtw89_mac_enable_imr (struct rtw89_dev *rtwdev,u8 mac_idx, +enum rtw89_mac_hwmod_sel sel) +{ +u32 reg, val; +int ret; + + ret =rtw89_mac_check_mac_en (rtwdev, mac_idx, sel); +if (ret) { +rtw89_err (rtwdev,"MAC%d mac_idx%d is not ready\n" , + sel, mac_idx); +return ret; + } + +if (sel ==RTW89_DMAC_SEL ) { +rtw89_write32_clr (rtwdev,R_AX_TXPKTCTL_ERR_IMR_ISR , +B_AX_TXPKTCTL_USRCTL_RLSBMPLEN_ERR_INT_EN | +B_AX_TXPKTCTL_USRCTL_RDNRLSCMD_ERR_INT_EN | +B_AX_TXPKTCTL_CMDPSR_FRZTO_ERR_INT_EN ); +rtw89_write32_clr (rtwdev,R_AX_TXPKTCTL_ERR_IMR_ISR_B1 , +B_AX_TXPKTCTL_USRCTL_RLSBMPLEN_ERR_INT_EN | +B_AX_TXPKTCTL_USRCTL_RDNRLSCMD_ERR_INT_EN ); +rtw89_write32_clr (rtwdev,R_AX_HOST_DISPATCHER_ERR_IMR , +B_AX_HDT_PKT_FAIL_DBG_INT_EN | +B_AX_HDT_OFFSET_UNMATCH_INT_EN ); +rtw89_write32_clr (rtwdev,R_AX_CPU_DISPATCHER_ERR_IMR , +B_AX_CPU_SHIFT_EN_ERR_INT_EN ); +rtw89_write32_clr (rtwdev,R_AX_PLE_ERR_IMR , +B_AX_PLE_GETNPG_STRPG_ERR_INT_EN ); +rtw89_write32_clr (rtwdev,R_AX_WDRLS_ERR_IMR , +B_AX_WDRLS_PLEBREQ_TO_ERR_INT_EN ); +rtw89_write32_set (rtwdev,R_AX_HD0IMR ,B_AX_WDT_PTFM_INT_EN ); +rtw89_write32_clr (rtwdev,R_AX_TXPKTCTL_ERR_IMR_ISR , +B_AX_TXPKTCTL_USRCTL_NOINIT_ERR_INT_EN ); + }else if (sel ==RTW89_CMAC_SEL ) { + reg =rtw89_mac_reg_by_idx (R_AX_SCHEDULE_ERR_IMR , mac_idx); +rtw89_write32_clr (rtwdev, reg, +B_AX_SORT_NON_IDLE_ERR_INT_EN ); + + reg =rtw89_mac_reg_by_idx (R_AX_DLE_CTRL , mac_idx); +rtw89_write32_clr (rtwdev, reg, +B_AX_NO_RESERVE_PAGE_ERR_IMR | +B_AX_RXDATA_FSM_HANG_ERROR_IMR ); + + reg =rtw89_mac_reg_by_idx (R_AX_PTCL_IMR0 , mac_idx); + val =B_AX_F2PCMD_USER_ALLC_ERR_INT_EN | +B_AX_TX_RECORD_PKTID_ERR_INT_EN | +B_AX_FSM_TIMEOUT_ERR_INT_EN ; +rtw89_write32 (rtwdev, reg, val); + + reg =rtw89_mac_reg_by_idx (R_AX_PHYINFO_ERR_IMR , mac_idx); +rtw89_write32_set (rtwdev, reg, +B_AX_PHY_TXON_TIMEOUT_INT_EN | +B_AX_CCK_CCA_TIMEOUT_INT_EN | +B_AX_OFDM_CCA_TIMEOUT_INT_EN | +B_AX_DATA_ON_TIMEOUT_INT_EN | +B_AX_STS_ON_TIMEOUT_INT_EN | +B_AX_CSI_ON_TIMEOUT_INT_EN ); + + reg =rtw89_mac_reg_by_idx (R_AX_RMAC_ERR_ISR , mac_idx); + val =rtw89_read32 (rtwdev, reg); + val |= (B_AX_RMAC_RX_CSI_TIMEOUT_INT_EN | +B_AX_RMAC_RX_TIMEOUT_INT_EN | +B_AX_RMAC_CSI_TIMEOUT_INT_EN ); + val &= ~(B_AX_RMAC_CCA_TO_IDLE_TIMEOUT_INT_EN | +B_AX_RMAC_DATA_ON_TO_IDLE_TIMEOUT_INT_EN | +B_AX_RMAC_CCA_TIMEOUT_INT_EN | +B_AX_RMAC_DATA_ON_TIMEOUT_INT_EN ); +rtw89_write32 (rtwdev, reg, val); + }else { +return -EINVAL ; + } + +return 0 ; +} + +static int rtw89_mac_dbcc_enable (struct rtw89_dev *rtwdev,bool enable) +{ +int ret =0 ; + +if (enable) { + ret =band1_enable (rtwdev); +if (ret) { +rtw89_err (rtwdev,"[ERR] band1_enable %d\n" , ret); +return ret; + } + + ret =rtw89_mac_enable_imr (rtwdev,RTW89_MAC_1 ,RTW89_CMAC_SEL ); +if (ret) { +rtw89_err (rtwdev,"[ERR] enable CMAC1 IMR %d\n" , ret); +return ret; + } + }else { +rtw89_err (rtwdev,"[ERR] disable dbcc is not implemented not\n" ); +return -EINVAL ; + } + +return 0 ; +} + +static int set_host_rpr (struct rtw89_dev *rtwdev) +{ +if (rtwdev->hci.type ==RTW89_HCI_TYPE_PCIE ) { +rtw89_write32_mask (rtwdev,R_AX_WDRLS_CFG , +B_AX_WDRLS_MODE_MASK ,RTW89_RPR_MODE_POH ); +rtw89_write32_set (rtwdev,R_AX_RLSRPT0_CFG0 , +B_AX_RLSRPT0_FLTR_MAP_MASK ); + }else { +rtw89_write32_mask (rtwdev,R_AX_WDRLS_CFG , +B_AX_WDRLS_MODE_MASK ,RTW89_RPR_MODE_STF ); +rtw89_write32_clr (rtwdev,R_AX_RLSRPT0_CFG0 , +B_AX_RLSRPT0_FLTR_MAP_MASK ); + } + +rtw89_write32_mask (rtwdev,R_AX_RLSRPT0_CFG1 ,B_AX_RLSRPT0_AGGNUM_MASK ,30 ); +rtw89_write32_mask (rtwdev,R_AX_RLSRPT0_CFG1 ,B_AX_RLSRPT0_TO_MASK ,255 ); + +return 0 ; +} + +static int rtw89_mac_trx_init (struct rtw89_dev *rtwdev) +{ +enum rtw89_qta_mode qta_mode = rtwdev->mac.qta_mode; +int ret; + + ret =dmac_init (rtwdev,0 ); +if (ret) { +rtw89_err (rtwdev,"[ERR]DMAC init %d\n" , ret); +return ret; + } + + ret =cmac_init (rtwdev,0 ); +if (ret) { +rtw89_err (rtwdev,"[ERR]CMAC%d init %d\n" ,0 , ret); +return ret; + } + +if (is_qta_dbcc (rtwdev, qta_mode)) { + ret =rtw89_mac_dbcc_enable (rtwdev, true); +if (ret) { +rtw89_err (rtwdev,"[ERR]dbcc_enable init %d\n" , ret); +return ret; + } + } + + ret =rtw89_mac_enable_imr (rtwdev,RTW89_MAC_0 ,RTW89_DMAC_SEL ); +if (ret) { +rtw89_err (rtwdev,"[ERR] enable DMAC IMR %d\n" , ret); +return ret; + } + + ret =rtw89_mac_enable_imr (rtwdev,RTW89_MAC_0 ,RTW89_CMAC_SEL ); +if (ret) { +rtw89_err (rtwdev,"[ERR] to enable CMAC0 IMR %d\n" , ret); +return ret; + } + + ret =set_host_rpr (rtwdev); +if (ret) { +rtw89_err (rtwdev,"[ERR] set host rpr %d\n" , ret); +return ret; + } + +return 0 ; +} + +static void rtw89_mac_disable_cpu (struct rtw89_dev *rtwdev) +{ +clear_bit (RTW89_FLAG_FW_RDY , rtwdev->flags); + +rtw89_write32_clr (rtwdev,R_AX_PLATFORM_ENABLE ,B_AX_WCPU_EN ); +rtw89_write32_clr (rtwdev,R_AX_SYS_CLK_CTRL ,B_AX_CPU_CLK_EN ); +} + +static int rtw89_mac_enable_cpu (struct rtw89_dev *rtwdev,u8 boot_reason, +bool dlfw) +{ +u32 val; +int ret; + +if (rtw89_read32 (rtwdev,R_AX_PLATFORM_ENABLE ) &B_AX_WCPU_EN ) +return -EFAULT ; + +rtw89_write32 (rtwdev,R_AX_HALT_H2C_CTRL ,0 ); +rtw89_write32 (rtwdev,R_AX_HALT_C2H_CTRL ,0 ); + +rtw89_write32_set (rtwdev,R_AX_SYS_CLK_CTRL ,B_AX_CPU_CLK_EN ); + + val =rtw89_read32 (rtwdev,R_AX_WCPU_FW_CTRL ); + val &= ~(B_AX_WCPU_FWDL_EN |B_AX_H2C_PATH_RDY |B_AX_FWDL_PATH_RDY ); + val =u32_replace_bits (val,RTW89_FWDL_INITIAL_STATE , +B_AX_WCPU_FWDL_STS_MASK ); + +if (dlfw) + val |=B_AX_WCPU_FWDL_EN ; + +rtw89_write32 (rtwdev,R_AX_WCPU_FW_CTRL , val); +rtw89_write16_mask (rtwdev,R_AX_BOOT_REASON ,B_AX_BOOT_REASON_MASK , + boot_reason); +rtw89_write32_set (rtwdev,R_AX_PLATFORM_ENABLE ,B_AX_WCPU_EN ); + +if (!dlfw) { +mdelay (5 ); + + ret =rtw89_fw_check_rdy (rtwdev); +if (ret) +return ret; + } + +return 0 ; +} + +static int rtw89_mac_fw_dl_pre_init (struct rtw89_dev *rtwdev) +{ +u32 val; +int ret; + + val =B_AX_MAC_FUNC_EN |B_AX_DMAC_FUNC_EN |B_AX_DISPATCHER_EN | +B_AX_PKT_BUF_EN ; +rtw89_write32 (rtwdev,R_AX_DMAC_FUNC_EN , val); + + val =B_AX_DISPATCHER_CLK_EN ; +rtw89_write32 (rtwdev,R_AX_DMAC_CLK_EN , val); + + ret =dle_init (rtwdev,RTW89_QTA_DLFW , rtwdev->mac.qta_mode); +if (ret) { +rtw89_err (rtwdev,"[ERR]DLE pre init %d\n" , ret); +return ret; + } + + ret =hfc_init (rtwdev, true, false, true); +if (ret) { +rtw89_err (rtwdev,"[ERR]HCI FC pre init %d\n" , ret); +return ret; + } + +return ret; +} + +static void rtw89_mac_hci_func_en (struct rtw89_dev *rtwdev) +{ +rtw89_write32_set (rtwdev,R_AX_HCI_FUNC_EN , +B_AX_HCI_TXDMA_EN |B_AX_HCI_RXDMA_EN ); +} + +void rtw89_mac_enable_bb_rf (struct rtw89_dev *rtwdev) +{ +rtw89_write8_set (rtwdev,R_AX_SYS_FUNC_EN , +B_AX_FEN_BBRSTB |B_AX_FEN_BB_GLB_RSTN ); +rtw89_write32_set (rtwdev,R_AX_WLRF_CTRL , +B_AX_WLRF1_CTRL_7 |B_AX_WLRF1_CTRL_1 | +B_AX_WLRF_CTRL_7 |B_AX_WLRF_CTRL_1 ); +rtw89_write8_set (rtwdev,R_AX_PHYREG_SET ,PHYREG_SET_ALL_CYCLE ); +} + +void rtw89_mac_disable_bb_rf (struct rtw89_dev *rtwdev) +{ +rtw89_write8_clr (rtwdev,R_AX_SYS_FUNC_EN , +B_AX_FEN_BBRSTB |B_AX_FEN_BB_GLB_RSTN ); +rtw89_write32_clr (rtwdev,R_AX_WLRF_CTRL , +B_AX_WLRF1_CTRL_7 |B_AX_WLRF1_CTRL_1 | +B_AX_WLRF_CTRL_7 |B_AX_WLRF_CTRL_1 ); +rtw89_write8_clr (rtwdev,R_AX_PHYREG_SET ,PHYREG_SET_ALL_CYCLE ); +} + +int rtw89_mac_partial_init (struct rtw89_dev *rtwdev) +{ +int ret; + + ret =rtw89_mac_power_switch (rtwdev, true); +if (ret) { +rtw89_mac_power_switch (rtwdev, false); + ret =rtw89_mac_power_switch (rtwdev, true); +if (ret) +return ret; + } + +rtw89_mac_hci_func_en (rtwdev); + +if (rtwdev->hci.ops->mac_pre_init) { + ret = rtwdev->hci.ops->mac_pre_init (rtwdev); +if (ret) +return ret; + } + + ret =rtw89_mac_fw_dl_pre_init (rtwdev); +if (ret) +return ret; + +rtw89_mac_disable_cpu (rtwdev); + ret =rtw89_mac_enable_cpu (rtwdev,0 , true); +if (ret) +return ret; + + ret =rtw89_fw_download (rtwdev,RTW89_FW_NORMAL ); +if (ret) +return ret; + +return 0 ; +} + +int rtw89_mac_init (struct rtw89_dev *rtwdev) +{ +int ret; + + ret =rtw89_mac_partial_init (rtwdev); +if (ret) + goto fail; + +rtw89_mac_enable_bb_rf (rtwdev); +if (ret) + goto fail; + + ret =rtw89_mac_sys_init (rtwdev); +if (ret) + goto fail; + + ret =rtw89_mac_trx_init (rtwdev); +if (ret) + goto fail; + +if (rtwdev->hci.ops->mac_post_init) { + ret = rtwdev->hci.ops->mac_post_init (rtwdev); +if (ret) + goto fail; + } + +rtw89_fw_h2c_set_ofld_cfg (rtwdev); + +return ret; +fail: +rtw89_mac_power_switch (rtwdev, false); + +return ret; +} + +static void rtw89_mac_dmac_tbl_init (struct rtw89_dev *rtwdev,u8 macid) +{ +u8 i; + +for (i =0 ; i <4 ; i++) { +rtw89_write32 (rtwdev,R_AX_FILTER_MODEL_ADDR , +DMAC_TBL_BASE_ADDR + (macid <<4 ) + (i <<2 )); +rtw89_write32 (rtwdev,R_AX_INDIR_ACCESS_ENTRY ,0 ); + } +} + +static void rtw89_mac_cmac_tbl_init (struct rtw89_dev *rtwdev,u8 macid) +{ +rtw89_write32 (rtwdev,R_AX_FILTER_MODEL_ADDR , +CMAC_TBL_BASE_ADDR + macid *CCTL_INFO_SIZE ); +rtw89_write32 (rtwdev,R_AX_INDIR_ACCESS_ENTRY ,0x4 ); +rtw89_write32 (rtwdev,R_AX_INDIR_ACCESS_ENTRY +4 ,0x400A0004 ); +rtw89_write32 (rtwdev,R_AX_INDIR_ACCESS_ENTRY +8 ,0 ); +rtw89_write32 (rtwdev,R_AX_INDIR_ACCESS_ENTRY +12 ,0 ); +rtw89_write32 (rtwdev,R_AX_INDIR_ACCESS_ENTRY +16 ,0 ); +rtw89_write32 (rtwdev,R_AX_INDIR_ACCESS_ENTRY +20 ,0xE43000B ); +rtw89_write32 (rtwdev,R_AX_INDIR_ACCESS_ENTRY +24 ,0 ); +rtw89_write32 (rtwdev,R_AX_INDIR_ACCESS_ENTRY +28 ,0xB8109 ); +} + +static int rtw89_set_macid_pause (struct rtw89_dev *rtwdev,u8 macid,bool pause) +{ +u8 sh =FIELD_GET (GENMASK (4 ,0 ), macid); +u8 grp = macid >>5 ; +int ret; + + ret =rtw89_mac_check_mac_en (rtwdev,RTW89_MAC_0 ,RTW89_CMAC_SEL ); +if (ret) +return ret; + +rtw89_fw_h2c_macid_pause (rtwdev, sh, grp, pause); + +return 0 ; +} + +static const struct rtw89_port_reg rtw_port_base = { + .port_cfg =R_AX_PORT_CFG_P0 , + .tbtt_prohib =R_AX_TBTT_PROHIB_P0 , + .bcn_area =R_AX_BCN_AREA_P0 , + .bcn_early =R_AX_BCNERLYINT_CFG_P0 , + .tbtt_early =R_AX_TBTTERLYINT_CFG_P0 , + .tbtt_agg =R_AX_TBTT_AGG_P0 , + .bcn_space =R_AX_BCN_SPACE_CFG_P0 , + .bcn_forcetx =R_AX_BCN_FORCETX_P0 , + .bcn_err_cnt =R_AX_BCN_ERR_CNT_P0 , + .bcn_err_flag =R_AX_BCN_ERR_FLAG_P0 , + .dtim_ctrl =R_AX_DTIM_CTRL_P0 , + .tbtt_shift =R_AX_TBTT_SHIFT_P0 , + .bcn_cnt_tmr =R_AX_BCN_CNT_TMR_P0 , + .tsftr_l =R_AX_TSFTR_LOW_P0 , + .tsftr_h =R_AX_TSFTR_HIGH_P0 +}; + +#define BCN_INTERVAL 100 +#define BCN_ERLY_DEF 160 +#define BCN_SETUP_DEF 2 +#define BCN_HOLD_DEF 200 +#define BCN_MASK_DEF 0 +#define TBTT_ERLY_DEF 5 +#define BCN_SET_UNIT 32 +#define BCN_ERLY_SET_DLY (10 * 2) + +static void rtw89_mac_port_cfg_func_sw (struct rtw89_dev *rtwdev, +struct rtw89_vif *rtwvif) +{ +struct ieee80211_vif *vif =rtwvif_to_vif (rtwvif); +const struct rtw89_port_reg *p = &rtw_port_base; + +if (!rtw89_read32_port_mask (rtwdev, rtwvif, p->port_cfg,B_AX_PORT_FUNC_EN )) +return ; + +rtw89_write32_port_clr (rtwdev, rtwvif, p->tbtt_prohib,B_AX_TBTT_SETUP_MASK ); +rtw89_write32_port_mask (rtwdev, rtwvif, p->tbtt_prohib,B_AX_TBTT_HOLD_MASK ,1 ); +rtw89_write16_port_clr (rtwdev, rtwvif, p->tbtt_early,B_AX_TBTTERLY_MASK ); +rtw89_write16_port_clr (rtwdev, rtwvif, p->bcn_early,B_AX_BCNERLY_MASK ); + +msleep (vif->bss_conf.beacon_int +1 ); + +rtw89_write32_port_clr (rtwdev, rtwvif, p->port_cfg,B_AX_PORT_FUNC_EN | +B_AX_BRK_SETUP ); +rtw89_write32_port_set (rtwdev, rtwvif, p->port_cfg,B_AX_TSFTR_RST ); +rtw89_write32_port (rtwdev, rtwvif, p->bcn_cnt_tmr,0 ); +} + +static void rtw89_mac_port_cfg_tx_rpt (struct rtw89_dev *rtwdev, +struct rtw89_vif *rtwvif,bool en) +{ +const struct rtw89_port_reg *p = &rtw_port_base; + +if (en) +rtw89_write32_port_set (rtwdev, rtwvif, p->port_cfg,B_AX_TXBCN_RPT_EN ); +else +rtw89_write32_port_clr (rtwdev, rtwvif, p->port_cfg,B_AX_TXBCN_RPT_EN ); +} + +static void rtw89_mac_port_cfg_rx_rpt (struct rtw89_dev *rtwdev, +struct rtw89_vif *rtwvif,bool en) +{ +const struct rtw89_port_reg *p = &rtw_port_base; + +if (en) +rtw89_write32_port_set (rtwdev, rtwvif, p->port_cfg,B_AX_RXBCN_RPT_EN ); +else +rtw89_write32_port_clr (rtwdev, rtwvif, p->port_cfg,B_AX_RXBCN_RPT_EN ); +} + +static void rtw89_mac_port_cfg_net_type (struct rtw89_dev *rtwdev, +struct rtw89_vif *rtwvif) +{ +const struct rtw89_port_reg *p = &rtw_port_base; + +rtw89_write32_port_mask (rtwdev, rtwvif, p->port_cfg,B_AX_NET_TYPE_MASK , + rtwvif->net_type); +} + +static void rtw89_mac_port_cfg_bcn_prct (struct rtw89_dev *rtwdev, +struct rtw89_vif *rtwvif) +{ +const struct rtw89_port_reg *p = &rtw_port_base; +bool en = rtwvif->net_type !=RTW89_NET_TYPE_NO_LINK ; +u32 bits =B_AX_TBTT_PROHIB_EN |B_AX_BRK_SETUP ; + +if (en) +rtw89_write32_port_set (rtwdev, rtwvif, p->port_cfg, bits); +else +rtw89_write32_port_clr (rtwdev, rtwvif, p->port_cfg, bits); +} + +static void rtw89_mac_port_cfg_rx_sw (struct rtw89_dev *rtwdev, +struct rtw89_vif *rtwvif) +{ +const struct rtw89_port_reg *p = &rtw_port_base; +bool en = rtwvif->net_type ==RTW89_NET_TYPE_INFRA || + rtwvif->net_type ==RTW89_NET_TYPE_AD_HOC ; +u32 bit =B_AX_RX_BSSID_FIT_EN ; + +if (en) +rtw89_write32_port_set (rtwdev, rtwvif, p->port_cfg, bit); +else +rtw89_write32_port_clr (rtwdev, rtwvif, p->port_cfg, bit); +} + +static void rtw89_mac_port_cfg_rx_sync (struct rtw89_dev *rtwdev, +struct rtw89_vif *rtwvif) +{ +const struct rtw89_port_reg *p = &rtw_port_base; +bool en = rtwvif->net_type ==RTW89_NET_TYPE_INFRA || + rtwvif->net_type ==RTW89_NET_TYPE_AD_HOC ; + +if (en) +rtw89_write32_port_set (rtwdev, rtwvif, p->port_cfg,B_AX_TSF_UDT_EN ); +else +rtw89_write32_port_clr (rtwdev, rtwvif, p->port_cfg,B_AX_TSF_UDT_EN ); +} + +static void rtw89_mac_port_cfg_tx_sw (struct rtw89_dev *rtwdev, +struct rtw89_vif *rtwvif) +{ +const struct rtw89_port_reg *p = &rtw_port_base; +bool en = rtwvif->net_type ==RTW89_NET_TYPE_AP_MODE || + rtwvif->net_type ==RTW89_NET_TYPE_AD_HOC ; + +if (en) +rtw89_write32_port_set (rtwdev, rtwvif, p->port_cfg,B_AX_BCNTX_EN ); +else +rtw89_write32_port_clr (rtwdev, rtwvif, p->port_cfg,B_AX_BCNTX_EN ); +} + +static void rtw89_mac_port_cfg_bcn_intv (struct rtw89_dev *rtwdev, +struct rtw89_vif *rtwvif) +{ +struct ieee80211_vif *vif =rtwvif_to_vif (rtwvif); +const struct rtw89_port_reg *p = &rtw_port_base; +u16 bcn_int = vif->bss_conf.beacon_int ? vif->bss_conf.beacon_int :BCN_INTERVAL ; + +rtw89_write32_port_mask (rtwdev, rtwvif, p->bcn_space,B_AX_BCN_SPACE_MASK , + bcn_int); +} + +static void rtw89_mac_port_cfg_bcn_setup_time (struct rtw89_dev *rtwdev, +struct rtw89_vif *rtwvif) +{ +const struct rtw89_port_reg *p = &rtw_port_base; + +rtw89_write32_port_mask (rtwdev, rtwvif, p->tbtt_prohib, +B_AX_TBTT_SETUP_MASK ,BCN_SETUP_DEF ); +} + +static void rtw89_mac_port_cfg_bcn_hold_time (struct rtw89_dev *rtwdev, +struct rtw89_vif *rtwvif) +{ +const struct rtw89_port_reg *p = &rtw_port_base; + +rtw89_write32_port_mask (rtwdev, rtwvif, p->tbtt_prohib, +B_AX_TBTT_HOLD_MASK ,BCN_HOLD_DEF ); +} + +static void rtw89_mac_port_cfg_bcn_mask_area (struct rtw89_dev *rtwdev, +struct rtw89_vif *rtwvif) +{ +const struct rtw89_port_reg *p = &rtw_port_base; + +rtw89_write32_port_mask (rtwdev, rtwvif, p->bcn_area, +B_AX_BCN_MSK_AREA_MASK ,BCN_MASK_DEF ); +} + +static void rtw89_mac_port_cfg_tbtt_early (struct rtw89_dev *rtwdev, +struct rtw89_vif *rtwvif) +{ +const struct rtw89_port_reg *p = &rtw_port_base; + +rtw89_write16_port_mask (rtwdev, rtwvif, p->tbtt_early, +B_AX_TBTTERLY_MASK ,TBTT_ERLY_DEF ); +} + +static void rtw89_mac_port_cfg_bss_color (struct rtw89_dev *rtwdev, +struct rtw89_vif *rtwvif) +{ +struct ieee80211_vif *vif =rtwvif_to_vif (rtwvif); +static const u32 masks[RTW89_PORT_NUM ] = { +B_AX_BSS_COLOB_AX_PORT_0_MASK ,B_AX_BSS_COLOB_AX_PORT_1_MASK , +B_AX_BSS_COLOB_AX_PORT_2_MASK ,B_AX_BSS_COLOB_AX_PORT_3_MASK , +B_AX_BSS_COLOB_AX_PORT_4_MASK , + }; +u8 port = rtwvif->port; +u32 reg_base; +u32 reg; +u8 bss_color; + + bss_color = vif->bss_conf.he_bss_color.color; + reg_base = port >=4 ?R_AX_PTCL_BSS_COLOR_1 :R_AX_PTCL_BSS_COLOR_0 ; + reg =rtw89_mac_reg_by_idx (reg_base, rtwvif->mac_idx); +rtw89_write32_mask (rtwdev, reg, masks[port], bss_color); +} + +static void rtw89_mac_port_cfg_mbssid (struct rtw89_dev *rtwdev, +struct rtw89_vif *rtwvif) +{ +u8 port = rtwvif->port; +u32 reg; + +if (rtwvif->net_type ==RTW89_NET_TYPE_AP_MODE ) +return ; + +if (port ==0 ) { + reg =rtw89_mac_reg_by_idx (R_AX_MBSSID_CTRL , rtwvif->mac_idx); +rtw89_write32_clr (rtwdev, reg,B_AX_P0MB_ALL_MASK ); + } +} + +static void rtw89_mac_port_cfg_hiq_drop (struct rtw89_dev *rtwdev, +struct rtw89_vif *rtwvif) +{ +u8 port = rtwvif->port; +u32 reg; +u32 val; + + reg =rtw89_mac_reg_by_idx (R_AX_MBSSID_DROP_0 , rtwvif->mac_idx); + val =rtw89_read32 (rtwdev, reg); + val &= ~FIELD_PREP (B_AX_PORT_DROP_4_0_MASK ,BIT (port)); +if (port ==0 ) + val &= ~BIT (0 ); +rtw89_write32 (rtwdev, reg, val); +} + +static void rtw89_mac_port_cfg_func_en (struct rtw89_dev *rtwdev, +struct rtw89_vif *rtwvif) +{ +const struct rtw89_port_reg *p = &rtw_port_base; + +rtw89_write32_port_set (rtwdev, rtwvif, p->port_cfg,B_AX_PORT_FUNC_EN ); +} + +static void rtw89_mac_port_cfg_bcn_early (struct rtw89_dev *rtwdev, +struct rtw89_vif *rtwvif) +{ +const struct rtw89_port_reg *p = &rtw_port_base; + +rtw89_write32_port_mask (rtwdev, rtwvif, p->bcn_early,B_AX_BCNERLY_MASK , +BCN_ERLY_DEF ); +} + +int rtw89_mac_vif_init (struct rtw89_dev *rtwdev,struct rtw89_vif *rtwvif) +{ +int ret; + + ret =rtw89_mac_port_update (rtwdev, rtwvif); +if (ret) +return ret; + +rtw89_mac_dmac_tbl_init (rtwdev, rtwvif->mac_id); +rtw89_mac_cmac_tbl_init (rtwdev, rtwvif->mac_id); + + ret =rtw89_set_macid_pause (rtwdev, rtwvif->mac_id, false); +if (ret) +return ret; + + ret =rtw89_fw_h2c_vif_maintain (rtwdev, rtwvif,RTW89_VIF_CREATE ); +if (ret) +return ret; + + ret =rtw89_cam_init (rtwdev, rtwvif); +if (ret) +return ret; + + ret =rtw89_fw_h2c_cam (rtwdev, rtwvif); +if (ret) +return ret; + + ret =rtw89_fw_h2c_default_cmac_tbl (rtwdev, rtwvif->mac_id); +if (ret) +return ret;
Lines of this file are not shown. View the raw message.
drivers/net/wireless/realtek/rtw89/mac.h new file +860 -0
860 lines not on this page · raw message
The rest of this diff is not shown. View the raw message.
Signature · 1 line
2.25.1
Part of the diff is not shown here. View the raw message.
Re: [PATCH v6 07/24] rtw89: add MAC files
Quoted 31 lines · Ping-Ke Shih
Provide interfaces to access MAC function blocks including power-on/off configuration.
MAC initialization does power-on, and then enable HCI to download firmware. The BB, RF, DMAC and CMAC function blocks are enabled, and then configure TXRX settings that contains DMAC, CMAC, interrupt mask, and RPQ parameters. DMAC is short for data MAC, including data link engine (DLE), HCI function control (HFC), MPDU processor, security engine and so on. CMAC is short for control MAC, including scheduler, address CAM, RX filter, CCA control, TMAC, RMAC, protocol component and so on.
The full MAC initialization is done for normal use case when user does network interface up. When device is probing, driver does partial initialization to do power-on and download firmware, because we need to read hardware capabilities from efuse and firmware.
MAC supports five ports, so we can have five VIFs at most. To control MAC port, we control a set of registers, and the most important one among them is
port_cfgregister namedR_AX_PORT_CFG_P0. We can turn on/off the port function and configure network type (STA or AP mode) by this register. The address and BSSID corresponding to this port are given by address and BSSID CAM that is set by firmware via H2C command.Since BT coexistence code needs to access coex registers, some help functions are provided to make WiFi-only case work.
To access MAC registers, normally we use rtw89_writeXX/rtw89_readYY, but we use
rtw89_mac_txpwr_write32_ZZto access TX power register with proper address range checking.Signed-off-by Ping-Ke Shih <[email protected]>
[...]
drivers/net/wireless/realtek/rtw89/mac.c line 486
Quoted 3 lines
drivers/net/wireless/realtek/rtw89/mac.c new file +3
-0+struct rtw89_hfc_prec_cfg rtw_hfc_preccfg_pcie = { +2 ,40 ,0 ,0 ,1 ,0 ,0 ,0 +};
This should be const.
Quoted 9 lines
drivers/net/wireless/realtek/rtw89/mac.c new file +9
-0+static int rtw89_mac_pwr_seq (struct rtw89_dev *rtwdev, +const struct rtw89_pwr_cfg *const *cfg_seq) +{ +u32 idx =0 ; +const struct rtw89_pwr_cfg *cfg; +int ret; + + do { + cfg = cfg_seq[idx];
drivers/net/wireless/realtek/rtw89/mac.c line 898
Quoted 10 lines
drivers/net/wireless/realtek/rtw89/mac.c new file +10
-0+if (!cfg) +break ; + + ret =rtw89_mac_sub_pwr_seq (rtwdev,BIT (rtwdev->hal.cv), +PWR_INTF_MSK_PCIE , cfg); +if (ret) +return -EBUSY ; + + idx++; + }while (1 );
Never ending loop is a bad idea in kernel, please add either a loop limit or a time limit.
Quoted 8 lines
drivers/net/wireless/realtek/rtw89/mac.c new file +8
-0+static int rtw89_mac_power_switch (struct rtw89_dev *rtwdev,bool on) +{ +#define PWR_ACT 1 +const struct rtw89_chip_info *chip = rtwdev->chip; +const struct rtw89_pwr_cfg *const *cfg_seq; +struct rtw89_hal *hal = &rtwdev->hal; +int ret; +u8 val;
drivers/net/wireless/realtek/rtw89/mac.c line 1027
Quoted 10 lines
drivers/net/wireless/realtek/rtw89/mac.c new file +10
-0+ +if (on) + cfg_seq = chip->pwr_on_seq; +else + cfg_seq = chip->pwr_off_seq; + +if (test_bit (RTW89_FLAG_FW_RDY , rtwdev->flags)) +__rtw89_leave_ps_mode (rtwdev); + + val =rtw89_read8 (rtwdev,0x3F1 ) &0x3 ;
Magic numbers.
Quoted 34 lines
drivers/net/wireless/realtek/rtw89/mac.c new file +34
-0+/* PCIE 64 */ +struct rtw89_dle_size wde_size0 = { +RTW89_WDE_PG_64 ,4095 ,1 , +}; + +/* DLFW */ +struct rtw89_dle_size wde_size4 = { +RTW89_WDE_PG_64 ,0 ,4096 , +}; + +/* PCIE */ +struct rtw89_dle_size ple_size0 = { +RTW89_PLE_PG_128 ,1520 ,16 , +}; + +/* DLFW */ +struct rtw89_dle_size ple_size4 = { +RTW89_PLE_PG_128 ,64 ,1472 , +}; + +/* PCIE 64 */ +struct rtw89_wde_quota wde_qt0 = { +3792 ,196 ,0 ,107 , +}; + +/* DLFW */ +struct rtw89_wde_quota wde_qt4 = { +0 ,0 ,0 ,0 , +}; + +/* PCIE SCC */ +struct rtw89_ple_quota ple_qt4 = { +264 ,0 ,16 ,20 ,26 ,13 ,356 ,0 ,32 ,40 ,8 , +};
drivers/net/wireless/realtek/rtw89/mac.c line 1190
Quoted 10 lines
drivers/net/wireless/realtek/rtw89/mac.c new file +10
-0+ +/* PCIE SCC */ +struct rtw89_ple_quota ple_qt5 = { +264 ,0 ,32 ,20 ,64 ,13 ,1101 ,0 ,64 ,128 ,120 , +}; + +/* DLFW */ +struct rtw89_ple_quota ple_qt13 = { +0 ,0 ,16 ,48 ,0 ,0 ,0 ,0 ,0 ,0 ,0 +};
These all should be const.
Quoted 5 lines
drivers/net/wireless/realtek/rtw89/mac.c new file +5
-0+static void (*rtw89_mac_c2h_ofld_handler[])(struct rtw89_dev *rtwdev, +struct sk_buff *c2h,u32 len) = { + [RTW89_MAC_C2H_FUNC_EFUSE_DUMP ] =NULL , + [RTW89_MAC_C2H_FUNC_READ_RSP ] =NULL , + [RTW89_MAC_C2H_FUNC_PKT_OFLD_RSP ] =NULL ,
drivers/net/wireless/realtek/rtw89/mac.c line 3132
Quoted 10 lines
drivers/net/wireless/realtek/rtw89/mac.c new file +10
-0+ [RTW89_MAC_C2H_FUNC_BCN_RESEND] = NULL, + [RTW89_MAC_C2H_FUNC_MACID_PAUSE] = rtw89_mac_c2h_macid_pause, +}; + +static void (*rtw89_mac_c2h_info_handler[])(struct rtw89_dev *rtwdev, +struct sk_buff *c2h,u32 len) = { + [RTW89_MAC_C2H_FUNC_REC_ACK ] = rtw89_mac_c2h_rec_ack, + [RTW89_MAC_C2H_FUNC_DONE_ACK ] = rtw89_mac_c2h_done_ack, + [RTW89_MAC_C2H_FUNC_C2H_LOG ] = rtw89_mac_c2h_log, +};
const?
Signature · 3 lines
https://patchwork.kernel.org/project/linux-wireless/list/ https://wireless.wiki.kernel.org/en/developers/documentation/submittingpatches
Ping-Ke Shih
maintainerPATCH v6 8/24 rtw89: implement mac80211 ops
In reply to Ping-Ke ShihImplement ops to interactive with mac80211. The ops contain start/stop, TX, add/remove vif, config, sta state, key, ampdu action, sw_scan_start/complete, and so on. To avoid racing between ieee80211 delayed work and ioctl, all of them are protected by rtwdev->mutex. To yield better TX performance, wake TX queue is implemented.
--- drivers/net/wireless/realtek/rtw89/mac80211.c | 673 ++++++++++++++++++ 1 file changed, 673 insertions(+) create mode 100644 drivers/net/wireless/realtek/rtw89/mac80211.c
drivers/net/wireless/realtek/rtw89/mac80211.c new file +673 -0
@@ -0,0 +1,673 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* Copyright(c) 2019-2020 Realtek Corporation +*/ + +#include "cam.h" +#include "coex.h" +#include "debug.h" +#include "fw.h" +#include "mac.h" +#include "phy.h" +#include "ps.h" +#include "reg.h" +#include "sar.h" +#include "ser.h" + +static void rtw89_ops_tx (struct ieee80211_hw *hw, +struct ieee80211_tx_control *control, +struct sk_buff *skb) +{ +struct rtw89_dev *rtwdev = hw->priv; +struct ieee80211_tx_info *info =IEEE80211_SKB_CB (skb); +struct ieee80211_vif *vif = info->control.vif; +struct ieee80211_sta *sta = control->sta; +int ret, qsel; + + ret =rtw89_core_tx_write (rtwdev, vif, sta, skb, &qsel); +if (ret) { +rtw89_err (rtwdev,"failed to transmit skb: %d\n" , ret); +ieee80211_free_txskb (hw, skb); + } +rtw89_core_tx_kick_off (rtwdev, qsel); +} + +static void rtw89_ops_wake_tx_queue (struct ieee80211_hw *hw, +struct ieee80211_txq *txq) +{ +struct rtw89_dev *rtwdev = hw->priv; + +ieee80211_schedule_txq (hw, txq); +queue_work (rtwdev->txq_wq, &rtwdev->txq_work); +} + +static int rtw89_ops_start (struct ieee80211_hw *hw) +{ +struct rtw89_dev *rtwdev = hw->priv; +int ret; + +mutex_lock (&rtwdev->mutex); + ret =rtw89_core_start (rtwdev); +mutex_unlock (&rtwdev->mutex); + +return ret; +} + +static void rtw89_ops_stop (struct ieee80211_hw *hw) +{ +struct rtw89_dev *rtwdev = hw->priv; + +mutex_lock (&rtwdev->mutex); +rtw89_core_stop (rtwdev); +mutex_unlock (&rtwdev->mutex); +} + +static int rtw89_ops_config (struct ieee80211_hw *hw,u32 changed) +{ +struct rtw89_dev *rtwdev = hw->priv; + +mutex_lock (&rtwdev->mutex); +rtw89_leave_ps_mode (rtwdev); + +if ((changed &IEEE80211_CONF_CHANGE_IDLE ) && + !(hw->conf.flags &IEEE80211_CONF_IDLE )) +rtw89_leave_ips (rtwdev); + +if (changed &IEEE80211_CONF_CHANGE_PS ) { +if (hw->conf.flags &IEEE80211_CONF_PS ) { + rtwdev->lps_enabled = true; + }else { +rtw89_leave_lps (rtwdev); + rtwdev->lps_enabled = false; + } + } + +if (changed &IEEE80211_CONF_CHANGE_CHANNEL ) +rtw89_set_channel (rtwdev); + +if ((changed &IEEE80211_CONF_CHANGE_IDLE ) && + (hw->conf.flags &IEEE80211_CONF_IDLE )) +rtw89_enter_ips (rtwdev); + +mutex_unlock (&rtwdev->mutex); + +return 0 ; +} + +static int rtw89_ops_add_interface (struct ieee80211_hw *hw, +struct ieee80211_vif *vif) +{ +struct rtw89_dev *rtwdev = hw->priv; +struct rtw89_vif *rtwvif = (struct rtw89_vif *)vif->drv_priv; +int ret =0 ; + +mutex_lock (&rtwdev->mutex); +list_add_tail (&rtwvif->list, &rtwdev->rtwvifs_list); +rtw89_leave_ps_mode (rtwdev); + +rtw89_traffic_stats_init (rtwdev, &rtwvif->stats); +rtw89_vif_type_mapping (vif, false); + rtwvif->port =rtw89_core_acquire_bit_map (rtwdev->hw_port, +RTW89_MAX_HW_PORT_NUM ); +if (rtwvif->port ==RTW89_MAX_HW_PORT_NUM ) { + ret = -ENOSPC ; + goto out; + } + + rtwvif->bcn_hit_cond =0 ; + rtwvif->mac_idx =RTW89_MAC_0 ; + rtwvif->phy_idx =RTW89_PHY_0 ; + rtwvif->hit_rule =0 ; +ether_addr_copy (rtwvif->mac_addr, vif->addr); + + ret =rtw89_mac_add_vif (rtwdev, rtwvif); +if (ret) { +rtw89_core_release_bit_map (rtwdev->hw_port, rtwvif->port); + goto out; + } + +rtw89_core_txq_init (rtwdev, vif->txq); + +rtw89_btc_ntfy_role_info (rtwdev, rtwvif,NULL ,BTC_ROLE_START ); +out: +mutex_unlock (&rtwdev->mutex); + +return ret; +} + +static void rtw89_ops_remove_interface (struct ieee80211_hw *hw, +struct ieee80211_vif *vif) +{ +struct rtw89_dev *rtwdev = hw->priv; +struct rtw89_vif *rtwvif = (struct rtw89_vif *)vif->drv_priv; + +mutex_lock (&rtwdev->mutex); +rtw89_leave_ps_mode (rtwdev); +rtw89_btc_ntfy_role_info (rtwdev, rtwvif,NULL ,BTC_ROLE_STOP ); +rtw89_mac_remove_vif (rtwdev, rtwvif); +rtw89_core_release_bit_map (rtwdev->hw_port, rtwvif->port); +list_del_init (&rtwvif->list); +mutex_unlock (&rtwdev->mutex); +} + +static void rtw89_ops_configure_filter (struct ieee80211_hw *hw, +unsigned int changed_flags, +unsigned int *new_flags, +u64 multicast) +{ +struct rtw89_dev *rtwdev = hw->priv; + +mutex_lock (&rtwdev->mutex); +rtw89_leave_ps_mode (rtwdev); + + *new_flags &=FIF_ALLMULTI |FIF_OTHER_BSS |FIF_FCSFAIL | +FIF_BCN_PRBRESP_PROMISC ; + +if (changed_flags &FIF_ALLMULTI ) { +if (*new_flags &FIF_ALLMULTI ) + rtwdev->hal.rx_fltr &= ~B_AX_A_MC ; +else + rtwdev->hal.rx_fltr |=B_AX_A_MC ; + } +if (changed_flags &FIF_FCSFAIL ) { +if (*new_flags &FIF_FCSFAIL ) + rtwdev->hal.rx_fltr |=B_AX_A_CRC32_ERR ; +else + rtwdev->hal.rx_fltr &= ~B_AX_A_CRC32_ERR ; + } +if (changed_flags &FIF_OTHER_BSS ) { +if (*new_flags &FIF_OTHER_BSS ) + rtwdev->hal.rx_fltr &= ~B_AX_A_A1_MATCH ; +else + rtwdev->hal.rx_fltr |=B_AX_A_A1_MATCH ; + } +if (changed_flags &FIF_BCN_PRBRESP_PROMISC ) { +if (*new_flags &FIF_BCN_PRBRESP_PROMISC ) { + rtwdev->hal.rx_fltr &= ~B_AX_A_BCN_CHK_EN ; + rtwdev->hal.rx_fltr &= ~B_AX_A_BC ; + rtwdev->hal.rx_fltr &= ~B_AX_A_A1_MATCH ; + }else { + rtwdev->hal.rx_fltr |=B_AX_A_BCN_CHK_EN ; + rtwdev->hal.rx_fltr |=B_AX_A_BC ; + rtwdev->hal.rx_fltr |=B_AX_A_A1_MATCH ; + } + } + +rtw89_write32_mask (rtwdev, +rtw89_mac_reg_by_idx (R_AX_RX_FLTR_OPT ,RTW89_MAC_0 ), +B_AX_RX_FLTR_CFG_MASK , + rtwdev->hal.rx_fltr); +if (!rtwdev->dbcc_en) + goto out; +rtw89_write32_mask (rtwdev, +rtw89_mac_reg_by_idx (R_AX_RX_FLTR_OPT ,RTW89_MAC_1 ), +B_AX_RX_FLTR_CFG_MASK , + rtwdev->hal.rx_fltr); + +out: +mutex_unlock (&rtwdev->mutex); +} + +static const u8 ac_to_fw_idx[IEEE80211_NUM_ACS ] = { + [IEEE80211_AC_VO ] =3 , + [IEEE80211_AC_VI ] =2 , + [IEEE80211_AC_BE ] =0 , + [IEEE80211_AC_BK ] =1 , +}; + +static u8 rtw89_aifsn_to_aifs (struct rtw89_dev *rtwdev, +struct rtw89_vif *rtwvif,u8 aifsn) +{ +struct ieee80211_vif *vif =rtwvif_to_vif (rtwvif); +u8 slot_time; +u8 sifs; + + slot_time = vif->bss_conf.use_short_slot ?9 :20 ; + sifs = rtwdev->hal.current_band_type ==RTW89_BAND_5G ?16 :10 ; + +return aifsn * slot_time + sifs; +} + +static void ____rtw89_conf_tx_edca (struct rtw89_dev *rtwdev, +struct rtw89_vif *rtwvif,u16 ac) +{ +struct ieee80211_tx_queue_params *params = &rtwvif->tx_params[ac]; +u32 val; +u8 ecw_max, ecw_min; +u8 aifs; + +/* 2^ecw - 1 = cw; ecw = log2(cw + 1) */ + ecw_max =ilog2 (params->cw_max +1 ); + ecw_min =ilog2 (params->cw_min +1 ); + aifs =rtw89_aifsn_to_aifs (rtwdev, rtwvif, params->aifs); + val =FIELD_PREP (FW_EDCA_PARAM_TXOPLMT_MSK , params->txop) | +FIELD_PREP (FW_EDCA_PARAM_CWMAX_MSK , ecw_max) | +FIELD_PREP (FW_EDCA_PARAM_CWMIN_MSK , ecw_min) | +FIELD_PREP (FW_EDCA_PARAM_AIFS_MSK , aifs); +rtw89_fw_h2c_set_edca (rtwdev, rtwvif, ac_to_fw_idx[ac], val); +} + +static const u32 ac_to_mu_edca_param[IEEE80211_NUM_ACS ] = { + [IEEE80211_AC_VO ] =R_AX_MUEDCA_VO_PARAM_0 , + [IEEE80211_AC_VI ] =R_AX_MUEDCA_VI_PARAM_0 , + [IEEE80211_AC_BE ] =R_AX_MUEDCA_BE_PARAM_0 , + [IEEE80211_AC_BK ] =R_AX_MUEDCA_BK_PARAM_0 , +}; + +static void ____rtw89_conf_tx_mu_edca (struct rtw89_dev *rtwdev, +struct rtw89_vif *rtwvif,u16 ac) +{ +struct ieee80211_tx_queue_params *params = &rtwvif->tx_params[ac]; +struct ieee80211_he_mu_edca_param_ac_rec *mu_edca; +u8 aifs, aifsn; +u16 timer_32us; +u32 reg; +u32 val; + +if (!params->mu_edca) +return ; + + mu_edca = ¶ms->mu_edca_param_rec; + aifsn =FIELD_GET (GENMASK (3 ,0 ), mu_edca->aifsn); + aifs = aifsn ?rtw89_aifsn_to_aifs (rtwdev, rtwvif, aifsn) :0 ; + timer_32us = mu_edca->mu_edca_timer <<8 ; + + val =FIELD_PREP (B_AX_MUEDCA_BE_PARAM_0_TIMER_MASK , timer_32us) | +FIELD_PREP (B_AX_MUEDCA_BE_PARAM_0_CW_MASK , mu_edca->ecw_min_max) | +FIELD_PREP (B_AX_MUEDCA_BE_PARAM_0_AIFS_MASK , aifs); + reg =rtw89_mac_reg_by_idx (ac_to_mu_edca_param[ac], rtwvif->mac_idx); +rtw89_write32 (rtwdev, reg, val); + +rtw89_mac_set_hw_muedca_ctrl (rtwdev, rtwvif, true); +} + +static void __rtw89_conf_tx (struct rtw89_dev *rtwdev, +struct rtw89_vif *rtwvif,u16 ac) +{ +____rtw89_conf_tx_edca (rtwdev, rtwvif, ac); +____rtw89_conf_tx_mu_edca (rtwdev, rtwvif, ac); +} + +static void rtw89_conf_tx (struct rtw89_dev *rtwdev, +struct rtw89_vif *rtwvif) +{ +u16 ac; + +for (ac =0 ; ac <IEEE80211_NUM_ACS ; ac++) +__rtw89_conf_tx (rtwdev, rtwvif, ac); +} + +static void rtw89_station_mode_sta_assoc (struct rtw89_dev *rtwdev, +struct ieee80211_vif *vif, +struct ieee80211_bss_conf *conf) +{ +struct ieee80211_sta *sta; + +if (vif->type !=NL80211_IFTYPE_STATION ) +return ; + + sta =ieee80211_find_sta (vif, conf->bssid); +if (!sta) { +rtw89_err (rtwdev,"can't find sta to set sta_assoc state\n" ); +return ; + } +rtw89_core_sta_assoc (rtwdev, vif, sta); +} + +static void rtw89_ops_bss_info_changed (struct ieee80211_hw *hw, +struct ieee80211_vif *vif, +struct ieee80211_bss_conf *conf, +u32 changed) +{ +struct rtw89_dev *rtwdev = hw->priv; +struct rtw89_vif *rtwvif = (struct rtw89_vif *)vif->drv_priv; + +mutex_lock (&rtwdev->mutex); +rtw89_leave_ps_mode (rtwdev); + +if (changed &BSS_CHANGED_ASSOC ) { +if (conf->assoc) { +rtw89_station_mode_sta_assoc (rtwdev, vif, conf); +rtw89_phy_set_bss_color (rtwdev, vif); +rtw89_chip_cfg_txpwr_ul_tb_offset (rtwdev, vif); +rtw89_mac_port_update (rtwdev, rtwvif); + } + } + +if (changed &BSS_CHANGED_BSSID ) { +ether_addr_copy (rtwvif->bssid, conf->bssid); +rtw89_cam_bssid_changed (rtwdev, rtwvif); +rtw89_fw_h2c_cam (rtwdev, rtwvif); + } + +if (changed &BSS_CHANGED_ERP_SLOT ) +rtw89_conf_tx (rtwdev, rtwvif); + +if (changed &BSS_CHANGED_HE_BSS_COLOR ) +rtw89_phy_set_bss_color (rtwdev, vif); + +if (changed &BSS_CHANGED_MU_GROUPS ) +rtw89_mac_bf_set_gid_table (rtwdev, vif, conf); + +mutex_unlock (&rtwdev->mutex); +} + +static int rtw89_ops_conf_tx (struct ieee80211_hw *hw, +struct ieee80211_vif *vif,u16 ac, +const struct ieee80211_tx_queue_params *params) +{ +struct rtw89_dev *rtwdev = hw->priv; +struct rtw89_vif *rtwvif = (struct rtw89_vif *)vif->drv_priv; + +mutex_lock (&rtwdev->mutex); +rtw89_leave_ps_mode (rtwdev); + rtwvif->tx_params[ac] = *params; +__rtw89_conf_tx (rtwdev, rtwvif, ac); +mutex_unlock (&rtwdev->mutex); + +return 0 ; +} + +static int __rtw89_ops_sta_state (struct ieee80211_hw *hw, +struct ieee80211_vif *vif, +struct ieee80211_sta *sta, +enum ieee80211_sta_state old_state, +enum ieee80211_sta_state new_state) +{ +struct rtw89_dev *rtwdev = hw->priv; + +if (old_state ==IEEE80211_STA_NOTEXIST && + new_state ==IEEE80211_STA_NONE ) +return rtw89_core_sta_add (rtwdev, vif, sta); + +if (old_state ==IEEE80211_STA_AUTH && + new_state ==IEEE80211_STA_ASSOC ) { +if (vif->type ==NL80211_IFTYPE_STATION ) +return 0 ;/* defer to bss_info_changed to have vif info */ +return rtw89_core_sta_assoc (rtwdev, vif, sta); + } + +if (old_state ==IEEE80211_STA_ASSOC && + new_state ==IEEE80211_STA_AUTH ) +return rtw89_core_sta_disassoc (rtwdev, vif, sta); + +if (old_state ==IEEE80211_STA_AUTH && + new_state ==IEEE80211_STA_NONE ) +return rtw89_core_sta_disconnect (rtwdev, vif, sta); + +if (old_state ==IEEE80211_STA_NONE && + new_state ==IEEE80211_STA_NOTEXIST ) +return rtw89_core_sta_remove (rtwdev, vif, sta); + +return 0 ; +} + +static int rtw89_ops_sta_state (struct ieee80211_hw *hw, +struct ieee80211_vif *vif, +struct ieee80211_sta *sta, +enum ieee80211_sta_state old_state, +enum ieee80211_sta_state new_state) +{ +struct rtw89_dev *rtwdev = hw->priv; +int ret; + +mutex_lock (&rtwdev->mutex); +rtw89_leave_ps_mode (rtwdev); + ret =__rtw89_ops_sta_state (hw, vif, sta, old_state, new_state); +mutex_unlock (&rtwdev->mutex); + +return ret; +} + +static int rtw89_ops_set_key (struct ieee80211_hw *hw,enum set_key_cmd cmd, +struct ieee80211_vif *vif, +struct ieee80211_sta *sta, +struct ieee80211_key_conf *key) +{ +struct rtw89_dev *rtwdev = hw->priv; +int ret =0 ; + +mutex_lock (&rtwdev->mutex); +rtw89_leave_ps_mode (rtwdev); + +switch (cmd) { +case SET_KEY : +rtw89_btc_ntfy_specific_packet (rtwdev,PACKET_EAPOL_END ); + ret =rtw89_cam_sec_key_add (rtwdev, vif, sta, key); +if (ret && ret != -EOPNOTSUPP ) { +rtw89_err (rtwdev,"failed to add key to sec cam\n" ); + goto out; + } +break ; +case DISABLE_KEY : +rtw89_hci_flush_queues (rtwdev,BIT (rtwdev->hw->queues) -1 , + false); +rtw89_mac_flush_txq (rtwdev,BIT (rtwdev->hw->queues) -1 , false); + ret =rtw89_cam_sec_key_del (rtwdev, vif, sta, key, true); +if (ret) { +rtw89_err (rtwdev,"failed to remove key from sec cam\n" ); + goto out; + } +break ; + } + +out: +mutex_unlock (&rtwdev->mutex); + +return ret; +} + +static int rtw89_ops_ampdu_action (struct ieee80211_hw *hw, +struct ieee80211_vif *vif, +struct ieee80211_ampdu_params *params) +{ +struct rtw89_dev *rtwdev = hw->priv; +struct ieee80211_sta *sta = params->sta; +struct rtw89_sta *rtwsta = (struct rtw89_sta *)sta->drv_priv; +u16 tid = params->tid; +struct ieee80211_txq *txq = sta->txq[tid]; +struct rtw89_txq *rtwtxq = (struct rtw89_txq *)txq->drv_priv; + +switch (params->action) { +case IEEE80211_AMPDU_TX_START : +return IEEE80211_AMPDU_TX_START_IMMEDIATE ; +case IEEE80211_AMPDU_TX_STOP_CONT : +case IEEE80211_AMPDU_TX_STOP_FLUSH : +case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT : +mutex_lock (&rtwdev->mutex); +clear_bit (RTW89_TXQ_F_AMPDU , &rtwtxq->flags); +rtw89_fw_h2c_ba_cam (rtwdev, false, rtwsta->mac_id, params); +mutex_unlock (&rtwdev->mutex); +ieee80211_stop_tx_ba_cb_irqsafe (vif, sta->addr, tid); +break ; +case IEEE80211_AMPDU_TX_OPERATIONAL : +mutex_lock (&rtwdev->mutex); +set_bit (RTW89_TXQ_F_AMPDU , &rtwtxq->flags); + rtwsta->ampdu_params[tid].agg_num = params->buf_size; + rtwsta->ampdu_params[tid].amsdu = params->amsdu; +rtw89_leave_ps_mode (rtwdev); +rtw89_fw_h2c_ba_cam (rtwdev, true, rtwsta->mac_id, params); +mutex_unlock (&rtwdev->mutex); +break ; +case IEEE80211_AMPDU_RX_START : +case IEEE80211_AMPDU_RX_STOP : +break ; +default : +WARN_ON (1 ); +return -ENOTSUPP ; + } + +return 0 ; +} + +static int rtw89_ops_set_rts_threshold (struct ieee80211_hw *hw,u32 value) +{ +struct rtw89_dev *rtwdev = hw->priv; + +mutex_lock (&rtwdev->mutex); +rtw89_leave_ps_mode (rtwdev); +if (test_bit (RTW89_FLAG_POWERON , rtwdev->flags)) +rtw89_mac_update_rts_threshold (rtwdev,RTW89_MAC_0 ); +mutex_unlock (&rtwdev->mutex); + +return 0 ; +} + +static void rtw89_ops_sta_statistics (struct ieee80211_hw *hw, +struct ieee80211_vif *vif, +struct ieee80211_sta *sta, +struct station_info *sinfo) +{ +struct rtw89_sta *rtwsta = (struct rtw89_sta *)sta->drv_priv; + + sinfo->txrate = rtwsta->ra_report.txrate; + sinfo->filled |=BIT_ULL (NL80211_STA_INFO_TX_BITRATE ); +} + +static void rtw89_ops_flush (struct ieee80211_hw *hw,struct ieee80211_vif *vif, +u32 queues,bool drop) +{ +struct rtw89_dev *rtwdev = hw->priv; + +mutex_lock (&rtwdev->mutex); +rtw89_leave_lps (rtwdev); +rtw89_hci_flush_queues (rtwdev, queues, drop); +rtw89_mac_flush_txq (rtwdev, queues, drop); +mutex_unlock (&rtwdev->mutex); +} + +struct rtw89_iter_bitrate_mask_data { +struct rtw89_dev *rtwdev; +struct ieee80211_vif *vif; +const struct cfg80211_bitrate_mask *mask; +}; + +static void rtw89_ra_mask_info_update_iter (void *data,struct ieee80211_sta *sta) +{ +struct rtw89_iter_bitrate_mask_data *br_data = data; +struct rtw89_sta *rtwsta = (struct rtw89_sta *)sta->drv_priv; +struct ieee80211_vif *vif =rtwvif_to_vif (rtwsta->rtwvif); + +if (vif != br_data->vif) +return ; + + rtwsta->use_cfg_mask = true; + rtwsta->mask = *br_data->mask; +rtw89_phy_ra_updata_sta (br_data->rtwdev, sta); +} + +static void rtw89_ra_mask_info_update (struct rtw89_dev *rtwdev, +struct ieee80211_vif *vif, +const struct cfg80211_bitrate_mask *mask) +{ +struct rtw89_iter_bitrate_mask_data br_data = { .rtwdev = rtwdev, + .vif = vif, + .mask = mask}; + +ieee80211_iterate_stations_atomic (rtwdev->hw, rtw89_ra_mask_info_update_iter, + &br_data); +} + +static int rtw89_ops_set_bitrate_mask (struct ieee80211_hw *hw, +struct ieee80211_vif *vif, +const struct cfg80211_bitrate_mask *mask) +{ +struct rtw89_dev *rtwdev = hw->priv; + +mutex_lock (&rtwdev->mutex); +rtw89_ra_mask_info_update (rtwdev, vif, mask); +mutex_unlock (&rtwdev->mutex); + +return 0 ; +} + +static +int rtw89_ops_set_antenna (struct ieee80211_hw *hw,u32 tx_ant,u32 rx_ant) +{ +struct rtw89_dev *rtwdev = hw->priv; +struct rtw89_hal *hal = &rtwdev->hal; + +if (rx_ant != hw->wiphy->available_antennas_rx) +return -EINVAL ; + +mutex_lock (&rtwdev->mutex); + hal->antenna_tx = tx_ant; + hal->antenna_rx = rx_ant; +mutex_unlock (&rtwdev->mutex); + +return 0 ; +} + +static +int rtw89_ops_get_antenna (struct ieee80211_hw *hw,u32 *tx_ant,u32 *rx_ant) +{ +struct rtw89_dev *rtwdev = hw->priv; +struct rtw89_hal *hal = &rtwdev->hal; + + *tx_ant = hal->antenna_tx; + *rx_ant = hal->antenna_rx; + +return 0 ; +} + +static void rtw89_ops_sw_scan_start (struct ieee80211_hw *hw, +struct ieee80211_vif *vif, +const u8 *mac_addr) +{ +struct rtw89_dev *rtwdev = hw->priv; +struct rtw89_hal *hal = &rtwdev->hal; + +mutex_lock (&rtwdev->mutex); + rtwdev->scanning = true; +rtw89_leave_lps (rtwdev); +rtw89_btc_ntfy_scan_start (rtwdev,RTW89_PHY_0 , hal->current_band_type); +rtw89_hci_recalc_int_mit (rtwdev); +mutex_unlock (&rtwdev->mutex); +} + +static void rtw89_ops_sw_scan_complete (struct ieee80211_hw *hw, +struct ieee80211_vif *vif) +{ +struct rtw89_dev *rtwdev = hw->priv; + +mutex_lock (&rtwdev->mutex); +rtw89_btc_ntfy_scan_finish (rtwdev,RTW89_PHY_0 ); + rtwdev->scanning = false; + rtwdev->dig.bypass_dig = true; +mutex_unlock (&rtwdev->mutex); +} + +static void rtw89_ops_reconfig_complete (struct ieee80211_hw *hw, +enum ieee80211_reconfig_type reconfig_type) +{ +struct rtw89_dev *rtwdev = hw->priv; + +if (reconfig_type ==IEEE80211_RECONFIG_TYPE_RESTART ) +rtw89_ser_recfg_done (rtwdev); +} + +const struct ieee80211_ops rtw89_ops = { + .tx = rtw89_ops_tx, + .wake_tx_queue = rtw89_ops_wake_tx_queue, + .start = rtw89_ops_start, + .stop = rtw89_ops_stop, + .config = rtw89_ops_config, + .add_interface = rtw89_ops_add_interface, + .remove_interface = rtw89_ops_remove_interface, + .configure_filter = rtw89_ops_configure_filter, + .bss_info_changed = rtw89_ops_bss_info_changed, + .conf_tx = rtw89_ops_conf_tx, + .sta_state = rtw89_ops_sta_state, + .set_key = rtw89_ops_set_key, + .ampdu_action = rtw89_ops_ampdu_action, + .set_rts_threshold = rtw89_ops_set_rts_threshold, + .sta_statistics = rtw89_ops_sta_statistics, + .flush = rtw89_ops_flush, + .set_bitrate_mask = rtw89_ops_set_bitrate_mask, + .set_antenna = rtw89_ops_set_antenna, + .get_antenna = rtw89_ops_get_antenna, + .sw_scan_start = rtw89_ops_sw_scan_start, + .sw_scan_complete = rtw89_ops_sw_scan_complete, + .reconfig_complete = rtw89_ops_reconfig_complete, + .set_sar_specs = rtw89_ops_set_sar_specs, +}; +EXPORT_SYMBOL (rtw89_ops);
Signature · 1 line
2.25.1