1e3ec7017SPing-Ke Shih // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
2e3ec7017SPing-Ke Shih /* Copyright(c) 2019-2020 Realtek Corporation
3e3ec7017SPing-Ke Shih */
4e3ec7017SPing-Ke Shih
59f8004bfSZong-Zhe Yang #include <linux/devcoredump.h>
69f8004bfSZong-Zhe Yang
7e3ec7017SPing-Ke Shih #include "cam.h"
8a88b6cc4SZong-Zhe Yang #include "chan.h"
9e3ec7017SPing-Ke Shih #include "debug.h"
109f8004bfSZong-Zhe Yang #include "fw.h"
11e3ec7017SPing-Ke Shih #include "mac.h"
12e3ec7017SPing-Ke Shih #include "ps.h"
139f8004bfSZong-Zhe Yang #include "reg.h"
14e3ec7017SPing-Ke Shih #include "ser.h"
15e3ec7017SPing-Ke Shih #include "util.h"
16e3ec7017SPing-Ke Shih
17e3ec7017SPing-Ke Shih #define SER_RECFG_TIMEOUT 1000
18e3ec7017SPing-Ke Shih
19e3ec7017SPing-Ke Shih enum ser_evt {
20e3ec7017SPing-Ke Shih SER_EV_NONE,
21e3ec7017SPing-Ke Shih SER_EV_STATE_IN,
22e3ec7017SPing-Ke Shih SER_EV_STATE_OUT,
2356617fd0SZong-Zhe Yang SER_EV_L1_RESET_PREPARE, /* pre-M0 */
24e3ec7017SPing-Ke Shih SER_EV_L1_RESET, /* M1 */
25e3ec7017SPing-Ke Shih SER_EV_DO_RECOVERY, /* M3 */
26e3ec7017SPing-Ke Shih SER_EV_MAC_RESET_DONE, /* M5 */
27e3ec7017SPing-Ke Shih SER_EV_L2_RESET,
28e3ec7017SPing-Ke Shih SER_EV_L2_RECFG_DONE,
29e3ec7017SPing-Ke Shih SER_EV_L2_RECFG_TIMEOUT,
3056617fd0SZong-Zhe Yang SER_EV_M1_TIMEOUT,
31e3ec7017SPing-Ke Shih SER_EV_M3_TIMEOUT,
32e3ec7017SPing-Ke Shih SER_EV_FW_M5_TIMEOUT,
33e3ec7017SPing-Ke Shih SER_EV_L0_RESET,
34e3ec7017SPing-Ke Shih SER_EV_MAXX
35e3ec7017SPing-Ke Shih };
36e3ec7017SPing-Ke Shih
37e3ec7017SPing-Ke Shih enum ser_state {
38e3ec7017SPing-Ke Shih SER_IDLE_ST,
3956617fd0SZong-Zhe Yang SER_L1_RESET_PRE_ST,
40e3ec7017SPing-Ke Shih SER_RESET_TRX_ST,
41e3ec7017SPing-Ke Shih SER_DO_HCI_ST,
42e3ec7017SPing-Ke Shih SER_L2_RESET_ST,
43e3ec7017SPing-Ke Shih SER_ST_MAX_ST
44e3ec7017SPing-Ke Shih };
45e3ec7017SPing-Ke Shih
46e3ec7017SPing-Ke Shih struct ser_msg {
47e3ec7017SPing-Ke Shih struct list_head list;
48e3ec7017SPing-Ke Shih u8 event;
49e3ec7017SPing-Ke Shih };
50e3ec7017SPing-Ke Shih
51e3ec7017SPing-Ke Shih struct state_ent {
52e3ec7017SPing-Ke Shih u8 state;
53e3ec7017SPing-Ke Shih char *name;
54e3ec7017SPing-Ke Shih void (*st_func)(struct rtw89_ser *ser, u8 event);
55e3ec7017SPing-Ke Shih };
56e3ec7017SPing-Ke Shih
57e3ec7017SPing-Ke Shih struct event_ent {
58e3ec7017SPing-Ke Shih u8 event;
59e3ec7017SPing-Ke Shih char *name;
60e3ec7017SPing-Ke Shih };
61e3ec7017SPing-Ke Shih
ser_ev_name(struct rtw89_ser * ser,u8 event)62e3ec7017SPing-Ke Shih static char *ser_ev_name(struct rtw89_ser *ser, u8 event)
63e3ec7017SPing-Ke Shih {
64e3ec7017SPing-Ke Shih if (event < SER_EV_MAXX)
65e3ec7017SPing-Ke Shih return ser->ev_tbl[event].name;
66e3ec7017SPing-Ke Shih
67e3ec7017SPing-Ke Shih return "err_ev_name";
68e3ec7017SPing-Ke Shih }
69e3ec7017SPing-Ke Shih
ser_st_name(struct rtw89_ser * ser)70e3ec7017SPing-Ke Shih static char *ser_st_name(struct rtw89_ser *ser)
71e3ec7017SPing-Ke Shih {
72e3ec7017SPing-Ke Shih if (ser->state < SER_ST_MAX_ST)
73e3ec7017SPing-Ke Shih return ser->st_tbl[ser->state].name;
74e3ec7017SPing-Ke Shih
75e3ec7017SPing-Ke Shih return "err_st_name";
76e3ec7017SPing-Ke Shih }
77e3ec7017SPing-Ke Shih
789f8004bfSZong-Zhe Yang #define RTW89_DEF_SER_CD_TYPE(_name, _type, _size) \
799f8004bfSZong-Zhe Yang struct ser_cd_ ## _name { \
809f8004bfSZong-Zhe Yang u32 type; \
819f8004bfSZong-Zhe Yang u32 type_size; \
829f8004bfSZong-Zhe Yang u64 padding; \
839f8004bfSZong-Zhe Yang u8 data[_size]; \
849f8004bfSZong-Zhe Yang } __packed; \
859f8004bfSZong-Zhe Yang static void ser_cd_ ## _name ## _init(struct ser_cd_ ## _name *p) \
869f8004bfSZong-Zhe Yang { \
879f8004bfSZong-Zhe Yang p->type = _type; \
889f8004bfSZong-Zhe Yang p->type_size = sizeof(p->data); \
899f8004bfSZong-Zhe Yang p->padding = 0x0123456789abcdef; \
909f8004bfSZong-Zhe Yang }
919f8004bfSZong-Zhe Yang
929f8004bfSZong-Zhe Yang enum rtw89_ser_cd_type {
939f8004bfSZong-Zhe Yang RTW89_SER_CD_FW_RSVD_PLE = 0,
94f5e24684SZong-Zhe Yang RTW89_SER_CD_FW_BACKTRACE = 1,
959f8004bfSZong-Zhe Yang };
969f8004bfSZong-Zhe Yang
979f8004bfSZong-Zhe Yang RTW89_DEF_SER_CD_TYPE(fw_rsvd_ple,
989f8004bfSZong-Zhe Yang RTW89_SER_CD_FW_RSVD_PLE,
999f8004bfSZong-Zhe Yang RTW89_FW_RSVD_PLE_SIZE);
1009f8004bfSZong-Zhe Yang
101f5e24684SZong-Zhe Yang RTW89_DEF_SER_CD_TYPE(fw_backtrace,
102f5e24684SZong-Zhe Yang RTW89_SER_CD_FW_BACKTRACE,
103f5e24684SZong-Zhe Yang RTW89_FW_BACKTRACE_MAX_SIZE);
104f5e24684SZong-Zhe Yang
1059f8004bfSZong-Zhe Yang struct rtw89_ser_cd_buffer {
1069f8004bfSZong-Zhe Yang struct ser_cd_fw_rsvd_ple fwple;
107f5e24684SZong-Zhe Yang struct ser_cd_fw_backtrace fwbt;
1089f8004bfSZong-Zhe Yang } __packed;
1099f8004bfSZong-Zhe Yang
rtw89_ser_cd_prep(struct rtw89_dev * rtwdev)1109f8004bfSZong-Zhe Yang static struct rtw89_ser_cd_buffer *rtw89_ser_cd_prep(struct rtw89_dev *rtwdev)
1119f8004bfSZong-Zhe Yang {
1129f8004bfSZong-Zhe Yang struct rtw89_ser_cd_buffer *buf;
1139f8004bfSZong-Zhe Yang
1149f8004bfSZong-Zhe Yang buf = vzalloc(sizeof(*buf));
1159f8004bfSZong-Zhe Yang if (!buf)
1169f8004bfSZong-Zhe Yang return NULL;
1179f8004bfSZong-Zhe Yang
1189f8004bfSZong-Zhe Yang ser_cd_fw_rsvd_ple_init(&buf->fwple);
119f5e24684SZong-Zhe Yang ser_cd_fw_backtrace_init(&buf->fwbt);
1209f8004bfSZong-Zhe Yang
1219f8004bfSZong-Zhe Yang return buf;
1229f8004bfSZong-Zhe Yang }
1239f8004bfSZong-Zhe Yang
rtw89_ser_cd_send(struct rtw89_dev * rtwdev,struct rtw89_ser_cd_buffer * buf)1249f8004bfSZong-Zhe Yang static void rtw89_ser_cd_send(struct rtw89_dev *rtwdev,
1259f8004bfSZong-Zhe Yang struct rtw89_ser_cd_buffer *buf)
1269f8004bfSZong-Zhe Yang {
1279f8004bfSZong-Zhe Yang rtw89_debug(rtwdev, RTW89_DBG_SER, "SER sends core dump\n");
1289f8004bfSZong-Zhe Yang
1299f8004bfSZong-Zhe Yang /* After calling dev_coredump, buf's lifetime is supposed to be
1309f8004bfSZong-Zhe Yang * handled by the device coredump framework. Note that a new dump
1319f8004bfSZong-Zhe Yang * will be discarded if a previous one hasn't been released by
1329f8004bfSZong-Zhe Yang * framework yet.
1339f8004bfSZong-Zhe Yang */
1349f8004bfSZong-Zhe Yang dev_coredumpv(rtwdev->dev, buf, sizeof(*buf), GFP_KERNEL);
1359f8004bfSZong-Zhe Yang }
1369f8004bfSZong-Zhe Yang
rtw89_ser_cd_free(struct rtw89_dev * rtwdev,struct rtw89_ser_cd_buffer * buf,bool free_self)137f5e24684SZong-Zhe Yang static void rtw89_ser_cd_free(struct rtw89_dev *rtwdev,
138f5e24684SZong-Zhe Yang struct rtw89_ser_cd_buffer *buf, bool free_self)
139f5e24684SZong-Zhe Yang {
140f5e24684SZong-Zhe Yang if (!free_self)
141f5e24684SZong-Zhe Yang return;
142f5e24684SZong-Zhe Yang
143f5e24684SZong-Zhe Yang rtw89_debug(rtwdev, RTW89_DBG_SER, "SER frees core dump by self\n");
144f5e24684SZong-Zhe Yang
145f5e24684SZong-Zhe Yang /* When some problems happen during filling data of core dump,
146f5e24684SZong-Zhe Yang * we won't send it to device coredump framework. Instead, we
147f5e24684SZong-Zhe Yang * free buf by ourselves.
148f5e24684SZong-Zhe Yang */
149f5e24684SZong-Zhe Yang vfree(buf);
150f5e24684SZong-Zhe Yang }
151f5e24684SZong-Zhe Yang
ser_state_run(struct rtw89_ser * ser,u8 evt)152e3ec7017SPing-Ke Shih static void ser_state_run(struct rtw89_ser *ser, u8 evt)
153e3ec7017SPing-Ke Shih {
154e3ec7017SPing-Ke Shih struct rtw89_dev *rtwdev = container_of(ser, struct rtw89_dev, ser);
155e3ec7017SPing-Ke Shih
156e3ec7017SPing-Ke Shih rtw89_debug(rtwdev, RTW89_DBG_SER, "ser: %s receive %s\n",
157e3ec7017SPing-Ke Shih ser_st_name(ser), ser_ev_name(ser, evt));
158e3ec7017SPing-Ke Shih
1598676031bSZong-Zhe Yang mutex_lock(&rtwdev->mutex);
160e3ec7017SPing-Ke Shih rtw89_leave_lps(rtwdev);
1618676031bSZong-Zhe Yang mutex_unlock(&rtwdev->mutex);
1628676031bSZong-Zhe Yang
163e3ec7017SPing-Ke Shih ser->st_tbl[ser->state].st_func(ser, evt);
164e3ec7017SPing-Ke Shih }
165e3ec7017SPing-Ke Shih
ser_state_goto(struct rtw89_ser * ser,u8 new_state)166e3ec7017SPing-Ke Shih static void ser_state_goto(struct rtw89_ser *ser, u8 new_state)
167e3ec7017SPing-Ke Shih {
168e3ec7017SPing-Ke Shih struct rtw89_dev *rtwdev = container_of(ser, struct rtw89_dev, ser);
169e3ec7017SPing-Ke Shih
170e3ec7017SPing-Ke Shih if (ser->state == new_state || new_state >= SER_ST_MAX_ST)
171e3ec7017SPing-Ke Shih return;
172e3ec7017SPing-Ke Shih ser_state_run(ser, SER_EV_STATE_OUT);
173e3ec7017SPing-Ke Shih
174e3ec7017SPing-Ke Shih rtw89_debug(rtwdev, RTW89_DBG_SER, "ser: %s goto -> %s\n",
175e3ec7017SPing-Ke Shih ser_st_name(ser), ser->st_tbl[new_state].name);
176e3ec7017SPing-Ke Shih
177e3ec7017SPing-Ke Shih ser->state = new_state;
178e3ec7017SPing-Ke Shih ser_state_run(ser, SER_EV_STATE_IN);
179e3ec7017SPing-Ke Shih }
180e3ec7017SPing-Ke Shih
__rtw89_ser_dequeue_msg(struct rtw89_ser * ser)181e3ec7017SPing-Ke Shih static struct ser_msg *__rtw89_ser_dequeue_msg(struct rtw89_ser *ser)
182e3ec7017SPing-Ke Shih {
183e3ec7017SPing-Ke Shih struct ser_msg *msg;
184e3ec7017SPing-Ke Shih
185e3ec7017SPing-Ke Shih spin_lock_irq(&ser->msg_q_lock);
186e3ec7017SPing-Ke Shih msg = list_first_entry_or_null(&ser->msg_q, struct ser_msg, list);
187e3ec7017SPing-Ke Shih if (msg)
188e3ec7017SPing-Ke Shih list_del(&msg->list);
189e3ec7017SPing-Ke Shih spin_unlock_irq(&ser->msg_q_lock);
190e3ec7017SPing-Ke Shih
191e3ec7017SPing-Ke Shih return msg;
192e3ec7017SPing-Ke Shih }
193e3ec7017SPing-Ke Shih
rtw89_ser_hdl_work(struct work_struct * work)194e3ec7017SPing-Ke Shih static void rtw89_ser_hdl_work(struct work_struct *work)
195e3ec7017SPing-Ke Shih {
196e3ec7017SPing-Ke Shih struct ser_msg *msg;
197e3ec7017SPing-Ke Shih struct rtw89_ser *ser = container_of(work, struct rtw89_ser,
198e3ec7017SPing-Ke Shih ser_hdl_work);
199e3ec7017SPing-Ke Shih
200e3ec7017SPing-Ke Shih while ((msg = __rtw89_ser_dequeue_msg(ser))) {
201e3ec7017SPing-Ke Shih ser_state_run(ser, msg->event);
202e3ec7017SPing-Ke Shih kfree(msg);
203e3ec7017SPing-Ke Shih }
204e3ec7017SPing-Ke Shih }
205e3ec7017SPing-Ke Shih
ser_send_msg(struct rtw89_ser * ser,u8 event)206e3ec7017SPing-Ke Shih static int ser_send_msg(struct rtw89_ser *ser, u8 event)
207e3ec7017SPing-Ke Shih {
208e3ec7017SPing-Ke Shih struct rtw89_dev *rtwdev = container_of(ser, struct rtw89_dev, ser);
209e3ec7017SPing-Ke Shih struct ser_msg *msg = NULL;
210e3ec7017SPing-Ke Shih
211e3ec7017SPing-Ke Shih if (test_bit(RTW89_SER_DRV_STOP_RUN, ser->flags))
212e3ec7017SPing-Ke Shih return -EIO;
213e3ec7017SPing-Ke Shih
214e3ec7017SPing-Ke Shih msg = kmalloc(sizeof(*msg), GFP_ATOMIC);
215e3ec7017SPing-Ke Shih if (!msg)
216e3ec7017SPing-Ke Shih return -ENOMEM;
217e3ec7017SPing-Ke Shih
218e3ec7017SPing-Ke Shih msg->event = event;
219e3ec7017SPing-Ke Shih
220e3ec7017SPing-Ke Shih spin_lock_irq(&ser->msg_q_lock);
221e3ec7017SPing-Ke Shih list_add(&msg->list, &ser->msg_q);
222e3ec7017SPing-Ke Shih spin_unlock_irq(&ser->msg_q_lock);
223e3ec7017SPing-Ke Shih
224e3ec7017SPing-Ke Shih ieee80211_queue_work(rtwdev->hw, &ser->ser_hdl_work);
225e3ec7017SPing-Ke Shih return 0;
226e3ec7017SPing-Ke Shih }
227e3ec7017SPing-Ke Shih
rtw89_ser_alarm_work(struct work_struct * work)228e3ec7017SPing-Ke Shih static void rtw89_ser_alarm_work(struct work_struct *work)
229e3ec7017SPing-Ke Shih {
230e3ec7017SPing-Ke Shih struct rtw89_ser *ser = container_of(work, struct rtw89_ser,
231e3ec7017SPing-Ke Shih ser_alarm_work.work);
232e3ec7017SPing-Ke Shih
233e3ec7017SPing-Ke Shih ser_send_msg(ser, ser->alarm_event);
234e3ec7017SPing-Ke Shih ser->alarm_event = SER_EV_NONE;
235e3ec7017SPing-Ke Shih }
236e3ec7017SPing-Ke Shih
ser_set_alarm(struct rtw89_ser * ser,u32 ms,u8 event)237e3ec7017SPing-Ke Shih static void ser_set_alarm(struct rtw89_ser *ser, u32 ms, u8 event)
238e3ec7017SPing-Ke Shih {
239e3ec7017SPing-Ke Shih struct rtw89_dev *rtwdev = container_of(ser, struct rtw89_dev, ser);
240e3ec7017SPing-Ke Shih
241e3ec7017SPing-Ke Shih if (test_bit(RTW89_SER_DRV_STOP_RUN, ser->flags))
242e3ec7017SPing-Ke Shih return;
243e3ec7017SPing-Ke Shih
244e3ec7017SPing-Ke Shih ser->alarm_event = event;
245e3ec7017SPing-Ke Shih ieee80211_queue_delayed_work(rtwdev->hw, &ser->ser_alarm_work,
246e3ec7017SPing-Ke Shih msecs_to_jiffies(ms));
247e3ec7017SPing-Ke Shih }
248e3ec7017SPing-Ke Shih
ser_del_alarm(struct rtw89_ser * ser)249e3ec7017SPing-Ke Shih static void ser_del_alarm(struct rtw89_ser *ser)
250e3ec7017SPing-Ke Shih {
251e3ec7017SPing-Ke Shih cancel_delayed_work(&ser->ser_alarm_work);
252e3ec7017SPing-Ke Shih ser->alarm_event = SER_EV_NONE;
253e3ec7017SPing-Ke Shih }
254e3ec7017SPing-Ke Shih
255e3ec7017SPing-Ke Shih /* driver function */
drv_stop_tx(struct rtw89_ser * ser)256e3ec7017SPing-Ke Shih static void drv_stop_tx(struct rtw89_ser *ser)
257e3ec7017SPing-Ke Shih {
258e3ec7017SPing-Ke Shih struct rtw89_dev *rtwdev = container_of(ser, struct rtw89_dev, ser);
259e3ec7017SPing-Ke Shih
260e3ec7017SPing-Ke Shih ieee80211_stop_queues(rtwdev->hw);
261e3ec7017SPing-Ke Shih set_bit(RTW89_SER_DRV_STOP_TX, ser->flags);
262e3ec7017SPing-Ke Shih }
263e3ec7017SPing-Ke Shih
drv_stop_rx(struct rtw89_ser * ser)264e3ec7017SPing-Ke Shih static void drv_stop_rx(struct rtw89_ser *ser)
265e3ec7017SPing-Ke Shih {
266e3ec7017SPing-Ke Shih struct rtw89_dev *rtwdev = container_of(ser, struct rtw89_dev, ser);
267e3ec7017SPing-Ke Shih
268e3ec7017SPing-Ke Shih clear_bit(RTW89_FLAG_RUNNING, rtwdev->flags);
269e3ec7017SPing-Ke Shih set_bit(RTW89_SER_DRV_STOP_RX, ser->flags);
270e3ec7017SPing-Ke Shih }
271e3ec7017SPing-Ke Shih
drv_trx_reset(struct rtw89_ser * ser)272e3ec7017SPing-Ke Shih static void drv_trx_reset(struct rtw89_ser *ser)
273e3ec7017SPing-Ke Shih {
274e3ec7017SPing-Ke Shih struct rtw89_dev *rtwdev = container_of(ser, struct rtw89_dev, ser);
275e3ec7017SPing-Ke Shih
276e3ec7017SPing-Ke Shih rtw89_hci_reset(rtwdev);
277e3ec7017SPing-Ke Shih }
278e3ec7017SPing-Ke Shih
drv_resume_tx(struct rtw89_ser * ser)279e3ec7017SPing-Ke Shih static void drv_resume_tx(struct rtw89_ser *ser)
280e3ec7017SPing-Ke Shih {
281e3ec7017SPing-Ke Shih struct rtw89_dev *rtwdev = container_of(ser, struct rtw89_dev, ser);
282e3ec7017SPing-Ke Shih
283e3ec7017SPing-Ke Shih if (!test_bit(RTW89_SER_DRV_STOP_TX, ser->flags))
284e3ec7017SPing-Ke Shih return;
285e3ec7017SPing-Ke Shih
286e3ec7017SPing-Ke Shih ieee80211_wake_queues(rtwdev->hw);
287e3ec7017SPing-Ke Shih clear_bit(RTW89_SER_DRV_STOP_TX, ser->flags);
288e3ec7017SPing-Ke Shih }
289e3ec7017SPing-Ke Shih
drv_resume_rx(struct rtw89_ser * ser)290e3ec7017SPing-Ke Shih static void drv_resume_rx(struct rtw89_ser *ser)
291e3ec7017SPing-Ke Shih {
292e3ec7017SPing-Ke Shih struct rtw89_dev *rtwdev = container_of(ser, struct rtw89_dev, ser);
293e3ec7017SPing-Ke Shih
294e3ec7017SPing-Ke Shih if (!test_bit(RTW89_SER_DRV_STOP_RX, ser->flags))
295e3ec7017SPing-Ke Shih return;
296e3ec7017SPing-Ke Shih
297e3ec7017SPing-Ke Shih set_bit(RTW89_FLAG_RUNNING, rtwdev->flags);
298e3ec7017SPing-Ke Shih clear_bit(RTW89_SER_DRV_STOP_RX, ser->flags);
299e3ec7017SPing-Ke Shih }
300e3ec7017SPing-Ke Shih
ser_reset_vif(struct rtw89_dev * rtwdev,struct rtw89_vif * rtwvif)301e3ec7017SPing-Ke Shih static void ser_reset_vif(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif)
302e3ec7017SPing-Ke Shih {
303e3ec7017SPing-Ke Shih rtw89_core_release_bit_map(rtwdev->hw_port, rtwvif->port);
304e3ec7017SPing-Ke Shih rtwvif->net_type = RTW89_NET_TYPE_NO_LINK;
305e3ec7017SPing-Ke Shih rtwvif->trigger = false;
306cda66049SZong-Zhe Yang rtwvif->tdls_peer = 0;
307e3ec7017SPing-Ke Shih }
308e3ec7017SPing-Ke Shih
ser_sta_deinit_cam_iter(void * data,struct ieee80211_sta * sta)30908aa8077SPing-Ke Shih static void ser_sta_deinit_cam_iter(void *data, struct ieee80211_sta *sta)
310b169f877SZong-Zhe Yang {
311*cea40665SZong-Zhe Yang struct rtw89_vif *target_rtwvif = (struct rtw89_vif *)data;
312b169f877SZong-Zhe Yang struct rtw89_sta *rtwsta = (struct rtw89_sta *)sta->drv_priv;
313*cea40665SZong-Zhe Yang struct rtw89_vif *rtwvif = rtwsta->rtwvif;
314*cea40665SZong-Zhe Yang struct rtw89_dev *rtwdev = rtwvif->rtwdev;
315*cea40665SZong-Zhe Yang
316*cea40665SZong-Zhe Yang if (rtwvif != target_rtwvif)
317*cea40665SZong-Zhe Yang return;
318b169f877SZong-Zhe Yang
3197312100dSPing-Ke Shih if (rtwvif->net_type == RTW89_NET_TYPE_AP_MODE || sta->tdls)
320b169f877SZong-Zhe Yang rtw89_cam_deinit_addr_cam(rtwdev, &rtwsta->addr_cam);
32139913cc8SPing-Ke Shih if (sta->tdls)
32239913cc8SPing-Ke Shih rtw89_cam_deinit_bssid_cam(rtwdev, &rtwsta->bssid_cam);
32308aa8077SPing-Ke Shih
32408aa8077SPing-Ke Shih INIT_LIST_HEAD(&rtwsta->ba_cam_list);
325b169f877SZong-Zhe Yang }
326b169f877SZong-Zhe Yang
ser_deinit_cam(struct rtw89_dev * rtwdev,struct rtw89_vif * rtwvif)327b169f877SZong-Zhe Yang static void ser_deinit_cam(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif)
328b169f877SZong-Zhe Yang {
329b169f877SZong-Zhe Yang ieee80211_iterate_stations_atomic(rtwdev->hw,
33008aa8077SPing-Ke Shih ser_sta_deinit_cam_iter,
3317312100dSPing-Ke Shih rtwvif);
332b169f877SZong-Zhe Yang
333b169f877SZong-Zhe Yang rtw89_cam_deinit(rtwdev, rtwvif);
33408aa8077SPing-Ke Shih
33508aa8077SPing-Ke Shih bitmap_zero(rtwdev->cam_info.ba_cam_map, RTW89_MAX_BA_CAM_NUM);
336b169f877SZong-Zhe Yang }
337b169f877SZong-Zhe Yang
ser_reset_mac_binding(struct rtw89_dev * rtwdev)338e3ec7017SPing-Ke Shih static void ser_reset_mac_binding(struct rtw89_dev *rtwdev)
339e3ec7017SPing-Ke Shih {
340e3ec7017SPing-Ke Shih struct rtw89_vif *rtwvif;
341e3ec7017SPing-Ke Shih
342e3ec7017SPing-Ke Shih rtw89_cam_reset_keys(rtwdev);
343b169f877SZong-Zhe Yang rtw89_for_each_rtwvif(rtwdev, rtwvif)
344b169f877SZong-Zhe Yang ser_deinit_cam(rtwdev, rtwvif);
345b169f877SZong-Zhe Yang
346e3ec7017SPing-Ke Shih rtw89_core_release_all_bits_map(rtwdev->mac_id_map, RTW89_MAX_MAC_ID_NUM);
347e3ec7017SPing-Ke Shih rtw89_for_each_rtwvif(rtwdev, rtwvif)
348e3ec7017SPing-Ke Shih ser_reset_vif(rtwdev, rtwvif);
349cda66049SZong-Zhe Yang
350cda66049SZong-Zhe Yang rtwdev->total_sta_assoc = 0;
351e3ec7017SPing-Ke Shih }
352e3ec7017SPing-Ke Shih
353e3ec7017SPing-Ke Shih /* hal function */
hal_enable_dma(struct rtw89_ser * ser)354e3ec7017SPing-Ke Shih static int hal_enable_dma(struct rtw89_ser *ser)
355e3ec7017SPing-Ke Shih {
356e3ec7017SPing-Ke Shih struct rtw89_dev *rtwdev = container_of(ser, struct rtw89_dev, ser);
357e3ec7017SPing-Ke Shih int ret;
358e3ec7017SPing-Ke Shih
359e3ec7017SPing-Ke Shih if (!test_bit(RTW89_SER_HAL_STOP_DMA, ser->flags))
360e3ec7017SPing-Ke Shih return 0;
361e3ec7017SPing-Ke Shih
362e3ec7017SPing-Ke Shih if (!rtwdev->hci.ops->mac_lv1_rcvy)
363e3ec7017SPing-Ke Shih return -EIO;
364e3ec7017SPing-Ke Shih
365e3ec7017SPing-Ke Shih ret = rtwdev->hci.ops->mac_lv1_rcvy(rtwdev, RTW89_LV1_RCVY_STEP_2);
366e3ec7017SPing-Ke Shih if (!ret)
367e3ec7017SPing-Ke Shih clear_bit(RTW89_SER_HAL_STOP_DMA, ser->flags);
368d720cca7SZong-Zhe Yang else
369d720cca7SZong-Zhe Yang rtw89_debug(rtwdev, RTW89_DBG_SER,
370d720cca7SZong-Zhe Yang "lv1 rcvy fail to start dma: %d\n", ret);
371e3ec7017SPing-Ke Shih
372e3ec7017SPing-Ke Shih return ret;
373e3ec7017SPing-Ke Shih }
374e3ec7017SPing-Ke Shih
hal_stop_dma(struct rtw89_ser * ser)375e3ec7017SPing-Ke Shih static int hal_stop_dma(struct rtw89_ser *ser)
376e3ec7017SPing-Ke Shih {
377e3ec7017SPing-Ke Shih struct rtw89_dev *rtwdev = container_of(ser, struct rtw89_dev, ser);
378e3ec7017SPing-Ke Shih int ret;
379e3ec7017SPing-Ke Shih
380e3ec7017SPing-Ke Shih if (!rtwdev->hci.ops->mac_lv1_rcvy)
381e3ec7017SPing-Ke Shih return -EIO;
382e3ec7017SPing-Ke Shih
383e3ec7017SPing-Ke Shih ret = rtwdev->hci.ops->mac_lv1_rcvy(rtwdev, RTW89_LV1_RCVY_STEP_1);
384e3ec7017SPing-Ke Shih if (!ret)
385e3ec7017SPing-Ke Shih set_bit(RTW89_SER_HAL_STOP_DMA, ser->flags);
386d720cca7SZong-Zhe Yang else
387d720cca7SZong-Zhe Yang rtw89_debug(rtwdev, RTW89_DBG_SER,
388d720cca7SZong-Zhe Yang "lv1 rcvy fail to stop dma: %d\n", ret);
389e3ec7017SPing-Ke Shih
390e3ec7017SPing-Ke Shih return ret;
391e3ec7017SPing-Ke Shih }
392e3ec7017SPing-Ke Shih
hal_send_post_m0_event(struct rtw89_ser * ser)39356617fd0SZong-Zhe Yang static void hal_send_post_m0_event(struct rtw89_ser *ser)
39456617fd0SZong-Zhe Yang {
39556617fd0SZong-Zhe Yang struct rtw89_dev *rtwdev = container_of(ser, struct rtw89_dev, ser);
39656617fd0SZong-Zhe Yang
39756617fd0SZong-Zhe Yang rtw89_mac_set_err_status(rtwdev, MAC_AX_ERR_L1_RESET_START_DMAC);
39856617fd0SZong-Zhe Yang }
39956617fd0SZong-Zhe Yang
hal_send_m2_event(struct rtw89_ser * ser)400e3ec7017SPing-Ke Shih static void hal_send_m2_event(struct rtw89_ser *ser)
401e3ec7017SPing-Ke Shih {
402e3ec7017SPing-Ke Shih struct rtw89_dev *rtwdev = container_of(ser, struct rtw89_dev, ser);
403e3ec7017SPing-Ke Shih
404e3ec7017SPing-Ke Shih rtw89_mac_set_err_status(rtwdev, MAC_AX_ERR_L1_DISABLE_EN);
405e3ec7017SPing-Ke Shih }
406e3ec7017SPing-Ke Shih
hal_send_m4_event(struct rtw89_ser * ser)407e3ec7017SPing-Ke Shih static void hal_send_m4_event(struct rtw89_ser *ser)
408e3ec7017SPing-Ke Shih {
409e3ec7017SPing-Ke Shih struct rtw89_dev *rtwdev = container_of(ser, struct rtw89_dev, ser);
410e3ec7017SPing-Ke Shih
411e3ec7017SPing-Ke Shih rtw89_mac_set_err_status(rtwdev, MAC_AX_ERR_L1_RCVY_EN);
412e3ec7017SPing-Ke Shih }
413e3ec7017SPing-Ke Shih
414e3ec7017SPing-Ke Shih /* state handler */
ser_idle_st_hdl(struct rtw89_ser * ser,u8 evt)415e3ec7017SPing-Ke Shih static void ser_idle_st_hdl(struct rtw89_ser *ser, u8 evt)
416e3ec7017SPing-Ke Shih {
41714f9f479SZong-Zhe Yang struct rtw89_dev *rtwdev = container_of(ser, struct rtw89_dev, ser);
41814f9f479SZong-Zhe Yang
419e3ec7017SPing-Ke Shih switch (evt) {
420e3ec7017SPing-Ke Shih case SER_EV_STATE_IN:
42114f9f479SZong-Zhe Yang rtw89_hci_recovery_complete(rtwdev);
422b79a84fbSZong-Zhe Yang clear_bit(RTW89_FLAG_SER_HANDLING, rtwdev->flags);
4238a1f6c88SZong-Zhe Yang clear_bit(RTW89_FLAG_CRASH_SIMULATING, rtwdev->flags);
424e3ec7017SPing-Ke Shih break;
42556617fd0SZong-Zhe Yang case SER_EV_L1_RESET_PREPARE:
42656617fd0SZong-Zhe Yang ser_state_goto(ser, SER_L1_RESET_PRE_ST);
42756617fd0SZong-Zhe Yang break;
428e3ec7017SPing-Ke Shih case SER_EV_L1_RESET:
429e3ec7017SPing-Ke Shih ser_state_goto(ser, SER_RESET_TRX_ST);
430e3ec7017SPing-Ke Shih break;
431e3ec7017SPing-Ke Shih case SER_EV_L2_RESET:
432e3ec7017SPing-Ke Shih ser_state_goto(ser, SER_L2_RESET_ST);
433e3ec7017SPing-Ke Shih break;
434e3ec7017SPing-Ke Shih case SER_EV_STATE_OUT:
435b79a84fbSZong-Zhe Yang set_bit(RTW89_FLAG_SER_HANDLING, rtwdev->flags);
43614f9f479SZong-Zhe Yang rtw89_hci_recovery_start(rtwdev);
4375ddfffd6SZong-Zhe Yang break;
438e3ec7017SPing-Ke Shih default:
439e3ec7017SPing-Ke Shih break;
440e3ec7017SPing-Ke Shih }
441e3ec7017SPing-Ke Shih }
442e3ec7017SPing-Ke Shih
ser_l1_reset_pre_st_hdl(struct rtw89_ser * ser,u8 evt)44356617fd0SZong-Zhe Yang static void ser_l1_reset_pre_st_hdl(struct rtw89_ser *ser, u8 evt)
44456617fd0SZong-Zhe Yang {
44556617fd0SZong-Zhe Yang switch (evt) {
44656617fd0SZong-Zhe Yang case SER_EV_STATE_IN:
44756617fd0SZong-Zhe Yang ser->prehandle_l1 = true;
44856617fd0SZong-Zhe Yang hal_send_post_m0_event(ser);
44956617fd0SZong-Zhe Yang ser_set_alarm(ser, 1000, SER_EV_M1_TIMEOUT);
45056617fd0SZong-Zhe Yang break;
45156617fd0SZong-Zhe Yang case SER_EV_L1_RESET:
45256617fd0SZong-Zhe Yang ser_state_goto(ser, SER_RESET_TRX_ST);
45356617fd0SZong-Zhe Yang break;
45456617fd0SZong-Zhe Yang case SER_EV_M1_TIMEOUT:
45556617fd0SZong-Zhe Yang ser_state_goto(ser, SER_L2_RESET_ST);
45656617fd0SZong-Zhe Yang break;
45756617fd0SZong-Zhe Yang case SER_EV_STATE_OUT:
45856617fd0SZong-Zhe Yang ser_del_alarm(ser);
45956617fd0SZong-Zhe Yang break;
46056617fd0SZong-Zhe Yang default:
46156617fd0SZong-Zhe Yang break;
46256617fd0SZong-Zhe Yang }
46356617fd0SZong-Zhe Yang }
46456617fd0SZong-Zhe Yang
ser_reset_trx_st_hdl(struct rtw89_ser * ser,u8 evt)465e3ec7017SPing-Ke Shih static void ser_reset_trx_st_hdl(struct rtw89_ser *ser, u8 evt)
466e3ec7017SPing-Ke Shih {
4675c48f943SChih-Kang Chang struct rtw89_dev *rtwdev = container_of(ser, struct rtw89_dev, ser);
4685c48f943SChih-Kang Chang
469e3ec7017SPing-Ke Shih switch (evt) {
470e3ec7017SPing-Ke Shih case SER_EV_STATE_IN:
4715c48f943SChih-Kang Chang cancel_delayed_work_sync(&rtwdev->track_work);
472e3ec7017SPing-Ke Shih drv_stop_tx(ser);
473e3ec7017SPing-Ke Shih
474e3ec7017SPing-Ke Shih if (hal_stop_dma(ser)) {
475e3ec7017SPing-Ke Shih ser_state_goto(ser, SER_L2_RESET_ST);
476e3ec7017SPing-Ke Shih break;
477e3ec7017SPing-Ke Shih }
478e3ec7017SPing-Ke Shih
479e3ec7017SPing-Ke Shih drv_stop_rx(ser);
480e3ec7017SPing-Ke Shih drv_trx_reset(ser);
481e3ec7017SPing-Ke Shih
482e3ec7017SPing-Ke Shih /* wait m3 */
483e3ec7017SPing-Ke Shih hal_send_m2_event(ser);
484e3ec7017SPing-Ke Shih
485e3ec7017SPing-Ke Shih /* set alarm to prevent FW response timeout */
486e3ec7017SPing-Ke Shih ser_set_alarm(ser, 1000, SER_EV_M3_TIMEOUT);
487e3ec7017SPing-Ke Shih break;
488e3ec7017SPing-Ke Shih
489e3ec7017SPing-Ke Shih case SER_EV_DO_RECOVERY:
490e3ec7017SPing-Ke Shih ser_state_goto(ser, SER_DO_HCI_ST);
491e3ec7017SPing-Ke Shih break;
492e3ec7017SPing-Ke Shih
493e3ec7017SPing-Ke Shih case SER_EV_M3_TIMEOUT:
494e3ec7017SPing-Ke Shih ser_state_goto(ser, SER_L2_RESET_ST);
495e3ec7017SPing-Ke Shih break;
496e3ec7017SPing-Ke Shih
497e3ec7017SPing-Ke Shih case SER_EV_STATE_OUT:
498e3ec7017SPing-Ke Shih ser_del_alarm(ser);
499e3ec7017SPing-Ke Shih hal_enable_dma(ser);
500e3ec7017SPing-Ke Shih drv_resume_rx(ser);
501e3ec7017SPing-Ke Shih drv_resume_tx(ser);
5025c48f943SChih-Kang Chang ieee80211_queue_delayed_work(rtwdev->hw, &rtwdev->track_work,
5035c48f943SChih-Kang Chang RTW89_TRACK_WORK_PERIOD);
504e3ec7017SPing-Ke Shih break;
505e3ec7017SPing-Ke Shih
506e3ec7017SPing-Ke Shih default:
507e3ec7017SPing-Ke Shih break;
508e3ec7017SPing-Ke Shih }
509e3ec7017SPing-Ke Shih }
510e3ec7017SPing-Ke Shih
ser_do_hci_st_hdl(struct rtw89_ser * ser,u8 evt)511e3ec7017SPing-Ke Shih static void ser_do_hci_st_hdl(struct rtw89_ser *ser, u8 evt)
512e3ec7017SPing-Ke Shih {
513e3ec7017SPing-Ke Shih switch (evt) {
514e3ec7017SPing-Ke Shih case SER_EV_STATE_IN:
515e3ec7017SPing-Ke Shih /* wait m5 */
516e3ec7017SPing-Ke Shih hal_send_m4_event(ser);
517e3ec7017SPing-Ke Shih
518e3ec7017SPing-Ke Shih /* prevent FW response timeout */
519e3ec7017SPing-Ke Shih ser_set_alarm(ser, 1000, SER_EV_FW_M5_TIMEOUT);
520e3ec7017SPing-Ke Shih break;
521e3ec7017SPing-Ke Shih
522e3ec7017SPing-Ke Shih case SER_EV_FW_M5_TIMEOUT:
523e3ec7017SPing-Ke Shih ser_state_goto(ser, SER_L2_RESET_ST);
524e3ec7017SPing-Ke Shih break;
525e3ec7017SPing-Ke Shih
526e3ec7017SPing-Ke Shih case SER_EV_MAC_RESET_DONE:
527e3ec7017SPing-Ke Shih ser_state_goto(ser, SER_IDLE_ST);
528e3ec7017SPing-Ke Shih break;
529e3ec7017SPing-Ke Shih
530e3ec7017SPing-Ke Shih case SER_EV_STATE_OUT:
531e3ec7017SPing-Ke Shih ser_del_alarm(ser);
532e3ec7017SPing-Ke Shih break;
533e3ec7017SPing-Ke Shih
534e3ec7017SPing-Ke Shih default:
535e3ec7017SPing-Ke Shih break;
536e3ec7017SPing-Ke Shih }
537e3ec7017SPing-Ke Shih }
538e3ec7017SPing-Ke Shih
ser_mac_mem_dump(struct rtw89_dev * rtwdev,u8 * buf,u8 sel,u32 start_addr,u32 len)5399f8004bfSZong-Zhe Yang static void ser_mac_mem_dump(struct rtw89_dev *rtwdev, u8 *buf,
5409f8004bfSZong-Zhe Yang u8 sel, u32 start_addr, u32 len)
5419f8004bfSZong-Zhe Yang {
54260168f6cSPing-Ke Shih const struct rtw89_mac_gen_def *mac = rtwdev->chip->mac_def;
54360168f6cSPing-Ke Shih u32 filter_model_addr = mac->filter_model_addr;
54460168f6cSPing-Ke Shih u32 indir_access_addr = mac->indir_access_addr;
5459f8004bfSZong-Zhe Yang u32 *ptr = (u32 *)buf;
5469f8004bfSZong-Zhe Yang u32 base_addr, start_page, residue;
5479f8004bfSZong-Zhe Yang u32 cnt = 0;
5489f8004bfSZong-Zhe Yang u32 i;
5499f8004bfSZong-Zhe Yang
5509f8004bfSZong-Zhe Yang start_page = start_addr / MAC_MEM_DUMP_PAGE_SIZE;
5519f8004bfSZong-Zhe Yang residue = start_addr % MAC_MEM_DUMP_PAGE_SIZE;
55260168f6cSPing-Ke Shih base_addr = mac->mem_base_addrs[sel];
5539f8004bfSZong-Zhe Yang base_addr += start_page * MAC_MEM_DUMP_PAGE_SIZE;
5549f8004bfSZong-Zhe Yang
5559f8004bfSZong-Zhe Yang while (cnt < len) {
55660168f6cSPing-Ke Shih rtw89_write32(rtwdev, filter_model_addr, base_addr);
5579f8004bfSZong-Zhe Yang
55860168f6cSPing-Ke Shih for (i = indir_access_addr + residue;
55960168f6cSPing-Ke Shih i < indir_access_addr + MAC_MEM_DUMP_PAGE_SIZE;
5609f8004bfSZong-Zhe Yang i += 4, ptr++) {
5619f8004bfSZong-Zhe Yang *ptr = rtw89_read32(rtwdev, i);
5629f8004bfSZong-Zhe Yang cnt += 4;
5639f8004bfSZong-Zhe Yang if (cnt >= len)
5649f8004bfSZong-Zhe Yang break;
5659f8004bfSZong-Zhe Yang }
5669f8004bfSZong-Zhe Yang
5679f8004bfSZong-Zhe Yang residue = 0;
5689f8004bfSZong-Zhe Yang base_addr += MAC_MEM_DUMP_PAGE_SIZE;
5699f8004bfSZong-Zhe Yang }
5709f8004bfSZong-Zhe Yang }
5719f8004bfSZong-Zhe Yang
rtw89_ser_fw_rsvd_ple_dump(struct rtw89_dev * rtwdev,u8 * buf)5729f8004bfSZong-Zhe Yang static void rtw89_ser_fw_rsvd_ple_dump(struct rtw89_dev *rtwdev, u8 *buf)
5739f8004bfSZong-Zhe Yang {
5749f8004bfSZong-Zhe Yang u32 start_addr = rtwdev->chip->rsvd_ple_ofst;
5759f8004bfSZong-Zhe Yang
5769f8004bfSZong-Zhe Yang rtw89_debug(rtwdev, RTW89_DBG_SER,
5779f8004bfSZong-Zhe Yang "dump mem for fw rsvd payload engine (start addr: 0x%x)\n",
5789f8004bfSZong-Zhe Yang start_addr);
5799f8004bfSZong-Zhe Yang ser_mac_mem_dump(rtwdev, buf, RTW89_MAC_MEM_SHARED_BUF, start_addr,
5809f8004bfSZong-Zhe Yang RTW89_FW_RSVD_PLE_SIZE);
5819f8004bfSZong-Zhe Yang }
5829f8004bfSZong-Zhe Yang
583f5e24684SZong-Zhe Yang struct __fw_backtrace_entry {
584f5e24684SZong-Zhe Yang u32 wcpu_addr;
585f5e24684SZong-Zhe Yang u32 size;
586f5e24684SZong-Zhe Yang u32 key;
587f5e24684SZong-Zhe Yang } __packed;
588f5e24684SZong-Zhe Yang
589f5e24684SZong-Zhe Yang struct __fw_backtrace_info {
590f5e24684SZong-Zhe Yang u32 ra;
591f5e24684SZong-Zhe Yang u32 sp;
592f5e24684SZong-Zhe Yang } __packed;
593f5e24684SZong-Zhe Yang
594f5e24684SZong-Zhe Yang static_assert(RTW89_FW_BACKTRACE_INFO_SIZE ==
595f5e24684SZong-Zhe Yang sizeof(struct __fw_backtrace_info));
596f5e24684SZong-Zhe Yang
convert_addr_from_wcpu(u32 wcpu_addr)597c5ece8d8SZong-Zhe Yang static u32 convert_addr_from_wcpu(u32 wcpu_addr)
598c5ece8d8SZong-Zhe Yang {
599c5ece8d8SZong-Zhe Yang if (wcpu_addr < 0x30000000)
600c5ece8d8SZong-Zhe Yang return wcpu_addr;
601c5ece8d8SZong-Zhe Yang
602c5ece8d8SZong-Zhe Yang return wcpu_addr & GENMASK(28, 0);
603c5ece8d8SZong-Zhe Yang }
604c5ece8d8SZong-Zhe Yang
rtw89_ser_fw_backtrace_dump(struct rtw89_dev * rtwdev,u8 * buf,const struct __fw_backtrace_entry * ent)605f5e24684SZong-Zhe Yang static int rtw89_ser_fw_backtrace_dump(struct rtw89_dev *rtwdev, u8 *buf,
606f5e24684SZong-Zhe Yang const struct __fw_backtrace_entry *ent)
607f5e24684SZong-Zhe Yang {
608f5e24684SZong-Zhe Yang struct __fw_backtrace_info *ptr = (struct __fw_backtrace_info *)buf;
60960168f6cSPing-Ke Shih const struct rtw89_mac_gen_def *mac = rtwdev->chip->mac_def;
61060168f6cSPing-Ke Shih u32 filter_model_addr = mac->filter_model_addr;
61160168f6cSPing-Ke Shih u32 indir_access_addr = mac->indir_access_addr;
612c5ece8d8SZong-Zhe Yang u32 fwbt_addr = convert_addr_from_wcpu(ent->wcpu_addr);
613f5e24684SZong-Zhe Yang u32 fwbt_size = ent->size;
614f5e24684SZong-Zhe Yang u32 fwbt_key = ent->key;
615f5e24684SZong-Zhe Yang u32 i;
616f5e24684SZong-Zhe Yang
617f5e24684SZong-Zhe Yang if (fwbt_addr == 0) {
618f5e24684SZong-Zhe Yang rtw89_warn(rtwdev, "FW backtrace invalid address: 0x%x\n",
619f5e24684SZong-Zhe Yang fwbt_addr);
620f5e24684SZong-Zhe Yang return -EINVAL;
621f5e24684SZong-Zhe Yang }
622f5e24684SZong-Zhe Yang
623f5e24684SZong-Zhe Yang if (fwbt_key != RTW89_FW_BACKTRACE_KEY) {
624f5e24684SZong-Zhe Yang rtw89_warn(rtwdev, "FW backtrace invalid key: 0x%x\n",
625f5e24684SZong-Zhe Yang fwbt_key);
626f5e24684SZong-Zhe Yang return -EINVAL;
627f5e24684SZong-Zhe Yang }
628f5e24684SZong-Zhe Yang
629f5e24684SZong-Zhe Yang if (fwbt_size == 0 || !RTW89_VALID_FW_BACKTRACE_SIZE(fwbt_size) ||
630f5e24684SZong-Zhe Yang fwbt_size > RTW89_FW_BACKTRACE_MAX_SIZE) {
631f5e24684SZong-Zhe Yang rtw89_warn(rtwdev, "FW backtrace invalid size: 0x%x\n",
632f5e24684SZong-Zhe Yang fwbt_size);
633f5e24684SZong-Zhe Yang return -EINVAL;
634f5e24684SZong-Zhe Yang }
635f5e24684SZong-Zhe Yang
636f5e24684SZong-Zhe Yang rtw89_debug(rtwdev, RTW89_DBG_SER, "dump fw backtrace start\n");
63760168f6cSPing-Ke Shih rtw89_write32(rtwdev, filter_model_addr, fwbt_addr);
638f5e24684SZong-Zhe Yang
63960168f6cSPing-Ke Shih for (i = indir_access_addr;
64060168f6cSPing-Ke Shih i < indir_access_addr + fwbt_size;
641f5e24684SZong-Zhe Yang i += RTW89_FW_BACKTRACE_INFO_SIZE, ptr++) {
642f5e24684SZong-Zhe Yang *ptr = (struct __fw_backtrace_info){
643f5e24684SZong-Zhe Yang .ra = rtw89_read32(rtwdev, i),
644f5e24684SZong-Zhe Yang .sp = rtw89_read32(rtwdev, i + 4),
645f5e24684SZong-Zhe Yang };
646f5e24684SZong-Zhe Yang rtw89_debug(rtwdev, RTW89_DBG_SER,
647f5e24684SZong-Zhe Yang "next sp: 0x%x, next ra: 0x%x\n",
648f5e24684SZong-Zhe Yang ptr->sp, ptr->ra);
649f5e24684SZong-Zhe Yang }
650f5e24684SZong-Zhe Yang
651f5e24684SZong-Zhe Yang rtw89_debug(rtwdev, RTW89_DBG_SER, "dump fw backtrace end\n");
652f5e24684SZong-Zhe Yang return 0;
653f5e24684SZong-Zhe Yang }
654f5e24684SZong-Zhe Yang
ser_l2_reset_st_pre_hdl(struct rtw89_ser * ser)6559f8004bfSZong-Zhe Yang static void ser_l2_reset_st_pre_hdl(struct rtw89_ser *ser)
6569f8004bfSZong-Zhe Yang {
6579f8004bfSZong-Zhe Yang struct rtw89_dev *rtwdev = container_of(ser, struct rtw89_dev, ser);
6589f8004bfSZong-Zhe Yang struct rtw89_ser_cd_buffer *buf;
659f5e24684SZong-Zhe Yang struct __fw_backtrace_entry fwbt_ent;
660f5e24684SZong-Zhe Yang int ret = 0;
6619f8004bfSZong-Zhe Yang
6629f8004bfSZong-Zhe Yang buf = rtw89_ser_cd_prep(rtwdev);
663f5e24684SZong-Zhe Yang if (!buf) {
664f5e24684SZong-Zhe Yang ret = -ENOMEM;
6659f8004bfSZong-Zhe Yang goto bottom;
666f5e24684SZong-Zhe Yang }
6679f8004bfSZong-Zhe Yang
6689f8004bfSZong-Zhe Yang rtw89_ser_fw_rsvd_ple_dump(rtwdev, buf->fwple.data);
669f5e24684SZong-Zhe Yang
670f5e24684SZong-Zhe Yang fwbt_ent = *(struct __fw_backtrace_entry *)buf->fwple.data;
671f5e24684SZong-Zhe Yang ret = rtw89_ser_fw_backtrace_dump(rtwdev, buf->fwbt.data, &fwbt_ent);
672f5e24684SZong-Zhe Yang if (ret)
673f5e24684SZong-Zhe Yang goto bottom;
674f5e24684SZong-Zhe Yang
6759f8004bfSZong-Zhe Yang rtw89_ser_cd_send(rtwdev, buf);
6769f8004bfSZong-Zhe Yang
6779f8004bfSZong-Zhe Yang bottom:
678f5e24684SZong-Zhe Yang rtw89_ser_cd_free(rtwdev, buf, !!ret);
679f5e24684SZong-Zhe Yang
6809f8004bfSZong-Zhe Yang ser_reset_mac_binding(rtwdev);
6819f8004bfSZong-Zhe Yang rtw89_core_stop(rtwdev);
682a88b6cc4SZong-Zhe Yang rtw89_entity_init(rtwdev);
6835c12bb66SChin-Yen Lee rtw89_fw_release_general_pkt_list(rtwdev, false);
6849f8004bfSZong-Zhe Yang INIT_LIST_HEAD(&rtwdev->rtwvifs_list);
6859f8004bfSZong-Zhe Yang }
6869f8004bfSZong-Zhe Yang
ser_l2_reset_st_hdl(struct rtw89_ser * ser,u8 evt)687e3ec7017SPing-Ke Shih static void ser_l2_reset_st_hdl(struct rtw89_ser *ser, u8 evt)
688e3ec7017SPing-Ke Shih {
689e3ec7017SPing-Ke Shih struct rtw89_dev *rtwdev = container_of(ser, struct rtw89_dev, ser);
690e3ec7017SPing-Ke Shih
691e3ec7017SPing-Ke Shih switch (evt) {
692e3ec7017SPing-Ke Shih case SER_EV_STATE_IN:
693e3ec7017SPing-Ke Shih mutex_lock(&rtwdev->mutex);
6949f8004bfSZong-Zhe Yang ser_l2_reset_st_pre_hdl(ser);
695e3ec7017SPing-Ke Shih mutex_unlock(&rtwdev->mutex);
696e3ec7017SPing-Ke Shih
697e3ec7017SPing-Ke Shih ieee80211_restart_hw(rtwdev->hw);
698e3ec7017SPing-Ke Shih ser_set_alarm(ser, SER_RECFG_TIMEOUT, SER_EV_L2_RECFG_TIMEOUT);
699e3ec7017SPing-Ke Shih break;
700e3ec7017SPing-Ke Shih
701e3ec7017SPing-Ke Shih case SER_EV_L2_RECFG_TIMEOUT:
702e3ec7017SPing-Ke Shih rtw89_info(rtwdev, "Err: ser L2 re-config timeout\n");
703e3ec7017SPing-Ke Shih fallthrough;
704e3ec7017SPing-Ke Shih case SER_EV_L2_RECFG_DONE:
705e3ec7017SPing-Ke Shih ser_state_goto(ser, SER_IDLE_ST);
706e3ec7017SPing-Ke Shih break;
707e3ec7017SPing-Ke Shih
708e3ec7017SPing-Ke Shih case SER_EV_STATE_OUT:
709e3ec7017SPing-Ke Shih ser_del_alarm(ser);
710e3ec7017SPing-Ke Shih break;
711e3ec7017SPing-Ke Shih
712e3ec7017SPing-Ke Shih default:
713e3ec7017SPing-Ke Shih break;
714e3ec7017SPing-Ke Shih }
715e3ec7017SPing-Ke Shih }
716e3ec7017SPing-Ke Shih
717af5175acSJoe Perches static const struct event_ent ser_ev_tbl[] = {
718e3ec7017SPing-Ke Shih {SER_EV_NONE, "SER_EV_NONE"},
719e3ec7017SPing-Ke Shih {SER_EV_STATE_IN, "SER_EV_STATE_IN"},
720e3ec7017SPing-Ke Shih {SER_EV_STATE_OUT, "SER_EV_STATE_OUT"},
72156617fd0SZong-Zhe Yang {SER_EV_L1_RESET_PREPARE, "SER_EV_L1_RESET_PREPARE pre-m0"},
72256617fd0SZong-Zhe Yang {SER_EV_L1_RESET, "SER_EV_L1_RESET m1"},
723e3ec7017SPing-Ke Shih {SER_EV_DO_RECOVERY, "SER_EV_DO_RECOVERY m3"},
724e3ec7017SPing-Ke Shih {SER_EV_MAC_RESET_DONE, "SER_EV_MAC_RESET_DONE m5"},
725e3ec7017SPing-Ke Shih {SER_EV_L2_RESET, "SER_EV_L2_RESET"},
726e3ec7017SPing-Ke Shih {SER_EV_L2_RECFG_DONE, "SER_EV_L2_RECFG_DONE"},
727e3ec7017SPing-Ke Shih {SER_EV_L2_RECFG_TIMEOUT, "SER_EV_L2_RECFG_TIMEOUT"},
72856617fd0SZong-Zhe Yang {SER_EV_M1_TIMEOUT, "SER_EV_M1_TIMEOUT"},
729e3ec7017SPing-Ke Shih {SER_EV_M3_TIMEOUT, "SER_EV_M3_TIMEOUT"},
730e3ec7017SPing-Ke Shih {SER_EV_FW_M5_TIMEOUT, "SER_EV_FW_M5_TIMEOUT"},
731e3ec7017SPing-Ke Shih {SER_EV_L0_RESET, "SER_EV_L0_RESET"},
732e3ec7017SPing-Ke Shih {SER_EV_MAXX, "SER_EV_MAX"}
733e3ec7017SPing-Ke Shih };
734e3ec7017SPing-Ke Shih
735af5175acSJoe Perches static const struct state_ent ser_st_tbl[] = {
736e3ec7017SPing-Ke Shih {SER_IDLE_ST, "SER_IDLE_ST", ser_idle_st_hdl},
73756617fd0SZong-Zhe Yang {SER_L1_RESET_PRE_ST, "SER_L1_RESET_PRE_ST", ser_l1_reset_pre_st_hdl},
738e3ec7017SPing-Ke Shih {SER_RESET_TRX_ST, "SER_RESET_TRX_ST", ser_reset_trx_st_hdl},
739e3ec7017SPing-Ke Shih {SER_DO_HCI_ST, "SER_DO_HCI_ST", ser_do_hci_st_hdl},
740e3ec7017SPing-Ke Shih {SER_L2_RESET_ST, "SER_L2_RESET_ST", ser_l2_reset_st_hdl}
741e3ec7017SPing-Ke Shih };
742e3ec7017SPing-Ke Shih
rtw89_ser_init(struct rtw89_dev * rtwdev)743e3ec7017SPing-Ke Shih int rtw89_ser_init(struct rtw89_dev *rtwdev)
744e3ec7017SPing-Ke Shih {
745e3ec7017SPing-Ke Shih struct rtw89_ser *ser = &rtwdev->ser;
746e3ec7017SPing-Ke Shih
747e3ec7017SPing-Ke Shih memset(ser, 0, sizeof(*ser));
748e3ec7017SPing-Ke Shih INIT_LIST_HEAD(&ser->msg_q);
749e3ec7017SPing-Ke Shih ser->state = SER_IDLE_ST;
750e3ec7017SPing-Ke Shih ser->st_tbl = ser_st_tbl;
751e3ec7017SPing-Ke Shih ser->ev_tbl = ser_ev_tbl;
752e3ec7017SPing-Ke Shih
753e3ec7017SPing-Ke Shih bitmap_zero(ser->flags, RTW89_NUM_OF_SER_FLAGS);
754e3ec7017SPing-Ke Shih spin_lock_init(&ser->msg_q_lock);
755e3ec7017SPing-Ke Shih INIT_WORK(&ser->ser_hdl_work, rtw89_ser_hdl_work);
756e3ec7017SPing-Ke Shih INIT_DELAYED_WORK(&ser->ser_alarm_work, rtw89_ser_alarm_work);
757e3ec7017SPing-Ke Shih return 0;
758e3ec7017SPing-Ke Shih }
759e3ec7017SPing-Ke Shih
rtw89_ser_deinit(struct rtw89_dev * rtwdev)760e3ec7017SPing-Ke Shih int rtw89_ser_deinit(struct rtw89_dev *rtwdev)
761e3ec7017SPing-Ke Shih {
762e3ec7017SPing-Ke Shih struct rtw89_ser *ser = (struct rtw89_ser *)&rtwdev->ser;
763e3ec7017SPing-Ke Shih
764e3ec7017SPing-Ke Shih set_bit(RTW89_SER_DRV_STOP_RUN, ser->flags);
765e3ec7017SPing-Ke Shih cancel_delayed_work_sync(&ser->ser_alarm_work);
766e3ec7017SPing-Ke Shih cancel_work_sync(&ser->ser_hdl_work);
767e3ec7017SPing-Ke Shih clear_bit(RTW89_SER_DRV_STOP_RUN, ser->flags);
768e3ec7017SPing-Ke Shih return 0;
769e3ec7017SPing-Ke Shih }
770e3ec7017SPing-Ke Shih
rtw89_ser_recfg_done(struct rtw89_dev * rtwdev)771e3ec7017SPing-Ke Shih void rtw89_ser_recfg_done(struct rtw89_dev *rtwdev)
772e3ec7017SPing-Ke Shih {
773e3ec7017SPing-Ke Shih ser_send_msg(&rtwdev->ser, SER_EV_L2_RECFG_DONE);
774e3ec7017SPing-Ke Shih }
775e3ec7017SPing-Ke Shih
rtw89_ser_notify(struct rtw89_dev * rtwdev,u32 err)776e3ec7017SPing-Ke Shih int rtw89_ser_notify(struct rtw89_dev *rtwdev, u32 err)
777e3ec7017SPing-Ke Shih {
778e3ec7017SPing-Ke Shih u8 event = SER_EV_NONE;
779e3ec7017SPing-Ke Shih
780198b6cf7SZong-Zhe Yang rtw89_info(rtwdev, "SER catches error: 0x%x\n", err);
781e3ec7017SPing-Ke Shih
782e3ec7017SPing-Ke Shih switch (err) {
78356617fd0SZong-Zhe Yang case MAC_AX_ERR_L1_PREERR_DMAC: /* pre-M0 */
78456617fd0SZong-Zhe Yang event = SER_EV_L1_RESET_PREPARE;
78556617fd0SZong-Zhe Yang break;
786e3ec7017SPing-Ke Shih case MAC_AX_ERR_L1_ERR_DMAC:
787e3ec7017SPing-Ke Shih case MAC_AX_ERR_L0_PROMOTE_TO_L1:
788e3ec7017SPing-Ke Shih event = SER_EV_L1_RESET; /* M1 */
789e3ec7017SPing-Ke Shih break;
790e3ec7017SPing-Ke Shih case MAC_AX_ERR_L1_RESET_DISABLE_DMAC_DONE:
791e3ec7017SPing-Ke Shih event = SER_EV_DO_RECOVERY; /* M3 */
792e3ec7017SPing-Ke Shih break;
793e3ec7017SPing-Ke Shih case MAC_AX_ERR_L1_RESET_RECOVERY_DONE:
794e3ec7017SPing-Ke Shih event = SER_EV_MAC_RESET_DONE; /* M5 */
795e3ec7017SPing-Ke Shih break;
796e3ec7017SPing-Ke Shih case MAC_AX_ERR_L0_ERR_CMAC0:
797e3ec7017SPing-Ke Shih case MAC_AX_ERR_L0_ERR_CMAC1:
798e3ec7017SPing-Ke Shih case MAC_AX_ERR_L0_RESET_DONE:
799e3ec7017SPing-Ke Shih event = SER_EV_L0_RESET;
800e3ec7017SPing-Ke Shih break;
801e3ec7017SPing-Ke Shih default:
802e3ec7017SPing-Ke Shih if (err == MAC_AX_ERR_L1_PROMOTE_TO_L2 ||
803e3ec7017SPing-Ke Shih (err >= MAC_AX_ERR_L2_ERR_AH_DMA &&
804e3ec7017SPing-Ke Shih err <= MAC_AX_GET_ERR_MAX))
805e3ec7017SPing-Ke Shih event = SER_EV_L2_RESET;
806e3ec7017SPing-Ke Shih break;
807e3ec7017SPing-Ke Shih }
808e3ec7017SPing-Ke Shih
809198b6cf7SZong-Zhe Yang if (event == SER_EV_NONE) {
810198b6cf7SZong-Zhe Yang rtw89_warn(rtwdev, "SER cannot recognize error: 0x%x\n", err);
811e3ec7017SPing-Ke Shih return -EINVAL;
812198b6cf7SZong-Zhe Yang }
813e3ec7017SPing-Ke Shih
814e3ec7017SPing-Ke Shih ser_send_msg(&rtwdev->ser, event);
815e3ec7017SPing-Ke Shih return 0;
816e3ec7017SPing-Ke Shih }
817e3ec7017SPing-Ke Shih EXPORT_SYMBOL(rtw89_ser_notify);
818