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 {
3117312100dSPing-Ke Shih 	struct rtw89_vif *rtwvif = (struct rtw89_vif *)data;
3127312100dSPing-Ke Shih 	struct rtw89_dev *rtwdev = rtwvif->rtwdev;
313b169f877SZong-Zhe Yang 	struct rtw89_sta *rtwsta = (struct rtw89_sta *)sta->drv_priv;
314b169f877SZong-Zhe Yang 
3157312100dSPing-Ke Shih 	if (rtwvif->net_type == RTW89_NET_TYPE_AP_MODE || sta->tdls)
316b169f877SZong-Zhe Yang 		rtw89_cam_deinit_addr_cam(rtwdev, &rtwsta->addr_cam);
31739913cc8SPing-Ke Shih 	if (sta->tdls)
31839913cc8SPing-Ke Shih 		rtw89_cam_deinit_bssid_cam(rtwdev, &rtwsta->bssid_cam);
31908aa8077SPing-Ke Shih 
32008aa8077SPing-Ke Shih 	INIT_LIST_HEAD(&rtwsta->ba_cam_list);
321b169f877SZong-Zhe Yang }
322b169f877SZong-Zhe Yang 
ser_deinit_cam(struct rtw89_dev * rtwdev,struct rtw89_vif * rtwvif)323b169f877SZong-Zhe Yang static void ser_deinit_cam(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif)
324b169f877SZong-Zhe Yang {
325b169f877SZong-Zhe Yang 	ieee80211_iterate_stations_atomic(rtwdev->hw,
32608aa8077SPing-Ke Shih 					  ser_sta_deinit_cam_iter,
3277312100dSPing-Ke Shih 					  rtwvif);
328b169f877SZong-Zhe Yang 
329b169f877SZong-Zhe Yang 	rtw89_cam_deinit(rtwdev, rtwvif);
33008aa8077SPing-Ke Shih 
33108aa8077SPing-Ke Shih 	bitmap_zero(rtwdev->cam_info.ba_cam_map, RTW89_MAX_BA_CAM_NUM);
332b169f877SZong-Zhe Yang }
333b169f877SZong-Zhe Yang 
ser_reset_mac_binding(struct rtw89_dev * rtwdev)334e3ec7017SPing-Ke Shih static void ser_reset_mac_binding(struct rtw89_dev *rtwdev)
335e3ec7017SPing-Ke Shih {
336e3ec7017SPing-Ke Shih 	struct rtw89_vif *rtwvif;
337e3ec7017SPing-Ke Shih 
338e3ec7017SPing-Ke Shih 	rtw89_cam_reset_keys(rtwdev);
339b169f877SZong-Zhe Yang 	rtw89_for_each_rtwvif(rtwdev, rtwvif)
340b169f877SZong-Zhe Yang 		ser_deinit_cam(rtwdev, rtwvif);
341b169f877SZong-Zhe Yang 
342e3ec7017SPing-Ke Shih 	rtw89_core_release_all_bits_map(rtwdev->mac_id_map, RTW89_MAX_MAC_ID_NUM);
343e3ec7017SPing-Ke Shih 	rtw89_for_each_rtwvif(rtwdev, rtwvif)
344e3ec7017SPing-Ke Shih 		ser_reset_vif(rtwdev, rtwvif);
345cda66049SZong-Zhe Yang 
346cda66049SZong-Zhe Yang 	rtwdev->total_sta_assoc = 0;
347e3ec7017SPing-Ke Shih }
348e3ec7017SPing-Ke Shih 
349e3ec7017SPing-Ke Shih /* hal function */
hal_enable_dma(struct rtw89_ser * ser)350e3ec7017SPing-Ke Shih static int hal_enable_dma(struct rtw89_ser *ser)
351e3ec7017SPing-Ke Shih {
352e3ec7017SPing-Ke Shih 	struct rtw89_dev *rtwdev = container_of(ser, struct rtw89_dev, ser);
353e3ec7017SPing-Ke Shih 	int ret;
354e3ec7017SPing-Ke Shih 
355e3ec7017SPing-Ke Shih 	if (!test_bit(RTW89_SER_HAL_STOP_DMA, ser->flags))
356e3ec7017SPing-Ke Shih 		return 0;
357e3ec7017SPing-Ke Shih 
358e3ec7017SPing-Ke Shih 	if (!rtwdev->hci.ops->mac_lv1_rcvy)
359e3ec7017SPing-Ke Shih 		return -EIO;
360e3ec7017SPing-Ke Shih 
361e3ec7017SPing-Ke Shih 	ret = rtwdev->hci.ops->mac_lv1_rcvy(rtwdev, RTW89_LV1_RCVY_STEP_2);
362e3ec7017SPing-Ke Shih 	if (!ret)
363e3ec7017SPing-Ke Shih 		clear_bit(RTW89_SER_HAL_STOP_DMA, ser->flags);
364e3ec7017SPing-Ke Shih 
365e3ec7017SPing-Ke Shih 	return ret;
366e3ec7017SPing-Ke Shih }
367e3ec7017SPing-Ke Shih 
hal_stop_dma(struct rtw89_ser * ser)368e3ec7017SPing-Ke Shih static int hal_stop_dma(struct rtw89_ser *ser)
369e3ec7017SPing-Ke Shih {
370e3ec7017SPing-Ke Shih 	struct rtw89_dev *rtwdev = container_of(ser, struct rtw89_dev, ser);
371e3ec7017SPing-Ke Shih 	int ret;
372e3ec7017SPing-Ke Shih 
373e3ec7017SPing-Ke Shih 	if (!rtwdev->hci.ops->mac_lv1_rcvy)
374e3ec7017SPing-Ke Shih 		return -EIO;
375e3ec7017SPing-Ke Shih 
376e3ec7017SPing-Ke Shih 	ret = rtwdev->hci.ops->mac_lv1_rcvy(rtwdev, RTW89_LV1_RCVY_STEP_1);
377e3ec7017SPing-Ke Shih 	if (!ret)
378e3ec7017SPing-Ke Shih 		set_bit(RTW89_SER_HAL_STOP_DMA, ser->flags);
379e3ec7017SPing-Ke Shih 
380e3ec7017SPing-Ke Shih 	return ret;
381e3ec7017SPing-Ke Shih }
382e3ec7017SPing-Ke Shih 
hal_send_post_m0_event(struct rtw89_ser * ser)38356617fd0SZong-Zhe Yang static void hal_send_post_m0_event(struct rtw89_ser *ser)
38456617fd0SZong-Zhe Yang {
38556617fd0SZong-Zhe Yang 	struct rtw89_dev *rtwdev = container_of(ser, struct rtw89_dev, ser);
38656617fd0SZong-Zhe Yang 
38756617fd0SZong-Zhe Yang 	rtw89_mac_set_err_status(rtwdev, MAC_AX_ERR_L1_RESET_START_DMAC);
38856617fd0SZong-Zhe Yang }
38956617fd0SZong-Zhe Yang 
hal_send_m2_event(struct rtw89_ser * ser)390e3ec7017SPing-Ke Shih static void hal_send_m2_event(struct rtw89_ser *ser)
391e3ec7017SPing-Ke Shih {
392e3ec7017SPing-Ke Shih 	struct rtw89_dev *rtwdev = container_of(ser, struct rtw89_dev, ser);
393e3ec7017SPing-Ke Shih 
394e3ec7017SPing-Ke Shih 	rtw89_mac_set_err_status(rtwdev, MAC_AX_ERR_L1_DISABLE_EN);
395e3ec7017SPing-Ke Shih }
396e3ec7017SPing-Ke Shih 
hal_send_m4_event(struct rtw89_ser * ser)397e3ec7017SPing-Ke Shih static void hal_send_m4_event(struct rtw89_ser *ser)
398e3ec7017SPing-Ke Shih {
399e3ec7017SPing-Ke Shih 	struct rtw89_dev *rtwdev = container_of(ser, struct rtw89_dev, ser);
400e3ec7017SPing-Ke Shih 
401e3ec7017SPing-Ke Shih 	rtw89_mac_set_err_status(rtwdev, MAC_AX_ERR_L1_RCVY_EN);
402e3ec7017SPing-Ke Shih }
403e3ec7017SPing-Ke Shih 
404e3ec7017SPing-Ke Shih /* state handler */
ser_idle_st_hdl(struct rtw89_ser * ser,u8 evt)405e3ec7017SPing-Ke Shih static void ser_idle_st_hdl(struct rtw89_ser *ser, u8 evt)
406e3ec7017SPing-Ke Shih {
40714f9f479SZong-Zhe Yang 	struct rtw89_dev *rtwdev = container_of(ser, struct rtw89_dev, ser);
40814f9f479SZong-Zhe Yang 
409e3ec7017SPing-Ke Shih 	switch (evt) {
410e3ec7017SPing-Ke Shih 	case SER_EV_STATE_IN:
41114f9f479SZong-Zhe Yang 		rtw89_hci_recovery_complete(rtwdev);
412b79a84fbSZong-Zhe Yang 		clear_bit(RTW89_FLAG_SER_HANDLING, rtwdev->flags);
4138a1f6c88SZong-Zhe Yang 		clear_bit(RTW89_FLAG_CRASH_SIMULATING, rtwdev->flags);
414e3ec7017SPing-Ke Shih 		break;
41556617fd0SZong-Zhe Yang 	case SER_EV_L1_RESET_PREPARE:
41656617fd0SZong-Zhe Yang 		ser_state_goto(ser, SER_L1_RESET_PRE_ST);
41756617fd0SZong-Zhe Yang 		break;
418e3ec7017SPing-Ke Shih 	case SER_EV_L1_RESET:
419e3ec7017SPing-Ke Shih 		ser_state_goto(ser, SER_RESET_TRX_ST);
420e3ec7017SPing-Ke Shih 		break;
421e3ec7017SPing-Ke Shih 	case SER_EV_L2_RESET:
422e3ec7017SPing-Ke Shih 		ser_state_goto(ser, SER_L2_RESET_ST);
423e3ec7017SPing-Ke Shih 		break;
424e3ec7017SPing-Ke Shih 	case SER_EV_STATE_OUT:
425b79a84fbSZong-Zhe Yang 		set_bit(RTW89_FLAG_SER_HANDLING, rtwdev->flags);
42614f9f479SZong-Zhe Yang 		rtw89_hci_recovery_start(rtwdev);
4275ddfffd6SZong-Zhe Yang 		break;
428e3ec7017SPing-Ke Shih 	default:
429e3ec7017SPing-Ke Shih 		break;
430e3ec7017SPing-Ke Shih 	}
431e3ec7017SPing-Ke Shih }
432e3ec7017SPing-Ke Shih 
ser_l1_reset_pre_st_hdl(struct rtw89_ser * ser,u8 evt)43356617fd0SZong-Zhe Yang static void ser_l1_reset_pre_st_hdl(struct rtw89_ser *ser, u8 evt)
43456617fd0SZong-Zhe Yang {
43556617fd0SZong-Zhe Yang 	switch (evt) {
43656617fd0SZong-Zhe Yang 	case SER_EV_STATE_IN:
43756617fd0SZong-Zhe Yang 		ser->prehandle_l1 = true;
43856617fd0SZong-Zhe Yang 		hal_send_post_m0_event(ser);
43956617fd0SZong-Zhe Yang 		ser_set_alarm(ser, 1000, SER_EV_M1_TIMEOUT);
44056617fd0SZong-Zhe Yang 		break;
44156617fd0SZong-Zhe Yang 	case SER_EV_L1_RESET:
44256617fd0SZong-Zhe Yang 		ser_state_goto(ser, SER_RESET_TRX_ST);
44356617fd0SZong-Zhe Yang 		break;
44456617fd0SZong-Zhe Yang 	case SER_EV_M1_TIMEOUT:
44556617fd0SZong-Zhe Yang 		ser_state_goto(ser, SER_L2_RESET_ST);
44656617fd0SZong-Zhe Yang 		break;
44756617fd0SZong-Zhe Yang 	case SER_EV_STATE_OUT:
44856617fd0SZong-Zhe Yang 		ser_del_alarm(ser);
44956617fd0SZong-Zhe Yang 		break;
45056617fd0SZong-Zhe Yang 	default:
45156617fd0SZong-Zhe Yang 		break;
45256617fd0SZong-Zhe Yang 	}
45356617fd0SZong-Zhe Yang }
45456617fd0SZong-Zhe Yang 
ser_reset_trx_st_hdl(struct rtw89_ser * ser,u8 evt)455e3ec7017SPing-Ke Shih static void ser_reset_trx_st_hdl(struct rtw89_ser *ser, u8 evt)
456e3ec7017SPing-Ke Shih {
4575c48f943SChih-Kang Chang 	struct rtw89_dev *rtwdev = container_of(ser, struct rtw89_dev, ser);
4585c48f943SChih-Kang Chang 
459e3ec7017SPing-Ke Shih 	switch (evt) {
460e3ec7017SPing-Ke Shih 	case SER_EV_STATE_IN:
4615c48f943SChih-Kang Chang 		cancel_delayed_work_sync(&rtwdev->track_work);
462e3ec7017SPing-Ke Shih 		drv_stop_tx(ser);
463e3ec7017SPing-Ke Shih 
464e3ec7017SPing-Ke Shih 		if (hal_stop_dma(ser)) {
465e3ec7017SPing-Ke Shih 			ser_state_goto(ser, SER_L2_RESET_ST);
466e3ec7017SPing-Ke Shih 			break;
467e3ec7017SPing-Ke Shih 		}
468e3ec7017SPing-Ke Shih 
469e3ec7017SPing-Ke Shih 		drv_stop_rx(ser);
470e3ec7017SPing-Ke Shih 		drv_trx_reset(ser);
471e3ec7017SPing-Ke Shih 
472e3ec7017SPing-Ke Shih 		/* wait m3 */
473e3ec7017SPing-Ke Shih 		hal_send_m2_event(ser);
474e3ec7017SPing-Ke Shih 
475e3ec7017SPing-Ke Shih 		/* set alarm to prevent FW response timeout */
476e3ec7017SPing-Ke Shih 		ser_set_alarm(ser, 1000, SER_EV_M3_TIMEOUT);
477e3ec7017SPing-Ke Shih 		break;
478e3ec7017SPing-Ke Shih 
479e3ec7017SPing-Ke Shih 	case SER_EV_DO_RECOVERY:
480e3ec7017SPing-Ke Shih 		ser_state_goto(ser, SER_DO_HCI_ST);
481e3ec7017SPing-Ke Shih 		break;
482e3ec7017SPing-Ke Shih 
483e3ec7017SPing-Ke Shih 	case SER_EV_M3_TIMEOUT:
484e3ec7017SPing-Ke Shih 		ser_state_goto(ser, SER_L2_RESET_ST);
485e3ec7017SPing-Ke Shih 		break;
486e3ec7017SPing-Ke Shih 
487e3ec7017SPing-Ke Shih 	case SER_EV_STATE_OUT:
488e3ec7017SPing-Ke Shih 		ser_del_alarm(ser);
489e3ec7017SPing-Ke Shih 		hal_enable_dma(ser);
490e3ec7017SPing-Ke Shih 		drv_resume_rx(ser);
491e3ec7017SPing-Ke Shih 		drv_resume_tx(ser);
4925c48f943SChih-Kang Chang 		ieee80211_queue_delayed_work(rtwdev->hw, &rtwdev->track_work,
4935c48f943SChih-Kang Chang 					     RTW89_TRACK_WORK_PERIOD);
494e3ec7017SPing-Ke Shih 		break;
495e3ec7017SPing-Ke Shih 
496e3ec7017SPing-Ke Shih 	default:
497e3ec7017SPing-Ke Shih 		break;
498e3ec7017SPing-Ke Shih 	}
499e3ec7017SPing-Ke Shih }
500e3ec7017SPing-Ke Shih 
ser_do_hci_st_hdl(struct rtw89_ser * ser,u8 evt)501e3ec7017SPing-Ke Shih static void ser_do_hci_st_hdl(struct rtw89_ser *ser, u8 evt)
502e3ec7017SPing-Ke Shih {
503e3ec7017SPing-Ke Shih 	switch (evt) {
504e3ec7017SPing-Ke Shih 	case SER_EV_STATE_IN:
505e3ec7017SPing-Ke Shih 		/* wait m5 */
506e3ec7017SPing-Ke Shih 		hal_send_m4_event(ser);
507e3ec7017SPing-Ke Shih 
508e3ec7017SPing-Ke Shih 		/* prevent FW response timeout */
509e3ec7017SPing-Ke Shih 		ser_set_alarm(ser, 1000, SER_EV_FW_M5_TIMEOUT);
510e3ec7017SPing-Ke Shih 		break;
511e3ec7017SPing-Ke Shih 
512e3ec7017SPing-Ke Shih 	case SER_EV_FW_M5_TIMEOUT:
513e3ec7017SPing-Ke Shih 		ser_state_goto(ser, SER_L2_RESET_ST);
514e3ec7017SPing-Ke Shih 		break;
515e3ec7017SPing-Ke Shih 
516e3ec7017SPing-Ke Shih 	case SER_EV_MAC_RESET_DONE:
517e3ec7017SPing-Ke Shih 		ser_state_goto(ser, SER_IDLE_ST);
518e3ec7017SPing-Ke Shih 		break;
519e3ec7017SPing-Ke Shih 
520e3ec7017SPing-Ke Shih 	case SER_EV_STATE_OUT:
521e3ec7017SPing-Ke Shih 		ser_del_alarm(ser);
522e3ec7017SPing-Ke Shih 		break;
523e3ec7017SPing-Ke Shih 
524e3ec7017SPing-Ke Shih 	default:
525e3ec7017SPing-Ke Shih 		break;
526e3ec7017SPing-Ke Shih 	}
527e3ec7017SPing-Ke Shih }
528e3ec7017SPing-Ke Shih 
ser_mac_mem_dump(struct rtw89_dev * rtwdev,u8 * buf,u8 sel,u32 start_addr,u32 len)5299f8004bfSZong-Zhe Yang static void ser_mac_mem_dump(struct rtw89_dev *rtwdev, u8 *buf,
5309f8004bfSZong-Zhe Yang 			     u8 sel, u32 start_addr, u32 len)
5319f8004bfSZong-Zhe Yang {
532*60168f6cSPing-Ke Shih 	const struct rtw89_mac_gen_def *mac = rtwdev->chip->mac_def;
533*60168f6cSPing-Ke Shih 	u32 filter_model_addr = mac->filter_model_addr;
534*60168f6cSPing-Ke Shih 	u32 indir_access_addr = mac->indir_access_addr;
5359f8004bfSZong-Zhe Yang 	u32 *ptr = (u32 *)buf;
5369f8004bfSZong-Zhe Yang 	u32 base_addr, start_page, residue;
5379f8004bfSZong-Zhe Yang 	u32 cnt = 0;
5389f8004bfSZong-Zhe Yang 	u32 i;
5399f8004bfSZong-Zhe Yang 
5409f8004bfSZong-Zhe Yang 	start_page = start_addr / MAC_MEM_DUMP_PAGE_SIZE;
5419f8004bfSZong-Zhe Yang 	residue = start_addr % MAC_MEM_DUMP_PAGE_SIZE;
542*60168f6cSPing-Ke Shih 	base_addr = mac->mem_base_addrs[sel];
5439f8004bfSZong-Zhe Yang 	base_addr += start_page * MAC_MEM_DUMP_PAGE_SIZE;
5449f8004bfSZong-Zhe Yang 
5459f8004bfSZong-Zhe Yang 	while (cnt < len) {
546*60168f6cSPing-Ke Shih 		rtw89_write32(rtwdev, filter_model_addr, base_addr);
5479f8004bfSZong-Zhe Yang 
548*60168f6cSPing-Ke Shih 		for (i = indir_access_addr + residue;
549*60168f6cSPing-Ke Shih 		     i < indir_access_addr + MAC_MEM_DUMP_PAGE_SIZE;
5509f8004bfSZong-Zhe Yang 		     i += 4, ptr++) {
5519f8004bfSZong-Zhe Yang 			*ptr = rtw89_read32(rtwdev, i);
5529f8004bfSZong-Zhe Yang 			cnt += 4;
5539f8004bfSZong-Zhe Yang 			if (cnt >= len)
5549f8004bfSZong-Zhe Yang 				break;
5559f8004bfSZong-Zhe Yang 		}
5569f8004bfSZong-Zhe Yang 
5579f8004bfSZong-Zhe Yang 		residue = 0;
5589f8004bfSZong-Zhe Yang 		base_addr += MAC_MEM_DUMP_PAGE_SIZE;
5599f8004bfSZong-Zhe Yang 	}
5609f8004bfSZong-Zhe Yang }
5619f8004bfSZong-Zhe Yang 
rtw89_ser_fw_rsvd_ple_dump(struct rtw89_dev * rtwdev,u8 * buf)5629f8004bfSZong-Zhe Yang static void rtw89_ser_fw_rsvd_ple_dump(struct rtw89_dev *rtwdev, u8 *buf)
5639f8004bfSZong-Zhe Yang {
5649f8004bfSZong-Zhe Yang 	u32 start_addr = rtwdev->chip->rsvd_ple_ofst;
5659f8004bfSZong-Zhe Yang 
5669f8004bfSZong-Zhe Yang 	rtw89_debug(rtwdev, RTW89_DBG_SER,
5679f8004bfSZong-Zhe Yang 		    "dump mem for fw rsvd payload engine (start addr: 0x%x)\n",
5689f8004bfSZong-Zhe Yang 		    start_addr);
5699f8004bfSZong-Zhe Yang 	ser_mac_mem_dump(rtwdev, buf, RTW89_MAC_MEM_SHARED_BUF, start_addr,
5709f8004bfSZong-Zhe Yang 			 RTW89_FW_RSVD_PLE_SIZE);
5719f8004bfSZong-Zhe Yang }
5729f8004bfSZong-Zhe Yang 
573f5e24684SZong-Zhe Yang struct __fw_backtrace_entry {
574f5e24684SZong-Zhe Yang 	u32 wcpu_addr;
575f5e24684SZong-Zhe Yang 	u32 size;
576f5e24684SZong-Zhe Yang 	u32 key;
577f5e24684SZong-Zhe Yang } __packed;
578f5e24684SZong-Zhe Yang 
579f5e24684SZong-Zhe Yang struct __fw_backtrace_info {
580f5e24684SZong-Zhe Yang 	u32 ra;
581f5e24684SZong-Zhe Yang 	u32 sp;
582f5e24684SZong-Zhe Yang } __packed;
583f5e24684SZong-Zhe Yang 
584f5e24684SZong-Zhe Yang static_assert(RTW89_FW_BACKTRACE_INFO_SIZE ==
585f5e24684SZong-Zhe Yang 	      sizeof(struct __fw_backtrace_info));
586f5e24684SZong-Zhe Yang 
rtw89_ser_fw_backtrace_dump(struct rtw89_dev * rtwdev,u8 * buf,const struct __fw_backtrace_entry * ent)587f5e24684SZong-Zhe Yang static int rtw89_ser_fw_backtrace_dump(struct rtw89_dev *rtwdev, u8 *buf,
588f5e24684SZong-Zhe Yang 				       const struct __fw_backtrace_entry *ent)
589f5e24684SZong-Zhe Yang {
590f5e24684SZong-Zhe Yang 	struct __fw_backtrace_info *ptr = (struct __fw_backtrace_info *)buf;
591*60168f6cSPing-Ke Shih 	const struct rtw89_mac_gen_def *mac = rtwdev->chip->mac_def;
592*60168f6cSPing-Ke Shih 	u32 filter_model_addr = mac->filter_model_addr;
593*60168f6cSPing-Ke Shih 	u32 indir_access_addr = mac->indir_access_addr;
594e77d3f8bSZong-Zhe Yang 	u32 fwbt_addr = ent->wcpu_addr & RTW89_WCPU_BASE_MASK;
595f5e24684SZong-Zhe Yang 	u32 fwbt_size = ent->size;
596f5e24684SZong-Zhe Yang 	u32 fwbt_key = ent->key;
597f5e24684SZong-Zhe Yang 	u32 i;
598f5e24684SZong-Zhe Yang 
599f5e24684SZong-Zhe Yang 	if (fwbt_addr == 0) {
600f5e24684SZong-Zhe Yang 		rtw89_warn(rtwdev, "FW backtrace invalid address: 0x%x\n",
601f5e24684SZong-Zhe Yang 			   fwbt_addr);
602f5e24684SZong-Zhe Yang 		return -EINVAL;
603f5e24684SZong-Zhe Yang 	}
604f5e24684SZong-Zhe Yang 
605f5e24684SZong-Zhe Yang 	if (fwbt_key != RTW89_FW_BACKTRACE_KEY) {
606f5e24684SZong-Zhe Yang 		rtw89_warn(rtwdev, "FW backtrace invalid key: 0x%x\n",
607f5e24684SZong-Zhe Yang 			   fwbt_key);
608f5e24684SZong-Zhe Yang 		return -EINVAL;
609f5e24684SZong-Zhe Yang 	}
610f5e24684SZong-Zhe Yang 
611f5e24684SZong-Zhe Yang 	if (fwbt_size == 0 || !RTW89_VALID_FW_BACKTRACE_SIZE(fwbt_size) ||
612f5e24684SZong-Zhe Yang 	    fwbt_size > RTW89_FW_BACKTRACE_MAX_SIZE) {
613f5e24684SZong-Zhe Yang 		rtw89_warn(rtwdev, "FW backtrace invalid size: 0x%x\n",
614f5e24684SZong-Zhe Yang 			   fwbt_size);
615f5e24684SZong-Zhe Yang 		return -EINVAL;
616f5e24684SZong-Zhe Yang 	}
617f5e24684SZong-Zhe Yang 
618f5e24684SZong-Zhe Yang 	rtw89_debug(rtwdev, RTW89_DBG_SER, "dump fw backtrace start\n");
619*60168f6cSPing-Ke Shih 	rtw89_write32(rtwdev, filter_model_addr, fwbt_addr);
620f5e24684SZong-Zhe Yang 
621*60168f6cSPing-Ke Shih 	for (i = indir_access_addr;
622*60168f6cSPing-Ke Shih 	     i < indir_access_addr + fwbt_size;
623f5e24684SZong-Zhe Yang 	     i += RTW89_FW_BACKTRACE_INFO_SIZE, ptr++) {
624f5e24684SZong-Zhe Yang 		*ptr = (struct __fw_backtrace_info){
625f5e24684SZong-Zhe Yang 			.ra = rtw89_read32(rtwdev, i),
626f5e24684SZong-Zhe Yang 			.sp = rtw89_read32(rtwdev, i + 4),
627f5e24684SZong-Zhe Yang 		};
628f5e24684SZong-Zhe Yang 		rtw89_debug(rtwdev, RTW89_DBG_SER,
629f5e24684SZong-Zhe Yang 			    "next sp: 0x%x, next ra: 0x%x\n",
630f5e24684SZong-Zhe Yang 			    ptr->sp, ptr->ra);
631f5e24684SZong-Zhe Yang 	}
632f5e24684SZong-Zhe Yang 
633f5e24684SZong-Zhe Yang 	rtw89_debug(rtwdev, RTW89_DBG_SER, "dump fw backtrace end\n");
634f5e24684SZong-Zhe Yang 	return 0;
635f5e24684SZong-Zhe Yang }
636f5e24684SZong-Zhe Yang 
ser_l2_reset_st_pre_hdl(struct rtw89_ser * ser)6379f8004bfSZong-Zhe Yang static void ser_l2_reset_st_pre_hdl(struct rtw89_ser *ser)
6389f8004bfSZong-Zhe Yang {
6399f8004bfSZong-Zhe Yang 	struct rtw89_dev *rtwdev = container_of(ser, struct rtw89_dev, ser);
6409f8004bfSZong-Zhe Yang 	struct rtw89_ser_cd_buffer *buf;
641f5e24684SZong-Zhe Yang 	struct __fw_backtrace_entry fwbt_ent;
642f5e24684SZong-Zhe Yang 	int ret = 0;
6439f8004bfSZong-Zhe Yang 
6449f8004bfSZong-Zhe Yang 	buf = rtw89_ser_cd_prep(rtwdev);
645f5e24684SZong-Zhe Yang 	if (!buf) {
646f5e24684SZong-Zhe Yang 		ret = -ENOMEM;
6479f8004bfSZong-Zhe Yang 		goto bottom;
648f5e24684SZong-Zhe Yang 	}
6499f8004bfSZong-Zhe Yang 
6509f8004bfSZong-Zhe Yang 	rtw89_ser_fw_rsvd_ple_dump(rtwdev, buf->fwple.data);
651f5e24684SZong-Zhe Yang 
652f5e24684SZong-Zhe Yang 	fwbt_ent = *(struct __fw_backtrace_entry *)buf->fwple.data;
653f5e24684SZong-Zhe Yang 	ret = rtw89_ser_fw_backtrace_dump(rtwdev, buf->fwbt.data, &fwbt_ent);
654f5e24684SZong-Zhe Yang 	if (ret)
655f5e24684SZong-Zhe Yang 		goto bottom;
656f5e24684SZong-Zhe Yang 
6579f8004bfSZong-Zhe Yang 	rtw89_ser_cd_send(rtwdev, buf);
6589f8004bfSZong-Zhe Yang 
6599f8004bfSZong-Zhe Yang bottom:
660f5e24684SZong-Zhe Yang 	rtw89_ser_cd_free(rtwdev, buf, !!ret);
661f5e24684SZong-Zhe Yang 
6629f8004bfSZong-Zhe Yang 	ser_reset_mac_binding(rtwdev);
6639f8004bfSZong-Zhe Yang 	rtw89_core_stop(rtwdev);
664a88b6cc4SZong-Zhe Yang 	rtw89_entity_init(rtwdev);
6655c12bb66SChin-Yen Lee 	rtw89_fw_release_general_pkt_list(rtwdev, false);
6669f8004bfSZong-Zhe Yang 	INIT_LIST_HEAD(&rtwdev->rtwvifs_list);
6679f8004bfSZong-Zhe Yang }
6689f8004bfSZong-Zhe Yang 
ser_l2_reset_st_hdl(struct rtw89_ser * ser,u8 evt)669e3ec7017SPing-Ke Shih static void ser_l2_reset_st_hdl(struct rtw89_ser *ser, u8 evt)
670e3ec7017SPing-Ke Shih {
671e3ec7017SPing-Ke Shih 	struct rtw89_dev *rtwdev = container_of(ser, struct rtw89_dev, ser);
672e3ec7017SPing-Ke Shih 
673e3ec7017SPing-Ke Shih 	switch (evt) {
674e3ec7017SPing-Ke Shih 	case SER_EV_STATE_IN:
675e3ec7017SPing-Ke Shih 		mutex_lock(&rtwdev->mutex);
6769f8004bfSZong-Zhe Yang 		ser_l2_reset_st_pre_hdl(ser);
677e3ec7017SPing-Ke Shih 		mutex_unlock(&rtwdev->mutex);
678e3ec7017SPing-Ke Shih 
679e3ec7017SPing-Ke Shih 		ieee80211_restart_hw(rtwdev->hw);
680e3ec7017SPing-Ke Shih 		ser_set_alarm(ser, SER_RECFG_TIMEOUT, SER_EV_L2_RECFG_TIMEOUT);
681e3ec7017SPing-Ke Shih 		break;
682e3ec7017SPing-Ke Shih 
683e3ec7017SPing-Ke Shih 	case SER_EV_L2_RECFG_TIMEOUT:
684e3ec7017SPing-Ke Shih 		rtw89_info(rtwdev, "Err: ser L2 re-config timeout\n");
685e3ec7017SPing-Ke Shih 		fallthrough;
686e3ec7017SPing-Ke Shih 	case SER_EV_L2_RECFG_DONE:
687e3ec7017SPing-Ke Shih 		ser_state_goto(ser, SER_IDLE_ST);
688e3ec7017SPing-Ke Shih 		break;
689e3ec7017SPing-Ke Shih 
690e3ec7017SPing-Ke Shih 	case SER_EV_STATE_OUT:
691e3ec7017SPing-Ke Shih 		ser_del_alarm(ser);
692e3ec7017SPing-Ke Shih 		break;
693e3ec7017SPing-Ke Shih 
694e3ec7017SPing-Ke Shih 	default:
695e3ec7017SPing-Ke Shih 		break;
696e3ec7017SPing-Ke Shih 	}
697e3ec7017SPing-Ke Shih }
698e3ec7017SPing-Ke Shih 
699af5175acSJoe Perches static const struct event_ent ser_ev_tbl[] = {
700e3ec7017SPing-Ke Shih 	{SER_EV_NONE, "SER_EV_NONE"},
701e3ec7017SPing-Ke Shih 	{SER_EV_STATE_IN, "SER_EV_STATE_IN"},
702e3ec7017SPing-Ke Shih 	{SER_EV_STATE_OUT, "SER_EV_STATE_OUT"},
70356617fd0SZong-Zhe Yang 	{SER_EV_L1_RESET_PREPARE, "SER_EV_L1_RESET_PREPARE pre-m0"},
70456617fd0SZong-Zhe Yang 	{SER_EV_L1_RESET, "SER_EV_L1_RESET m1"},
705e3ec7017SPing-Ke Shih 	{SER_EV_DO_RECOVERY, "SER_EV_DO_RECOVERY m3"},
706e3ec7017SPing-Ke Shih 	{SER_EV_MAC_RESET_DONE, "SER_EV_MAC_RESET_DONE m5"},
707e3ec7017SPing-Ke Shih 	{SER_EV_L2_RESET, "SER_EV_L2_RESET"},
708e3ec7017SPing-Ke Shih 	{SER_EV_L2_RECFG_DONE, "SER_EV_L2_RECFG_DONE"},
709e3ec7017SPing-Ke Shih 	{SER_EV_L2_RECFG_TIMEOUT, "SER_EV_L2_RECFG_TIMEOUT"},
71056617fd0SZong-Zhe Yang 	{SER_EV_M1_TIMEOUT, "SER_EV_M1_TIMEOUT"},
711e3ec7017SPing-Ke Shih 	{SER_EV_M3_TIMEOUT, "SER_EV_M3_TIMEOUT"},
712e3ec7017SPing-Ke Shih 	{SER_EV_FW_M5_TIMEOUT, "SER_EV_FW_M5_TIMEOUT"},
713e3ec7017SPing-Ke Shih 	{SER_EV_L0_RESET, "SER_EV_L0_RESET"},
714e3ec7017SPing-Ke Shih 	{SER_EV_MAXX, "SER_EV_MAX"}
715e3ec7017SPing-Ke Shih };
716e3ec7017SPing-Ke Shih 
717af5175acSJoe Perches static const struct state_ent ser_st_tbl[] = {
718e3ec7017SPing-Ke Shih 	{SER_IDLE_ST, "SER_IDLE_ST", ser_idle_st_hdl},
71956617fd0SZong-Zhe Yang 	{SER_L1_RESET_PRE_ST, "SER_L1_RESET_PRE_ST", ser_l1_reset_pre_st_hdl},
720e3ec7017SPing-Ke Shih 	{SER_RESET_TRX_ST, "SER_RESET_TRX_ST", ser_reset_trx_st_hdl},
721e3ec7017SPing-Ke Shih 	{SER_DO_HCI_ST, "SER_DO_HCI_ST", ser_do_hci_st_hdl},
722e3ec7017SPing-Ke Shih 	{SER_L2_RESET_ST, "SER_L2_RESET_ST", ser_l2_reset_st_hdl}
723e3ec7017SPing-Ke Shih };
724e3ec7017SPing-Ke Shih 
rtw89_ser_init(struct rtw89_dev * rtwdev)725e3ec7017SPing-Ke Shih int rtw89_ser_init(struct rtw89_dev *rtwdev)
726e3ec7017SPing-Ke Shih {
727e3ec7017SPing-Ke Shih 	struct rtw89_ser *ser = &rtwdev->ser;
728e3ec7017SPing-Ke Shih 
729e3ec7017SPing-Ke Shih 	memset(ser, 0, sizeof(*ser));
730e3ec7017SPing-Ke Shih 	INIT_LIST_HEAD(&ser->msg_q);
731e3ec7017SPing-Ke Shih 	ser->state = SER_IDLE_ST;
732e3ec7017SPing-Ke Shih 	ser->st_tbl = ser_st_tbl;
733e3ec7017SPing-Ke Shih 	ser->ev_tbl = ser_ev_tbl;
734e3ec7017SPing-Ke Shih 
735e3ec7017SPing-Ke Shih 	bitmap_zero(ser->flags, RTW89_NUM_OF_SER_FLAGS);
736e3ec7017SPing-Ke Shih 	spin_lock_init(&ser->msg_q_lock);
737e3ec7017SPing-Ke Shih 	INIT_WORK(&ser->ser_hdl_work, rtw89_ser_hdl_work);
738e3ec7017SPing-Ke Shih 	INIT_DELAYED_WORK(&ser->ser_alarm_work, rtw89_ser_alarm_work);
739e3ec7017SPing-Ke Shih 	return 0;
740e3ec7017SPing-Ke Shih }
741e3ec7017SPing-Ke Shih 
rtw89_ser_deinit(struct rtw89_dev * rtwdev)742e3ec7017SPing-Ke Shih int rtw89_ser_deinit(struct rtw89_dev *rtwdev)
743e3ec7017SPing-Ke Shih {
744e3ec7017SPing-Ke Shih 	struct rtw89_ser *ser = (struct rtw89_ser *)&rtwdev->ser;
745e3ec7017SPing-Ke Shih 
746e3ec7017SPing-Ke Shih 	set_bit(RTW89_SER_DRV_STOP_RUN, ser->flags);
747e3ec7017SPing-Ke Shih 	cancel_delayed_work_sync(&ser->ser_alarm_work);
748e3ec7017SPing-Ke Shih 	cancel_work_sync(&ser->ser_hdl_work);
749e3ec7017SPing-Ke Shih 	clear_bit(RTW89_SER_DRV_STOP_RUN, ser->flags);
750e3ec7017SPing-Ke Shih 	return 0;
751e3ec7017SPing-Ke Shih }
752e3ec7017SPing-Ke Shih 
rtw89_ser_recfg_done(struct rtw89_dev * rtwdev)753e3ec7017SPing-Ke Shih void rtw89_ser_recfg_done(struct rtw89_dev *rtwdev)
754e3ec7017SPing-Ke Shih {
755e3ec7017SPing-Ke Shih 	ser_send_msg(&rtwdev->ser, SER_EV_L2_RECFG_DONE);
756e3ec7017SPing-Ke Shih }
757e3ec7017SPing-Ke Shih 
rtw89_ser_notify(struct rtw89_dev * rtwdev,u32 err)758e3ec7017SPing-Ke Shih int rtw89_ser_notify(struct rtw89_dev *rtwdev, u32 err)
759e3ec7017SPing-Ke Shih {
760e3ec7017SPing-Ke Shih 	u8 event = SER_EV_NONE;
761e3ec7017SPing-Ke Shih 
762198b6cf7SZong-Zhe Yang 	rtw89_info(rtwdev, "SER catches error: 0x%x\n", err);
763e3ec7017SPing-Ke Shih 
764e3ec7017SPing-Ke Shih 	switch (err) {
76556617fd0SZong-Zhe Yang 	case MAC_AX_ERR_L1_PREERR_DMAC: /* pre-M0 */
76656617fd0SZong-Zhe Yang 		event = SER_EV_L1_RESET_PREPARE;
76756617fd0SZong-Zhe Yang 		break;
768e3ec7017SPing-Ke Shih 	case MAC_AX_ERR_L1_ERR_DMAC:
769e3ec7017SPing-Ke Shih 	case MAC_AX_ERR_L0_PROMOTE_TO_L1:
770e3ec7017SPing-Ke Shih 		event = SER_EV_L1_RESET; /* M1 */
771e3ec7017SPing-Ke Shih 		break;
772e3ec7017SPing-Ke Shih 	case MAC_AX_ERR_L1_RESET_DISABLE_DMAC_DONE:
773e3ec7017SPing-Ke Shih 		event = SER_EV_DO_RECOVERY; /* M3 */
774e3ec7017SPing-Ke Shih 		break;
775e3ec7017SPing-Ke Shih 	case MAC_AX_ERR_L1_RESET_RECOVERY_DONE:
776e3ec7017SPing-Ke Shih 		event = SER_EV_MAC_RESET_DONE; /* M5 */
777e3ec7017SPing-Ke Shih 		break;
778e3ec7017SPing-Ke Shih 	case MAC_AX_ERR_L0_ERR_CMAC0:
779e3ec7017SPing-Ke Shih 	case MAC_AX_ERR_L0_ERR_CMAC1:
780e3ec7017SPing-Ke Shih 	case MAC_AX_ERR_L0_RESET_DONE:
781e3ec7017SPing-Ke Shih 		event = SER_EV_L0_RESET;
782e3ec7017SPing-Ke Shih 		break;
783e3ec7017SPing-Ke Shih 	default:
784e3ec7017SPing-Ke Shih 		if (err == MAC_AX_ERR_L1_PROMOTE_TO_L2 ||
785e3ec7017SPing-Ke Shih 		    (err >= MAC_AX_ERR_L2_ERR_AH_DMA &&
786e3ec7017SPing-Ke Shih 		     err <= MAC_AX_GET_ERR_MAX))
787e3ec7017SPing-Ke Shih 			event = SER_EV_L2_RESET;
788e3ec7017SPing-Ke Shih 		break;
789e3ec7017SPing-Ke Shih 	}
790e3ec7017SPing-Ke Shih 
791198b6cf7SZong-Zhe Yang 	if (event == SER_EV_NONE) {
792198b6cf7SZong-Zhe Yang 		rtw89_warn(rtwdev, "SER cannot recognize error: 0x%x\n", err);
793e3ec7017SPing-Ke Shih 		return -EINVAL;
794198b6cf7SZong-Zhe Yang 	}
795e3ec7017SPing-Ke Shih 
796e3ec7017SPing-Ke Shih 	ser_send_msg(&rtwdev->ser, event);
797e3ec7017SPing-Ke Shih 	return 0;
798e3ec7017SPing-Ke Shih }
799e3ec7017SPing-Ke Shih EXPORT_SYMBOL(rtw89_ser_notify);
800