summaryrefslogtreecommitdiff
path: root/drivers/net/wireless/realtek/rtw88
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/wireless/realtek/rtw88')
-rw-r--r--drivers/net/wireless/realtek/rtw88/debug.c26
-rw-r--r--drivers/net/wireless/realtek/rtw88/fw.c86
-rw-r--r--drivers/net/wireless/realtek/rtw88/fw.h18
-rw-r--r--drivers/net/wireless/realtek/rtw88/mac80211.c81
-rw-r--r--drivers/net/wireless/realtek/rtw88/main.c193
-rw-r--r--drivers/net/wireless/realtek/rtw88/main.h32
-rw-r--r--drivers/net/wireless/realtek/rtw88/pci.c5
-rw-r--r--drivers/net/wireless/realtek/rtw88/pci.h4
-rw-r--r--drivers/net/wireless/realtek/rtw88/reg.h5
-rw-r--r--drivers/net/wireless/realtek/rtw88/rtw8822b.c3
-rw-r--r--drivers/net/wireless/realtek/rtw88/rtw8822c.c3
-rw-r--r--drivers/net/wireless/realtek/rtw88/util.h2
12 files changed, 374 insertions, 84 deletions
diff --git a/drivers/net/wireless/realtek/rtw88/debug.c b/drivers/net/wireless/realtek/rtw88/debug.c
index 985cf5d60615..3852c4f0ac0b 100644
--- a/drivers/net/wireless/realtek/rtw88/debug.c
+++ b/drivers/net/wireless/realtek/rtw88/debug.c
@@ -229,7 +229,8 @@ static int rtw_debugfs_get_rsvd_page(struct seq_file *m, void *v)
if (!buf)
return -ENOMEM;
- ret = rtw_dump_drv_rsvd_page(rtwdev, offset, buf_size, (u32 *)buf);
+ ret = rtw_fw_dump_fifo(rtwdev, RTW_FW_FIFO_SEL_RSVD_PAGE, offset,
+ buf_size, (u32 *)buf);
if (ret) {
rtw_err(rtwdev, "failed to dump rsvd page\n");
vfree(buf);
@@ -543,6 +544,28 @@ static void rtw_print_rate(struct seq_file *m, u8 rate)
}
}
+#define case_REGD(src) \
+ case RTW_REGD_##src: return #src
+
+static const char *rtw_get_regd_string(u8 regd)
+{
+ switch (regd) {
+ case_REGD(FCC);
+ case_REGD(MKK);
+ case_REGD(ETSI);
+ case_REGD(IC);
+ case_REGD(KCC);
+ case_REGD(ACMA);
+ case_REGD(CHILE);
+ case_REGD(UKRAINE);
+ case_REGD(MEXICO);
+ case_REGD(CN);
+ case_REGD(WW);
+ default:
+ return "Unknown";
+ }
+}
+
static int rtw_debugfs_get_tx_pwr_tbl(struct seq_file *m, void *v)
{
struct rtw_debugfs_priv *debugfs_priv = m->private;
@@ -554,6 +577,7 @@ static int rtw_debugfs_get_tx_pwr_tbl(struct seq_file *m, void *v)
u8 ch = hal->current_channel;
u8 regd = rtwdev->regd.txpwr_regd;
+ seq_printf(m, "regulatory: %s\n", rtw_get_regd_string(regd));
seq_printf(m, "%-4s %-10s %-3s%6s %-4s %4s (%-4s %-4s) %-4s\n",
"path", "rate", "pwr", "", "base", "", "byr", "lmt", "rem");
diff --git a/drivers/net/wireless/realtek/rtw88/fw.c b/drivers/net/wireless/realtek/rtw88/fw.c
index 63b00bc19000..042015bc8055 100644
--- a/drivers/net/wireless/realtek/rtw88/fw.c
+++ b/drivers/net/wireless/realtek/rtw88/fw.c
@@ -193,6 +193,15 @@ void rtw_fw_c2h_cmd_rx_irqsafe(struct rtw_dev *rtwdev, u32 pkt_offset,
}
EXPORT_SYMBOL(rtw_fw_c2h_cmd_rx_irqsafe);
+void rtw_fw_c2h_cmd_isr(struct rtw_dev *rtwdev)
+{
+ if (rtw_read8(rtwdev, REG_MCU_TST_CFG) == VAL_FW_TRIGGER)
+ rtw_fw_recovery(rtwdev);
+ else
+ rtw_warn(rtwdev, "unhandled firmware c2h interrupt\n");
+}
+EXPORT_SYMBOL(rtw_fw_c2h_cmd_isr);
+
static void rtw_fw_send_h2c_command(struct rtw_dev *rtwdev,
u8 *h2c)
{
@@ -1404,29 +1413,16 @@ free:
return ret;
}
-int rtw_dump_drv_rsvd_page(struct rtw_dev *rtwdev,
- u32 offset, u32 size, u32 *buf)
+static void rtw_fw_read_fifo_page(struct rtw_dev *rtwdev, u32 offset, u32 size,
+ u32 *buf, u32 residue, u16 start_pg)
{
- struct rtw_fifo_conf *fifo = &rtwdev->fifo;
- u32 residue, i;
- u16 start_pg;
+ u32 i;
u16 idx = 0;
u16 ctl;
u8 rcr;
- if (size & 0x3) {
- rtw_warn(rtwdev, "should be 4-byte aligned\n");
- return -EINVAL;
- }
-
- offset += fifo->rsvd_boundary << TX_PAGE_SIZE_SHIFT;
- residue = offset & (FIFO_PAGE_SIZE - 1);
- start_pg = offset >> FIFO_PAGE_SIZE_SHIFT;
- start_pg += RSVD_PAGE_START_ADDR;
-
rcr = rtw_read8(rtwdev, REG_RCR + 2);
ctl = rtw_read16(rtwdev, REG_PKTBUF_DBG_CTRL) & 0xf000;
-
/* disable rx clock gate */
rtw_write8(rtwdev, REG_RCR, rcr | BIT(3));
@@ -1448,6 +1444,64 @@ int rtw_dump_drv_rsvd_page(struct rtw_dev *rtwdev,
out:
rtw_write16(rtwdev, REG_PKTBUF_DBG_CTRL, ctl);
rtw_write8(rtwdev, REG_RCR + 2, rcr);
+}
+
+static void rtw_fw_read_fifo(struct rtw_dev *rtwdev, enum rtw_fw_fifo_sel sel,
+ u32 offset, u32 size, u32 *buf)
+{
+ struct rtw_chip_info *chip = rtwdev->chip;
+ u32 start_pg, residue;
+
+ if (sel >= RTW_FW_FIFO_MAX) {
+ rtw_dbg(rtwdev, RTW_DBG_FW, "wrong fw fifo sel\n");
+ return;
+ }
+ if (sel == RTW_FW_FIFO_SEL_RSVD_PAGE)
+ offset += rtwdev->fifo.rsvd_boundary << TX_PAGE_SIZE_SHIFT;
+ residue = offset & (FIFO_PAGE_SIZE - 1);
+ start_pg = (offset >> FIFO_PAGE_SIZE_SHIFT) + chip->fw_fifo_addr[sel];
+
+ rtw_fw_read_fifo_page(rtwdev, offset, size, buf, residue, start_pg);
+}
+
+static bool rtw_fw_dump_check_size(struct rtw_dev *rtwdev,
+ enum rtw_fw_fifo_sel sel,
+ u32 start_addr, u32 size)
+{
+ switch (sel) {
+ case RTW_FW_FIFO_SEL_TX:
+ case RTW_FW_FIFO_SEL_RX:
+ if ((start_addr + size) > rtwdev->chip->fw_fifo_addr[sel])
+ return false;
+ /*fall through*/
+ default:
+ return true;
+ }
+}
+
+int rtw_fw_dump_fifo(struct rtw_dev *rtwdev, u8 fifo_sel, u32 addr, u32 size,
+ u32 *buffer)
+{
+ if (!rtwdev->chip->fw_fifo_addr) {
+ rtw_dbg(rtwdev, RTW_DBG_FW, "chip not support dump fw fifo\n");
+ return -ENOTSUPP;
+ }
+
+ if (size == 0 || !buffer)
+ return -EINVAL;
+
+ if (size & 0x3) {
+ rtw_dbg(rtwdev, RTW_DBG_FW, "not 4byte alignment\n");
+ return -EINVAL;
+ }
+
+ if (!rtw_fw_dump_check_size(rtwdev, fifo_sel, addr, size)) {
+ rtw_dbg(rtwdev, RTW_DBG_FW, "fw fifo dump size overflow\n");
+ return -EINVAL;
+ }
+
+ rtw_fw_read_fifo(rtwdev, fifo_sel, addr, size, buffer);
+
return 0;
}
diff --git a/drivers/net/wireless/realtek/rtw88/fw.h b/drivers/net/wireless/realtek/rtw88/fw.h
index 686dcd3bbda6..08644540d259 100644
--- a/drivers/net/wireless/realtek/rtw88/fw.h
+++ b/drivers/net/wireless/realtek/rtw88/fw.h
@@ -16,7 +16,6 @@
#define FIFO_PAGE_SIZE_SHIFT 12
#define FIFO_PAGE_SIZE 4096
-#define RSVD_PAGE_START_ADDR 0x780
#define FIFO_DUMP_ADDR 0x8000
#define DLFW_PAGE_SIZE_SHIFT_LEGACY 12
@@ -508,6 +507,20 @@ static inline void rtw_h2c_pkt_set_header(u8 *h2c_pkt, u8 sub_id)
#define SET_NLO_LOC_NLO_INFO(h2c_pkt, value) \
le32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, GENMASK(23, 16))
+#define GET_FW_DUMP_LEN(_header) \
+ le32_get_bits(*((__le32 *)(_header) + 0x00), GENMASK(15, 0))
+#define GET_FW_DUMP_SEQ(_header) \
+ le32_get_bits(*((__le32 *)(_header) + 0x00), GENMASK(22, 16))
+#define GET_FW_DUMP_MORE(_header) \
+ le32_get_bits(*((__le32 *)(_header) + 0x00), BIT(23))
+#define GET_FW_DUMP_VERSION(_header) \
+ le32_get_bits(*((__le32 *)(_header) + 0x00), GENMASK(31, 24))
+#define GET_FW_DUMP_TLV_TYPE(_header) \
+ le32_get_bits(*((__le32 *)(_header) + 0x01), GENMASK(15, 0))
+#define GET_FW_DUMP_TLV_LEN(_header) \
+ le32_get_bits(*((__le32 *)(_header) + 0x01), GENMASK(31, 16))
+#define GET_FW_DUMP_TLV_VAL(_header) \
+ le32_get_bits(*((__le32 *)(_header) + 0x02), GENMASK(31, 0))
static inline struct rtw_c2h_cmd *get_c2h_from_skb(struct sk_buff *skb)
{
u32 pkt_offset;
@@ -564,5 +577,8 @@ void rtw_fw_update_pkt_probe_req(struct rtw_dev *rtwdev,
struct cfg80211_ssid *ssid);
void rtw_fw_channel_switch(struct rtw_dev *rtwdev, bool enable);
void rtw_fw_h2c_cmd_dbg(struct rtw_dev *rtwdev, u8 *h2c);
+void rtw_fw_c2h_cmd_isr(struct rtw_dev *rtwdev);
+int rtw_fw_dump_fifo(struct rtw_dev *rtwdev, u8 fifo_sel, u32 addr, u32 size,
+ u32 *buffer);
#endif
diff --git a/drivers/net/wireless/realtek/rtw88/mac80211.c b/drivers/net/wireless/realtek/rtw88/mac80211.c
index 6b199152abcf..c92fba2fa480 100644
--- a/drivers/net/wireless/realtek/rtw88/mac80211.c
+++ b/drivers/net/wireless/realtek/rtw88/mac80211.c
@@ -358,13 +358,10 @@ static void rtw_ops_bss_info_changed(struct ieee80211_hw *hw,
rtw_leave_lps_deep(rtwdev);
if (changed & BSS_CHANGED_ASSOC) {
- enum rtw_net_type net_type;
-
+ rtw_vif_assoc_changed(rtwvif, conf);
if (conf->assoc) {
rtw_coex_connect_notify(rtwdev, COEX_ASSOCIATE_FINISH);
- net_type = RTW_NET_MGD_LINKED;
- rtwvif->aid = conf->aid;
rtw_fw_download_rsvd_page(rtwdev);
rtw_send_rsvd_page_h2c(rtwdev);
rtw_coex_media_status_notify(rtwdev, conf->assoc);
@@ -372,12 +369,9 @@ static void rtw_ops_bss_info_changed(struct ieee80211_hw *hw,
rtw_bf_assoc(rtwdev, vif, conf);
} else {
rtw_leave_lps(rtwdev);
- net_type = RTW_NET_NO_LINK;
- rtwvif->aid = 0;
rtw_bf_disassoc(rtwdev, vif, conf);
}
- rtwvif->net_type = net_type;
config |= PORT_SET_NET_TYPE;
config |= PORT_SET_AID;
}
@@ -429,56 +423,17 @@ static int rtw_ops_conf_tx(struct ieee80211_hw *hw,
return 0;
}
-static u8 rtw_acquire_macid(struct rtw_dev *rtwdev)
-{
- unsigned long mac_id;
-
- mac_id = find_first_zero_bit(rtwdev->mac_id_map, RTW_MAX_MAC_ID_NUM);
- if (mac_id < RTW_MAX_MAC_ID_NUM)
- set_bit(mac_id, rtwdev->mac_id_map);
-
- return mac_id;
-}
-
-static void rtw_release_macid(struct rtw_dev *rtwdev, u8 mac_id)
-{
- clear_bit(mac_id, rtwdev->mac_id_map);
-}
-
static int rtw_ops_sta_add(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ieee80211_sta *sta)
{
struct rtw_dev *rtwdev = hw->priv;
- struct rtw_sta_info *si = (struct rtw_sta_info *)sta->drv_priv;
- int i;
int ret = 0;
mutex_lock(&rtwdev->mutex);
-
- si->mac_id = rtw_acquire_macid(rtwdev);
- if (si->mac_id >= RTW_MAX_MAC_ID_NUM) {
- ret = -ENOSPC;
- goto out;
- }
-
- si->sta = sta;
- si->vif = vif;
- si->init_ra_lv = 1;
- ewma_rssi_init(&si->avg_rssi);
- for (i = 0; i < ARRAY_SIZE(sta->txq); i++)
- rtw_txq_init(rtwdev, sta->txq[i]);
-
- rtw_update_sta_info(rtwdev, si);
- rtw_fw_media_status_report(rtwdev, si->mac_id, true);
-
- rtwdev->sta_cnt++;
-
- rtw_info(rtwdev, "sta %pM joined with macid %d\n",
- sta->addr, si->mac_id);
-
-out:
+ ret = rtw_sta_add(rtwdev, sta, vif);
mutex_unlock(&rtwdev->mutex);
+
return ret;
}
@@ -487,25 +442,11 @@ static int rtw_ops_sta_remove(struct ieee80211_hw *hw,
struct ieee80211_sta *sta)
{
struct rtw_dev *rtwdev = hw->priv;
- struct rtw_sta_info *si = (struct rtw_sta_info *)sta->drv_priv;
- int i;
mutex_lock(&rtwdev->mutex);
-
- rtw_release_macid(rtwdev, si->mac_id);
- rtw_fw_media_status_report(rtwdev, si->mac_id, false);
-
- for (i = 0; i < ARRAY_SIZE(sta->txq); i++)
- rtw_txq_cleanup(rtwdev, sta->txq[i]);
-
- kfree(si->mask);
-
- rtwdev->sta_cnt--;
-
- rtw_info(rtwdev, "sta %pM with macid %d left\n",
- sta->addr, si->mac_id);
-
+ rtw_sta_remove(rtwdev, sta, true);
mutex_unlock(&rtwdev->mutex);
+
return 0;
}
@@ -845,6 +786,17 @@ static void rtw_ops_set_wakeup(struct ieee80211_hw *hw, bool enabled)
}
#endif
+static void rtw_reconfig_complete(struct ieee80211_hw *hw,
+ enum ieee80211_reconfig_type reconfig_type)
+{
+ struct rtw_dev *rtwdev = hw->priv;
+
+ mutex_lock(&rtwdev->mutex);
+ if (reconfig_type == IEEE80211_RECONFIG_TYPE_RESTART)
+ clear_bit(RTW_FLAG_RESTARTING, rtwdev->flags);
+ mutex_unlock(&rtwdev->mutex);
+}
+
const struct ieee80211_ops rtw_ops = {
.tx = rtw_ops_tx,
.wake_tx_queue = rtw_ops_wake_tx_queue,
@@ -871,6 +823,7 @@ const struct ieee80211_ops rtw_ops = {
.set_bitrate_mask = rtw_ops_set_bitrate_mask,
.set_antenna = rtw_ops_set_antenna,
.get_antenna = rtw_ops_get_antenna,
+ .reconfig_complete = rtw_reconfig_complete,
#ifdef CONFIG_PM
.suspend = rtw_ops_suspend,
.resume = rtw_ops_resume,
diff --git a/drivers/net/wireless/realtek/rtw88/main.c b/drivers/net/wireless/realtek/rtw88/main.c
index cc82c80f0433..565efd880624 100644
--- a/drivers/net/wireless/realtek/rtw88/main.c
+++ b/drivers/net/wireless/realtek/rtw88/main.c
@@ -259,6 +259,198 @@ static void rtw_c2h_work(struct work_struct *work)
}
}
+static u8 rtw_acquire_macid(struct rtw_dev *rtwdev)
+{
+ unsigned long mac_id;
+
+ mac_id = find_first_zero_bit(rtwdev->mac_id_map, RTW_MAX_MAC_ID_NUM);
+ if (mac_id < RTW_MAX_MAC_ID_NUM)
+ set_bit(mac_id, rtwdev->mac_id_map);
+
+ return mac_id;
+}
+
+int rtw_sta_add(struct rtw_dev *rtwdev, struct ieee80211_sta *sta,
+ struct ieee80211_vif *vif)
+{
+ struct rtw_sta_info *si = (struct rtw_sta_info *)sta->drv_priv;
+ int i;
+
+ si->mac_id = rtw_acquire_macid(rtwdev);
+ if (si->mac_id >= RTW_MAX_MAC_ID_NUM)
+ return -ENOSPC;
+
+ si->sta = sta;
+ si->vif = vif;
+ si->init_ra_lv = 1;
+ ewma_rssi_init(&si->avg_rssi);
+ for (i = 0; i < ARRAY_SIZE(sta->txq); i++)
+ rtw_txq_init(rtwdev, sta->txq[i]);
+
+ rtw_update_sta_info(rtwdev, si);
+ rtw_fw_media_status_report(rtwdev, si->mac_id, true);
+
+ rtwdev->sta_cnt++;
+ rtw_info(rtwdev, "sta %pM joined with macid %d\n",
+ sta->addr, si->mac_id);
+
+ return 0;
+}
+
+void rtw_sta_remove(struct rtw_dev *rtwdev, struct ieee80211_sta *sta,
+ bool fw_exist)
+{
+ struct rtw_sta_info *si = (struct rtw_sta_info *)sta->drv_priv;
+ int i;
+
+ rtw_release_macid(rtwdev, si->mac_id);
+ if (fw_exist)
+ rtw_fw_media_status_report(rtwdev, si->mac_id, false);
+
+ for (i = 0; i < ARRAY_SIZE(sta->txq); i++)
+ rtw_txq_cleanup(rtwdev, sta->txq[i]);
+
+ kfree(si->mask);
+
+ rtwdev->sta_cnt--;
+ rtw_info(rtwdev, "sta %pM with macid %d left\n",
+ sta->addr, si->mac_id);
+}
+
+static bool rtw_fw_dump_crash_log(struct rtw_dev *rtwdev)
+{
+ u32 size = rtwdev->chip->fw_rxff_size;
+ u32 *buf;
+ u8 seq;
+ bool ret = true;
+
+ buf = vmalloc(size);
+ if (!buf)
+ goto exit;
+
+ if (rtw_fw_dump_fifo(rtwdev, RTW_FW_FIFO_SEL_RXBUF_FW, 0, size, buf)) {
+ rtw_dbg(rtwdev, RTW_DBG_FW, "dump fw fifo fail\n");
+ goto free_buf;
+ }
+
+ if (GET_FW_DUMP_LEN(buf) == 0) {
+ rtw_dbg(rtwdev, RTW_DBG_FW, "fw crash dump's length is 0\n");
+ goto free_buf;
+ }
+
+ seq = GET_FW_DUMP_SEQ(buf);
+ if (seq > 0 && seq != (rtwdev->fw.prev_dump_seq + 1)) {
+ rtw_dbg(rtwdev, RTW_DBG_FW,
+ "fw crash dump's seq is wrong: %d\n", seq);
+ goto free_buf;
+ }
+ if (seq == 0 &&
+ (GET_FW_DUMP_TLV_TYPE(buf) != FW_CD_TYPE ||
+ GET_FW_DUMP_TLV_LEN(buf) != FW_CD_LEN ||
+ GET_FW_DUMP_TLV_VAL(buf) != FW_CD_VAL)) {
+ rtw_dbg(rtwdev, RTW_DBG_FW, "fw crash dump's tlv is wrong\n");
+ goto free_buf;
+ }
+
+ print_hex_dump_bytes("rtw88 fw dump: ", DUMP_PREFIX_OFFSET, buf, size);
+
+ if (GET_FW_DUMP_MORE(buf) == 1) {
+ rtwdev->fw.prev_dump_seq = seq;
+ ret = false;
+ }
+
+free_buf:
+ vfree(buf);
+exit:
+ rtw_write8(rtwdev, REG_MCU_TST_CFG, 0);
+
+ return ret;
+}
+
+void rtw_vif_assoc_changed(struct rtw_vif *rtwvif,
+ struct ieee80211_bss_conf *conf)
+{
+ if (conf && conf->assoc) {
+ rtwvif->aid = conf->aid;
+ rtwvif->net_type = RTW_NET_MGD_LINKED;
+ } else {
+ rtwvif->aid = 0;
+ rtwvif->net_type = RTW_NET_NO_LINK;
+ }
+}
+
+static void rtw_reset_key_iter(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ struct ieee80211_key_conf *key,
+ void *data)
+{
+ struct rtw_dev *rtwdev = (struct rtw_dev *)data;
+ struct rtw_sec_desc *sec = &rtwdev->sec;
+
+ rtw_sec_clear_cam(rtwdev, sec, key->hw_key_idx);
+}
+
+static void rtw_reset_sta_iter(void *data, struct ieee80211_sta *sta)
+{
+ struct rtw_dev *rtwdev = (struct rtw_dev *)data;
+
+ if (rtwdev->sta_cnt == 0) {
+ rtw_warn(rtwdev, "sta count before reset should not be 0\n");
+ return;
+ }
+ rtw_sta_remove(rtwdev, sta, false);
+}
+
+static void rtw_reset_vif_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
+{
+ struct rtw_dev *rtwdev = (struct rtw_dev *)data;
+ struct rtw_vif *rtwvif = (struct rtw_vif *)vif->drv_priv;
+
+ rtw_bf_disassoc(rtwdev, vif, NULL);
+ rtw_vif_assoc_changed(rtwvif, NULL);
+ rtw_txq_cleanup(rtwdev, vif->txq);
+}
+
+void rtw_fw_recovery(struct rtw_dev *rtwdev)
+{
+ if (!test_bit(RTW_FLAG_RESTARTING, rtwdev->flags))
+ ieee80211_queue_work(rtwdev->hw, &rtwdev->fw_recovery_work);
+}
+
+static void rtw_fw_recovery_work(struct work_struct *work)
+{
+ struct rtw_dev *rtwdev = container_of(work, struct rtw_dev,
+ fw_recovery_work);
+
+ /* rtw_fw_dump_crash_log() returns false indicates that there are
+ * still more log to dump. Driver set 0x1cf[7:0] = 0x1 to tell firmware
+ * to dump the remaining part of the log, and firmware will trigger an
+ * IMR_C2HCMD interrupt to inform driver the log is ready.
+ */
+ if (!rtw_fw_dump_crash_log(rtwdev)) {
+ rtw_write8(rtwdev, REG_HRCV_MSG, 1);
+ return;
+ }
+ rtwdev->fw.prev_dump_seq = 0;
+
+ WARN(1, "firmware crash, start reset and recover\n");
+
+ mutex_lock(&rtwdev->mutex);
+
+ set_bit(RTW_FLAG_RESTARTING, rtwdev->flags);
+ rcu_read_lock();
+ rtw_iterate_keys_rcu(rtwdev, NULL, rtw_reset_key_iter, rtwdev);
+ rcu_read_unlock();
+ rtw_iterate_stas_atomic(rtwdev, rtw_reset_sta_iter, rtwdev);
+ rtw_iterate_vifs_atomic(rtwdev, rtw_reset_vif_iter, rtwdev);
+ rtw_enter_ips(rtwdev);
+
+ mutex_unlock(&rtwdev->mutex);
+
+ ieee80211_restart_hw(rtwdev->hw);
+}
+
struct rtw_txq_ba_iter_data {
};
@@ -1431,6 +1623,7 @@ int rtw_core_init(struct rtw_dev *rtwdev)
INIT_DELAYED_WORK(&coex->wl_remain_work, rtw_coex_wl_remain_work);
INIT_DELAYED_WORK(&coex->bt_remain_work, rtw_coex_bt_remain_work);
INIT_WORK(&rtwdev->c2h_work, rtw_c2h_work);
+ INIT_WORK(&rtwdev->fw_recovery_work, rtw_fw_recovery_work);
INIT_WORK(&rtwdev->ba_work, rtw_txq_ba_work);
skb_queue_head_init(&rtwdev->c2h_queue);
skb_queue_head_init(&rtwdev->coex.queue);
diff --git a/drivers/net/wireless/realtek/rtw88/main.h b/drivers/net/wireless/realtek/rtw88/main.h
index 276b5d381467..ffb02e614217 100644
--- a/drivers/net/wireless/realtek/rtw88/main.h
+++ b/drivers/net/wireless/realtek/rtw88/main.h
@@ -359,6 +359,7 @@ enum rtw_flags {
RTW_FLAG_DIG_DISABLE,
RTW_FLAG_BUSY_TRAFFIC,
RTW_FLAG_WOWLAN,
+ RTW_FLAG_RESTARTING,
NUM_OF_RTW_FLAGS,
};
@@ -1082,6 +1083,17 @@ enum rtw_wlan_cpu {
RTW_WCPU_11N,
};
+enum rtw_fw_fifo_sel {
+ RTW_FW_FIFO_SEL_TX,
+ RTW_FW_FIFO_SEL_RX,
+ RTW_FW_FIFO_SEL_RSVD_PAGE,
+ RTW_FW_FIFO_SEL_REPORT,
+ RTW_FW_FIFO_SEL_LLT,
+ RTW_FW_FIFO_SEL_RXBUF_FW,
+
+ RTW_FW_FIFO_MAX,
+};
+
/* hardware configuration for each IC */
struct rtw_chip_info {
struct rtw_chip_ops *ops;
@@ -1098,6 +1110,7 @@ struct rtw_chip_info {
u32 ptct_efuse_size;
u32 txff_size;
u32 rxff_size;
+ u32 fw_rxff_size;
u8 band;
u8 page_size;
u8 csi_buf_pg_num;
@@ -1108,6 +1121,8 @@ struct rtw_chip_info {
bool rx_ldpc;
u8 max_power_index;
+ u16 fw_fifo_addr[RTW_FW_FIFO_MAX];
+
bool ht_supported;
bool vht_supported;
u8 lps_deep_mode_supported;
@@ -1606,6 +1621,9 @@ struct rtw_fifo_conf {
const struct rtw_rqpn *rqpn;
};
+#define FW_CD_TYPE 0xffff
+#define FW_CD_LEN 4
+#define FW_CD_VAL 0xaabbccdd
struct rtw_fw_state {
const struct firmware *firmware;
struct rtw_dev *rtwdev;
@@ -1614,6 +1632,7 @@ struct rtw_fw_state {
u8 sub_version;
u8 sub_index;
u16 h2c_version;
+ u8 prev_dump_seq;
};
struct rtw_hal {
@@ -1699,6 +1718,7 @@ struct rtw_dev {
/* c2h cmd queue & handler work */
struct sk_buff_head c2h_queue;
struct work_struct c2h_work;
+ struct work_struct fw_recovery_work;
/* used to protect txqs list */
spinlock_t txq_lock;
@@ -1799,6 +1819,11 @@ static inline bool rtw_chip_has_rx_ldpc(struct rtw_dev *rtwdev)
return rtwdev->chip->rx_ldpc;
}
+static inline void rtw_release_macid(struct rtw_dev *rtwdev, u8 mac_id)
+{
+ clear_bit(mac_id, rtwdev->mac_id_map);
+}
+
void rtw_get_channel_params(struct cfg80211_chan_def *chandef,
struct rtw_channel_params *ch_param);
bool check_hw_ready(struct rtw_dev *rtwdev, u32 addr, u32 mask, u32 target);
@@ -1821,5 +1846,12 @@ void rtw_core_deinit(struct rtw_dev *rtwdev);
int rtw_register_hw(struct rtw_dev *rtwdev, struct ieee80211_hw *hw);
void rtw_unregister_hw(struct rtw_dev *rtwdev, struct ieee80211_hw *hw);
u16 rtw_desc_to_bitrate(u8 desc_rate);
+void rtw_vif_assoc_changed(struct rtw_vif *rtwvif,
+ struct ieee80211_bss_conf *conf);
+int rtw_sta_add(struct rtw_dev *rtwdev, struct ieee80211_sta *sta,
+ struct ieee80211_vif *vif);
+void rtw_sta_remove(struct rtw_dev *rtwdev, struct ieee80211_sta *sta,
+ bool fw_exist);
+void rtw_fw_recovery(struct rtw_dev *rtwdev);
#endif
diff --git a/drivers/net/wireless/realtek/rtw88/pci.c b/drivers/net/wireless/realtek/rtw88/pci.c
index 135dd331691c..676d861aaf99 100644
--- a/drivers/net/wireless/realtek/rtw88/pci.c
+++ b/drivers/net/wireless/realtek/rtw88/pci.c
@@ -389,6 +389,7 @@ static int rtw_pci_init(struct rtw_dev *rtwdev)
IMR_VODOK |
IMR_ROK |
IMR_BCNDMAINT_E |
+ IMR_C2HCMD |
0;
rtwpci->irq_mask[1] = IMR_TXFOVW |
0;
@@ -1079,6 +1080,8 @@ static irqreturn_t rtw_pci_interrupt_threadfn(int irq, void *dev)
rtw_pci_tx_isr(rtwdev, rtwpci, RTW_TX_QUEUE_H2C);
if (irq_status[0] & IMR_ROK)
rtw_pci_rx_isr(rtwdev, rtwpci, RTW_RX_QUEUE_MPDU);
+ if (unlikely(irq_status[0] & IMR_C2HCMD))
+ rtw_fw_c2h_cmd_isr(rtwdev);
/* all of the jobs for this interrupt have been done */
rtw_pci_enable_interrupt(rtwdev, rtwpci);
@@ -1598,6 +1601,8 @@ void rtw_pci_shutdown(struct pci_dev *pdev)
if (chip->ops->shutdown)
chip->ops->shutdown(rtwdev);
+
+ pci_set_power_state(pdev, PCI_D3hot);
}
EXPORT_SYMBOL(rtw_pci_shutdown);
diff --git a/drivers/net/wireless/realtek/rtw88/pci.h b/drivers/net/wireless/realtek/rtw88/pci.h
index 024c2bc275cb..ca17aa9cf7dc 100644
--- a/drivers/net/wireless/realtek/rtw88/pci.h
+++ b/drivers/net/wireless/realtek/rtw88/pci.h
@@ -9,8 +9,8 @@
#define RTK_BEQ_TX_DESC_NUM 256
#define RTK_MAX_RX_DESC_NUM 512
-/* 8K + rx desc size */
-#define RTK_PCI_RX_BUF_SIZE (8192 + 24)
+/* 11K + rx desc size */
+#define RTK_PCI_RX_BUF_SIZE (11454 + 24)
#define RTK_PCI_CTRL 0x300
#define BIT_RST_TRXDMA_INTF BIT(20)
diff --git a/drivers/net/wireless/realtek/rtw88/reg.h b/drivers/net/wireless/realtek/rtw88/reg.h
index 8f468d6b5f78..86b94c008a27 100644
--- a/drivers/net/wireless/realtek/rtw88/reg.h
+++ b/drivers/net/wireless/realtek/rtw88/reg.h
@@ -126,6 +126,9 @@
BIT_WINTINI_RDY | BIT_RAM_DL_SEL)
#define FW_READY_MASK 0xffff
+#define REG_MCU_TST_CFG 0x84
+#define VAL_FW_TRIGGER 0x1
+
#define REG_EFUSE_ACCESS 0x00CF
#define EFUSE_ACCESS_ON 0x69
#define EFUSE_ACCESS_OFF 0x00
@@ -616,6 +619,8 @@
#define BIT_ANAPAR_BTPS BIT(22)
#define REG_RSTB_SEL 0x1c38
+#define REG_HRCV_MSG 0x1cf
+
#define REG_IGN_GNTBT4 0x4160
#define RF_MODE 0x00
diff --git a/drivers/net/wireless/realtek/rtw88/rtw8822b.c b/drivers/net/wireless/realtek/rtw88/rtw8822b.c
index b7a98dbbb09c..22d0dd640ac9 100644
--- a/drivers/net/wireless/realtek/rtw88/rtw8822b.c
+++ b/drivers/net/wireless/realtek/rtw88/rtw8822b.c
@@ -2442,6 +2442,7 @@ struct rtw_chip_info rtw8822b_hw_spec = {
.ptct_efuse_size = 96,
.txff_size = 262144,
.rxff_size = 24576,
+ .fw_rxff_size = 12288,
.txgi_factor = 1,
.is_pwr_by_rate_dec = true,
.max_power_index = 0x3f,
@@ -2504,6 +2505,8 @@ struct rtw_chip_info rtw8822b_hw_spec = {
.coex_info_hw_regs_num = ARRAY_SIZE(coex_info_hw_regs_8822b),
.coex_info_hw_regs = coex_info_hw_regs_8822b,
+
+ .fw_fifo_addr = {0x780, 0x700, 0x780, 0x660, 0x650, 0x680},
};
EXPORT_SYMBOL(rtw8822b_hw_spec);
diff --git a/drivers/net/wireless/realtek/rtw88/rtw8822c.c b/drivers/net/wireless/realtek/rtw88/rtw8822c.c
index dd216a23fc99..e37300e98517 100644
--- a/drivers/net/wireless/realtek/rtw88/rtw8822c.c
+++ b/drivers/net/wireless/realtek/rtw88/rtw8822c.c
@@ -4294,6 +4294,7 @@ struct rtw_chip_info rtw8822c_hw_spec = {
.ptct_efuse_size = 124,
.txff_size = 262144,
.rxff_size = 24576,
+ .fw_rxff_size = 12288,
.txgi_factor = 2,
.is_pwr_by_rate_dec = false,
.max_power_index = 0x7f,
@@ -4364,6 +4365,8 @@ struct rtw_chip_info rtw8822c_hw_spec = {
.coex_info_hw_regs_num = ARRAY_SIZE(coex_info_hw_regs_8822c),
.coex_info_hw_regs = coex_info_hw_regs_8822c,
+
+ .fw_fifo_addr = {0x780, 0x700, 0x780, 0x660, 0x650, 0x680},
};
EXPORT_SYMBOL(rtw8822c_hw_spec);
diff --git a/drivers/net/wireless/realtek/rtw88/util.h b/drivers/net/wireless/realtek/rtw88/util.h
index 41c10e7144df..0c23b5069be0 100644
--- a/drivers/net/wireless/realtek/rtw88/util.h
+++ b/drivers/net/wireless/realtek/rtw88/util.h
@@ -17,6 +17,8 @@ struct rtw_dev;
ieee80211_iterate_stations_atomic(rtwdev->hw, iterator, data)
#define rtw_iterate_keys(rtwdev, vif, iterator, data) \
ieee80211_iter_keys(rtwdev->hw, vif, iterator, data)
+#define rtw_iterate_keys_rcu(rtwdev, vif, iterator, data) \
+ ieee80211_iter_keys_rcu((rtwdev)->hw, vif, iterator, data)
static inline u8 *get_hdr_bssid(struct ieee80211_hdr *hdr)
{