xref: /linux/drivers/net/wireless/realtek/rtw89/ser.c (revision 8676031bae1c91037d06341214f4150b33707c68)
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,
23e3ec7017SPing-Ke Shih 	SER_EV_L1_RESET, /* M1 */
24e3ec7017SPing-Ke Shih 	SER_EV_DO_RECOVERY, /* M3 */
25e3ec7017SPing-Ke Shih 	SER_EV_MAC_RESET_DONE, /* M5 */
26e3ec7017SPing-Ke Shih 	SER_EV_L2_RESET,
27e3ec7017SPing-Ke Shih 	SER_EV_L2_RECFG_DONE,
28e3ec7017SPing-Ke Shih 	SER_EV_L2_RECFG_TIMEOUT,
29e3ec7017SPing-Ke Shih 	SER_EV_M3_TIMEOUT,
30e3ec7017SPing-Ke Shih 	SER_EV_FW_M5_TIMEOUT,
31e3ec7017SPing-Ke Shih 	SER_EV_L0_RESET,
32e3ec7017SPing-Ke Shih 	SER_EV_MAXX
33e3ec7017SPing-Ke Shih };
34e3ec7017SPing-Ke Shih 
35e3ec7017SPing-Ke Shih enum ser_state {
36e3ec7017SPing-Ke Shih 	SER_IDLE_ST,
37e3ec7017SPing-Ke Shih 	SER_RESET_TRX_ST,
38e3ec7017SPing-Ke Shih 	SER_DO_HCI_ST,
39e3ec7017SPing-Ke Shih 	SER_L2_RESET_ST,
40e3ec7017SPing-Ke Shih 	SER_ST_MAX_ST
41e3ec7017SPing-Ke Shih };
42e3ec7017SPing-Ke Shih 
43e3ec7017SPing-Ke Shih struct ser_msg {
44e3ec7017SPing-Ke Shih 	struct list_head list;
45e3ec7017SPing-Ke Shih 	u8 event;
46e3ec7017SPing-Ke Shih };
47e3ec7017SPing-Ke Shih 
48e3ec7017SPing-Ke Shih struct state_ent {
49e3ec7017SPing-Ke Shih 	u8 state;
50e3ec7017SPing-Ke Shih 	char *name;
51e3ec7017SPing-Ke Shih 	void (*st_func)(struct rtw89_ser *ser, u8 event);
52e3ec7017SPing-Ke Shih };
53e3ec7017SPing-Ke Shih 
54e3ec7017SPing-Ke Shih struct event_ent {
55e3ec7017SPing-Ke Shih 	u8 event;
56e3ec7017SPing-Ke Shih 	char *name;
57e3ec7017SPing-Ke Shih };
58e3ec7017SPing-Ke Shih 
59e3ec7017SPing-Ke Shih static char *ser_ev_name(struct rtw89_ser *ser, u8 event)
60e3ec7017SPing-Ke Shih {
61e3ec7017SPing-Ke Shih 	if (event < SER_EV_MAXX)
62e3ec7017SPing-Ke Shih 		return ser->ev_tbl[event].name;
63e3ec7017SPing-Ke Shih 
64e3ec7017SPing-Ke Shih 	return "err_ev_name";
65e3ec7017SPing-Ke Shih }
66e3ec7017SPing-Ke Shih 
67e3ec7017SPing-Ke Shih static char *ser_st_name(struct rtw89_ser *ser)
68e3ec7017SPing-Ke Shih {
69e3ec7017SPing-Ke Shih 	if (ser->state < SER_ST_MAX_ST)
70e3ec7017SPing-Ke Shih 		return ser->st_tbl[ser->state].name;
71e3ec7017SPing-Ke Shih 
72e3ec7017SPing-Ke Shih 	return "err_st_name";
73e3ec7017SPing-Ke Shih }
74e3ec7017SPing-Ke Shih 
759f8004bfSZong-Zhe Yang #define RTW89_DEF_SER_CD_TYPE(_name, _type, _size) \
769f8004bfSZong-Zhe Yang struct ser_cd_ ## _name { \
779f8004bfSZong-Zhe Yang 	u32 type; \
789f8004bfSZong-Zhe Yang 	u32 type_size; \
799f8004bfSZong-Zhe Yang 	u64 padding; \
809f8004bfSZong-Zhe Yang 	u8 data[_size]; \
819f8004bfSZong-Zhe Yang } __packed; \
829f8004bfSZong-Zhe Yang static void ser_cd_ ## _name ## _init(struct ser_cd_ ## _name *p) \
839f8004bfSZong-Zhe Yang { \
849f8004bfSZong-Zhe Yang 	p->type = _type; \
859f8004bfSZong-Zhe Yang 	p->type_size = sizeof(p->data); \
869f8004bfSZong-Zhe Yang 	p->padding = 0x0123456789abcdef; \
879f8004bfSZong-Zhe Yang }
889f8004bfSZong-Zhe Yang 
899f8004bfSZong-Zhe Yang enum rtw89_ser_cd_type {
909f8004bfSZong-Zhe Yang 	RTW89_SER_CD_FW_RSVD_PLE	= 0,
91f5e24684SZong-Zhe Yang 	RTW89_SER_CD_FW_BACKTRACE	= 1,
929f8004bfSZong-Zhe Yang };
939f8004bfSZong-Zhe Yang 
949f8004bfSZong-Zhe Yang RTW89_DEF_SER_CD_TYPE(fw_rsvd_ple,
959f8004bfSZong-Zhe Yang 		      RTW89_SER_CD_FW_RSVD_PLE,
969f8004bfSZong-Zhe Yang 		      RTW89_FW_RSVD_PLE_SIZE);
979f8004bfSZong-Zhe Yang 
98f5e24684SZong-Zhe Yang RTW89_DEF_SER_CD_TYPE(fw_backtrace,
99f5e24684SZong-Zhe Yang 		      RTW89_SER_CD_FW_BACKTRACE,
100f5e24684SZong-Zhe Yang 		      RTW89_FW_BACKTRACE_MAX_SIZE);
101f5e24684SZong-Zhe Yang 
1029f8004bfSZong-Zhe Yang struct rtw89_ser_cd_buffer {
1039f8004bfSZong-Zhe Yang 	struct ser_cd_fw_rsvd_ple fwple;
104f5e24684SZong-Zhe Yang 	struct ser_cd_fw_backtrace fwbt;
1059f8004bfSZong-Zhe Yang } __packed;
1069f8004bfSZong-Zhe Yang 
1079f8004bfSZong-Zhe Yang static struct rtw89_ser_cd_buffer *rtw89_ser_cd_prep(struct rtw89_dev *rtwdev)
1089f8004bfSZong-Zhe Yang {
1099f8004bfSZong-Zhe Yang 	struct rtw89_ser_cd_buffer *buf;
1109f8004bfSZong-Zhe Yang 
1119f8004bfSZong-Zhe Yang 	buf = vzalloc(sizeof(*buf));
1129f8004bfSZong-Zhe Yang 	if (!buf)
1139f8004bfSZong-Zhe Yang 		return NULL;
1149f8004bfSZong-Zhe Yang 
1159f8004bfSZong-Zhe Yang 	ser_cd_fw_rsvd_ple_init(&buf->fwple);
116f5e24684SZong-Zhe Yang 	ser_cd_fw_backtrace_init(&buf->fwbt);
1179f8004bfSZong-Zhe Yang 
1189f8004bfSZong-Zhe Yang 	return buf;
1199f8004bfSZong-Zhe Yang }
1209f8004bfSZong-Zhe Yang 
1219f8004bfSZong-Zhe Yang static void rtw89_ser_cd_send(struct rtw89_dev *rtwdev,
1229f8004bfSZong-Zhe Yang 			      struct rtw89_ser_cd_buffer *buf)
1239f8004bfSZong-Zhe Yang {
1249f8004bfSZong-Zhe Yang 	rtw89_debug(rtwdev, RTW89_DBG_SER, "SER sends core dump\n");
1259f8004bfSZong-Zhe Yang 
1269f8004bfSZong-Zhe Yang 	/* After calling dev_coredump, buf's lifetime is supposed to be
1279f8004bfSZong-Zhe Yang 	 * handled by the device coredump framework. Note that a new dump
1289f8004bfSZong-Zhe Yang 	 * will be discarded if a previous one hasn't been released by
1299f8004bfSZong-Zhe Yang 	 * framework yet.
1309f8004bfSZong-Zhe Yang 	 */
1319f8004bfSZong-Zhe Yang 	dev_coredumpv(rtwdev->dev, buf, sizeof(*buf), GFP_KERNEL);
1329f8004bfSZong-Zhe Yang }
1339f8004bfSZong-Zhe Yang 
134f5e24684SZong-Zhe Yang static void rtw89_ser_cd_free(struct rtw89_dev *rtwdev,
135f5e24684SZong-Zhe Yang 			      struct rtw89_ser_cd_buffer *buf, bool free_self)
136f5e24684SZong-Zhe Yang {
137f5e24684SZong-Zhe Yang 	if (!free_self)
138f5e24684SZong-Zhe Yang 		return;
139f5e24684SZong-Zhe Yang 
140f5e24684SZong-Zhe Yang 	rtw89_debug(rtwdev, RTW89_DBG_SER, "SER frees core dump by self\n");
141f5e24684SZong-Zhe Yang 
142f5e24684SZong-Zhe Yang 	/* When some problems happen during filling data of core dump,
143f5e24684SZong-Zhe Yang 	 * we won't send it to device coredump framework. Instead, we
144f5e24684SZong-Zhe Yang 	 * free buf by ourselves.
145f5e24684SZong-Zhe Yang 	 */
146f5e24684SZong-Zhe Yang 	vfree(buf);
147f5e24684SZong-Zhe Yang }
148f5e24684SZong-Zhe Yang 
149e3ec7017SPing-Ke Shih static void ser_state_run(struct rtw89_ser *ser, u8 evt)
150e3ec7017SPing-Ke Shih {
151e3ec7017SPing-Ke Shih 	struct rtw89_dev *rtwdev = container_of(ser, struct rtw89_dev, ser);
152e3ec7017SPing-Ke Shih 
153e3ec7017SPing-Ke Shih 	rtw89_debug(rtwdev, RTW89_DBG_SER, "ser: %s receive %s\n",
154e3ec7017SPing-Ke Shih 		    ser_st_name(ser), ser_ev_name(ser, evt));
155e3ec7017SPing-Ke Shih 
156*8676031bSZong-Zhe Yang 	mutex_lock(&rtwdev->mutex);
157e3ec7017SPing-Ke Shih 	rtw89_leave_lps(rtwdev);
158*8676031bSZong-Zhe Yang 	mutex_unlock(&rtwdev->mutex);
159*8676031bSZong-Zhe Yang 
160e3ec7017SPing-Ke Shih 	ser->st_tbl[ser->state].st_func(ser, evt);
161e3ec7017SPing-Ke Shih }
162e3ec7017SPing-Ke Shih 
163e3ec7017SPing-Ke Shih static void ser_state_goto(struct rtw89_ser *ser, u8 new_state)
164e3ec7017SPing-Ke Shih {
165e3ec7017SPing-Ke Shih 	struct rtw89_dev *rtwdev = container_of(ser, struct rtw89_dev, ser);
166e3ec7017SPing-Ke Shih 
167e3ec7017SPing-Ke Shih 	if (ser->state == new_state || new_state >= SER_ST_MAX_ST)
168e3ec7017SPing-Ke Shih 		return;
169e3ec7017SPing-Ke Shih 	ser_state_run(ser, SER_EV_STATE_OUT);
170e3ec7017SPing-Ke Shih 
171e3ec7017SPing-Ke Shih 	rtw89_debug(rtwdev, RTW89_DBG_SER, "ser: %s goto -> %s\n",
172e3ec7017SPing-Ke Shih 		    ser_st_name(ser), ser->st_tbl[new_state].name);
173e3ec7017SPing-Ke Shih 
174e3ec7017SPing-Ke Shih 	ser->state = new_state;
175e3ec7017SPing-Ke Shih 	ser_state_run(ser, SER_EV_STATE_IN);
176e3ec7017SPing-Ke Shih }
177e3ec7017SPing-Ke Shih 
178e3ec7017SPing-Ke Shih static struct ser_msg *__rtw89_ser_dequeue_msg(struct rtw89_ser *ser)
179e3ec7017SPing-Ke Shih {
180e3ec7017SPing-Ke Shih 	struct ser_msg *msg;
181e3ec7017SPing-Ke Shih 
182e3ec7017SPing-Ke Shih 	spin_lock_irq(&ser->msg_q_lock);
183e3ec7017SPing-Ke Shih 	msg = list_first_entry_or_null(&ser->msg_q, struct ser_msg, list);
184e3ec7017SPing-Ke Shih 	if (msg)
185e3ec7017SPing-Ke Shih 		list_del(&msg->list);
186e3ec7017SPing-Ke Shih 	spin_unlock_irq(&ser->msg_q_lock);
187e3ec7017SPing-Ke Shih 
188e3ec7017SPing-Ke Shih 	return msg;
189e3ec7017SPing-Ke Shih }
190e3ec7017SPing-Ke Shih 
191e3ec7017SPing-Ke Shih static void rtw89_ser_hdl_work(struct work_struct *work)
192e3ec7017SPing-Ke Shih {
193e3ec7017SPing-Ke Shih 	struct ser_msg *msg;
194e3ec7017SPing-Ke Shih 	struct rtw89_ser *ser = container_of(work, struct rtw89_ser,
195e3ec7017SPing-Ke Shih 					     ser_hdl_work);
196e3ec7017SPing-Ke Shih 
197e3ec7017SPing-Ke Shih 	while ((msg = __rtw89_ser_dequeue_msg(ser))) {
198e3ec7017SPing-Ke Shih 		ser_state_run(ser, msg->event);
199e3ec7017SPing-Ke Shih 		kfree(msg);
200e3ec7017SPing-Ke Shih 	}
201e3ec7017SPing-Ke Shih }
202e3ec7017SPing-Ke Shih 
203e3ec7017SPing-Ke Shih static int ser_send_msg(struct rtw89_ser *ser, u8 event)
204e3ec7017SPing-Ke Shih {
205e3ec7017SPing-Ke Shih 	struct rtw89_dev *rtwdev = container_of(ser, struct rtw89_dev, ser);
206e3ec7017SPing-Ke Shih 	struct ser_msg *msg = NULL;
207e3ec7017SPing-Ke Shih 
208e3ec7017SPing-Ke Shih 	if (test_bit(RTW89_SER_DRV_STOP_RUN, ser->flags))
209e3ec7017SPing-Ke Shih 		return -EIO;
210e3ec7017SPing-Ke Shih 
211e3ec7017SPing-Ke Shih 	msg = kmalloc(sizeof(*msg), GFP_ATOMIC);
212e3ec7017SPing-Ke Shih 	if (!msg)
213e3ec7017SPing-Ke Shih 		return -ENOMEM;
214e3ec7017SPing-Ke Shih 
215e3ec7017SPing-Ke Shih 	msg->event = event;
216e3ec7017SPing-Ke Shih 
217e3ec7017SPing-Ke Shih 	spin_lock_irq(&ser->msg_q_lock);
218e3ec7017SPing-Ke Shih 	list_add(&msg->list, &ser->msg_q);
219e3ec7017SPing-Ke Shih 	spin_unlock_irq(&ser->msg_q_lock);
220e3ec7017SPing-Ke Shih 
221e3ec7017SPing-Ke Shih 	ieee80211_queue_work(rtwdev->hw, &ser->ser_hdl_work);
222e3ec7017SPing-Ke Shih 	return 0;
223e3ec7017SPing-Ke Shih }
224e3ec7017SPing-Ke Shih 
225e3ec7017SPing-Ke Shih static void rtw89_ser_alarm_work(struct work_struct *work)
226e3ec7017SPing-Ke Shih {
227e3ec7017SPing-Ke Shih 	struct rtw89_ser *ser = container_of(work, struct rtw89_ser,
228e3ec7017SPing-Ke Shih 					     ser_alarm_work.work);
229e3ec7017SPing-Ke Shih 
230e3ec7017SPing-Ke Shih 	ser_send_msg(ser, ser->alarm_event);
231e3ec7017SPing-Ke Shih 	ser->alarm_event = SER_EV_NONE;
232e3ec7017SPing-Ke Shih }
233e3ec7017SPing-Ke Shih 
234e3ec7017SPing-Ke Shih static void ser_set_alarm(struct rtw89_ser *ser, u32 ms, u8 event)
235e3ec7017SPing-Ke Shih {
236e3ec7017SPing-Ke Shih 	struct rtw89_dev *rtwdev = container_of(ser, struct rtw89_dev, ser);
237e3ec7017SPing-Ke Shih 
238e3ec7017SPing-Ke Shih 	if (test_bit(RTW89_SER_DRV_STOP_RUN, ser->flags))
239e3ec7017SPing-Ke Shih 		return;
240e3ec7017SPing-Ke Shih 
241e3ec7017SPing-Ke Shih 	ser->alarm_event = event;
242e3ec7017SPing-Ke Shih 	ieee80211_queue_delayed_work(rtwdev->hw, &ser->ser_alarm_work,
243e3ec7017SPing-Ke Shih 				     msecs_to_jiffies(ms));
244e3ec7017SPing-Ke Shih }
245e3ec7017SPing-Ke Shih 
246e3ec7017SPing-Ke Shih static void ser_del_alarm(struct rtw89_ser *ser)
247e3ec7017SPing-Ke Shih {
248e3ec7017SPing-Ke Shih 	cancel_delayed_work(&ser->ser_alarm_work);
249e3ec7017SPing-Ke Shih 	ser->alarm_event = SER_EV_NONE;
250e3ec7017SPing-Ke Shih }
251e3ec7017SPing-Ke Shih 
252e3ec7017SPing-Ke Shih /* driver function */
253e3ec7017SPing-Ke Shih static void drv_stop_tx(struct rtw89_ser *ser)
254e3ec7017SPing-Ke Shih {
255e3ec7017SPing-Ke Shih 	struct rtw89_dev *rtwdev = container_of(ser, struct rtw89_dev, ser);
256e3ec7017SPing-Ke Shih 
257e3ec7017SPing-Ke Shih 	ieee80211_stop_queues(rtwdev->hw);
258e3ec7017SPing-Ke Shih 	set_bit(RTW89_SER_DRV_STOP_TX, ser->flags);
259e3ec7017SPing-Ke Shih }
260e3ec7017SPing-Ke Shih 
261e3ec7017SPing-Ke Shih static void drv_stop_rx(struct rtw89_ser *ser)
262e3ec7017SPing-Ke Shih {
263e3ec7017SPing-Ke Shih 	struct rtw89_dev *rtwdev = container_of(ser, struct rtw89_dev, ser);
264e3ec7017SPing-Ke Shih 
265e3ec7017SPing-Ke Shih 	clear_bit(RTW89_FLAG_RUNNING, rtwdev->flags);
266e3ec7017SPing-Ke Shih 	set_bit(RTW89_SER_DRV_STOP_RX, ser->flags);
267e3ec7017SPing-Ke Shih }
268e3ec7017SPing-Ke Shih 
269e3ec7017SPing-Ke Shih static void drv_trx_reset(struct rtw89_ser *ser)
270e3ec7017SPing-Ke Shih {
271e3ec7017SPing-Ke Shih 	struct rtw89_dev *rtwdev = container_of(ser, struct rtw89_dev, ser);
272e3ec7017SPing-Ke Shih 
273e3ec7017SPing-Ke Shih 	rtw89_hci_reset(rtwdev);
274e3ec7017SPing-Ke Shih }
275e3ec7017SPing-Ke Shih 
276e3ec7017SPing-Ke Shih static void drv_resume_tx(struct rtw89_ser *ser)
277e3ec7017SPing-Ke Shih {
278e3ec7017SPing-Ke Shih 	struct rtw89_dev *rtwdev = container_of(ser, struct rtw89_dev, ser);
279e3ec7017SPing-Ke Shih 
280e3ec7017SPing-Ke Shih 	if (!test_bit(RTW89_SER_DRV_STOP_TX, ser->flags))
281e3ec7017SPing-Ke Shih 		return;
282e3ec7017SPing-Ke Shih 
283e3ec7017SPing-Ke Shih 	ieee80211_wake_queues(rtwdev->hw);
284e3ec7017SPing-Ke Shih 	clear_bit(RTW89_SER_DRV_STOP_TX, ser->flags);
285e3ec7017SPing-Ke Shih }
286e3ec7017SPing-Ke Shih 
287e3ec7017SPing-Ke Shih static void drv_resume_rx(struct rtw89_ser *ser)
288e3ec7017SPing-Ke Shih {
289e3ec7017SPing-Ke Shih 	struct rtw89_dev *rtwdev = container_of(ser, struct rtw89_dev, ser);
290e3ec7017SPing-Ke Shih 
291e3ec7017SPing-Ke Shih 	if (!test_bit(RTW89_SER_DRV_STOP_RX, ser->flags))
292e3ec7017SPing-Ke Shih 		return;
293e3ec7017SPing-Ke Shih 
294e3ec7017SPing-Ke Shih 	set_bit(RTW89_FLAG_RUNNING, rtwdev->flags);
295e3ec7017SPing-Ke Shih 	clear_bit(RTW89_SER_DRV_STOP_RX, ser->flags);
296e3ec7017SPing-Ke Shih }
297e3ec7017SPing-Ke Shih 
298e3ec7017SPing-Ke Shih static void ser_reset_vif(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif)
299e3ec7017SPing-Ke Shih {
300e3ec7017SPing-Ke Shih 	rtw89_core_release_bit_map(rtwdev->hw_port, rtwvif->port);
301e3ec7017SPing-Ke Shih 	rtwvif->net_type = RTW89_NET_TYPE_NO_LINK;
302e3ec7017SPing-Ke Shih 	rtwvif->trigger = false;
303e3ec7017SPing-Ke Shih }
304e3ec7017SPing-Ke Shih 
305b169f877SZong-Zhe Yang static void ser_sta_deinit_addr_cam_iter(void *data, struct ieee80211_sta *sta)
306b169f877SZong-Zhe Yang {
3077312100dSPing-Ke Shih 	struct rtw89_vif *rtwvif = (struct rtw89_vif *)data;
3087312100dSPing-Ke Shih 	struct rtw89_dev *rtwdev = rtwvif->rtwdev;
309b169f877SZong-Zhe Yang 	struct rtw89_sta *rtwsta = (struct rtw89_sta *)sta->drv_priv;
310b169f877SZong-Zhe Yang 
3117312100dSPing-Ke Shih 	if (rtwvif->net_type == RTW89_NET_TYPE_AP_MODE || sta->tdls)
312b169f877SZong-Zhe Yang 		rtw89_cam_deinit_addr_cam(rtwdev, &rtwsta->addr_cam);
31339913cc8SPing-Ke Shih 	if (sta->tdls)
31439913cc8SPing-Ke Shih 		rtw89_cam_deinit_bssid_cam(rtwdev, &rtwsta->bssid_cam);
315b169f877SZong-Zhe Yang }
316b169f877SZong-Zhe Yang 
317b169f877SZong-Zhe Yang static void ser_deinit_cam(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif)
318b169f877SZong-Zhe Yang {
319b169f877SZong-Zhe Yang 	ieee80211_iterate_stations_atomic(rtwdev->hw,
320b169f877SZong-Zhe Yang 					  ser_sta_deinit_addr_cam_iter,
3217312100dSPing-Ke Shih 					  rtwvif);
322b169f877SZong-Zhe Yang 
323b169f877SZong-Zhe Yang 	rtw89_cam_deinit(rtwdev, rtwvif);
324b169f877SZong-Zhe Yang }
325b169f877SZong-Zhe Yang 
326e3ec7017SPing-Ke Shih static void ser_reset_mac_binding(struct rtw89_dev *rtwdev)
327e3ec7017SPing-Ke Shih {
328e3ec7017SPing-Ke Shih 	struct rtw89_vif *rtwvif;
329e3ec7017SPing-Ke Shih 
330e3ec7017SPing-Ke Shih 	rtw89_cam_reset_keys(rtwdev);
331b169f877SZong-Zhe Yang 	rtw89_for_each_rtwvif(rtwdev, rtwvif)
332b169f877SZong-Zhe Yang 		ser_deinit_cam(rtwdev, rtwvif);
333b169f877SZong-Zhe Yang 
334e3ec7017SPing-Ke Shih 	rtw89_core_release_all_bits_map(rtwdev->mac_id_map, RTW89_MAX_MAC_ID_NUM);
335e3ec7017SPing-Ke Shih 	rtw89_for_each_rtwvif(rtwdev, rtwvif)
336e3ec7017SPing-Ke Shih 		ser_reset_vif(rtwdev, rtwvif);
337e3ec7017SPing-Ke Shih }
338e3ec7017SPing-Ke Shih 
339e3ec7017SPing-Ke Shih /* hal function */
340e3ec7017SPing-Ke Shih static int hal_enable_dma(struct rtw89_ser *ser)
341e3ec7017SPing-Ke Shih {
342e3ec7017SPing-Ke Shih 	struct rtw89_dev *rtwdev = container_of(ser, struct rtw89_dev, ser);
343e3ec7017SPing-Ke Shih 	int ret;
344e3ec7017SPing-Ke Shih 
345e3ec7017SPing-Ke Shih 	if (!test_bit(RTW89_SER_HAL_STOP_DMA, ser->flags))
346e3ec7017SPing-Ke Shih 		return 0;
347e3ec7017SPing-Ke Shih 
348e3ec7017SPing-Ke Shih 	if (!rtwdev->hci.ops->mac_lv1_rcvy)
349e3ec7017SPing-Ke Shih 		return -EIO;
350e3ec7017SPing-Ke Shih 
351e3ec7017SPing-Ke Shih 	ret = rtwdev->hci.ops->mac_lv1_rcvy(rtwdev, RTW89_LV1_RCVY_STEP_2);
352e3ec7017SPing-Ke Shih 	if (!ret)
353e3ec7017SPing-Ke Shih 		clear_bit(RTW89_SER_HAL_STOP_DMA, ser->flags);
354e3ec7017SPing-Ke Shih 
355e3ec7017SPing-Ke Shih 	return ret;
356e3ec7017SPing-Ke Shih }
357e3ec7017SPing-Ke Shih 
358e3ec7017SPing-Ke Shih static int hal_stop_dma(struct rtw89_ser *ser)
359e3ec7017SPing-Ke Shih {
360e3ec7017SPing-Ke Shih 	struct rtw89_dev *rtwdev = container_of(ser, struct rtw89_dev, ser);
361e3ec7017SPing-Ke Shih 	int ret;
362e3ec7017SPing-Ke Shih 
363e3ec7017SPing-Ke Shih 	if (!rtwdev->hci.ops->mac_lv1_rcvy)
364e3ec7017SPing-Ke Shih 		return -EIO;
365e3ec7017SPing-Ke Shih 
366e3ec7017SPing-Ke Shih 	ret = rtwdev->hci.ops->mac_lv1_rcvy(rtwdev, RTW89_LV1_RCVY_STEP_1);
367e3ec7017SPing-Ke Shih 	if (!ret)
368e3ec7017SPing-Ke Shih 		set_bit(RTW89_SER_HAL_STOP_DMA, ser->flags);
369e3ec7017SPing-Ke Shih 
370e3ec7017SPing-Ke Shih 	return ret;
371e3ec7017SPing-Ke Shih }
372e3ec7017SPing-Ke Shih 
373e3ec7017SPing-Ke Shih static void hal_send_m2_event(struct rtw89_ser *ser)
374e3ec7017SPing-Ke Shih {
375e3ec7017SPing-Ke Shih 	struct rtw89_dev *rtwdev = container_of(ser, struct rtw89_dev, ser);
376e3ec7017SPing-Ke Shih 
377e3ec7017SPing-Ke Shih 	rtw89_mac_set_err_status(rtwdev, MAC_AX_ERR_L1_DISABLE_EN);
378e3ec7017SPing-Ke Shih }
379e3ec7017SPing-Ke Shih 
380e3ec7017SPing-Ke Shih static void hal_send_m4_event(struct rtw89_ser *ser)
381e3ec7017SPing-Ke Shih {
382e3ec7017SPing-Ke Shih 	struct rtw89_dev *rtwdev = container_of(ser, struct rtw89_dev, ser);
383e3ec7017SPing-Ke Shih 
384e3ec7017SPing-Ke Shih 	rtw89_mac_set_err_status(rtwdev, MAC_AX_ERR_L1_RCVY_EN);
385e3ec7017SPing-Ke Shih }
386e3ec7017SPing-Ke Shih 
387e3ec7017SPing-Ke Shih /* state handler */
388e3ec7017SPing-Ke Shih static void ser_idle_st_hdl(struct rtw89_ser *ser, u8 evt)
389e3ec7017SPing-Ke Shih {
39014f9f479SZong-Zhe Yang 	struct rtw89_dev *rtwdev = container_of(ser, struct rtw89_dev, ser);
39114f9f479SZong-Zhe Yang 
392e3ec7017SPing-Ke Shih 	switch (evt) {
393e3ec7017SPing-Ke Shih 	case SER_EV_STATE_IN:
39414f9f479SZong-Zhe Yang 		rtw89_hci_recovery_complete(rtwdev);
395e3ec7017SPing-Ke Shih 		break;
396e3ec7017SPing-Ke Shih 	case SER_EV_L1_RESET:
397e3ec7017SPing-Ke Shih 		ser_state_goto(ser, SER_RESET_TRX_ST);
398e3ec7017SPing-Ke Shih 		break;
399e3ec7017SPing-Ke Shih 	case SER_EV_L2_RESET:
400e3ec7017SPing-Ke Shih 		ser_state_goto(ser, SER_L2_RESET_ST);
401e3ec7017SPing-Ke Shih 		break;
402e3ec7017SPing-Ke Shih 	case SER_EV_STATE_OUT:
40314f9f479SZong-Zhe Yang 		rtw89_hci_recovery_start(rtwdev);
4045ddfffd6SZong-Zhe Yang 		break;
405e3ec7017SPing-Ke Shih 	default:
406e3ec7017SPing-Ke Shih 		break;
407e3ec7017SPing-Ke Shih 	}
408e3ec7017SPing-Ke Shih }
409e3ec7017SPing-Ke Shih 
410e3ec7017SPing-Ke Shih static void ser_reset_trx_st_hdl(struct rtw89_ser *ser, u8 evt)
411e3ec7017SPing-Ke Shih {
412e3ec7017SPing-Ke Shih 	switch (evt) {
413e3ec7017SPing-Ke Shih 	case SER_EV_STATE_IN:
414e3ec7017SPing-Ke Shih 		drv_stop_tx(ser);
415e3ec7017SPing-Ke Shih 
416e3ec7017SPing-Ke Shih 		if (hal_stop_dma(ser)) {
417e3ec7017SPing-Ke Shih 			ser_state_goto(ser, SER_L2_RESET_ST);
418e3ec7017SPing-Ke Shih 			break;
419e3ec7017SPing-Ke Shih 		}
420e3ec7017SPing-Ke Shih 
421e3ec7017SPing-Ke Shih 		drv_stop_rx(ser);
422e3ec7017SPing-Ke Shih 		drv_trx_reset(ser);
423e3ec7017SPing-Ke Shih 
424e3ec7017SPing-Ke Shih 		/* wait m3 */
425e3ec7017SPing-Ke Shih 		hal_send_m2_event(ser);
426e3ec7017SPing-Ke Shih 
427e3ec7017SPing-Ke Shih 		/* set alarm to prevent FW response timeout */
428e3ec7017SPing-Ke Shih 		ser_set_alarm(ser, 1000, SER_EV_M3_TIMEOUT);
429e3ec7017SPing-Ke Shih 		break;
430e3ec7017SPing-Ke Shih 
431e3ec7017SPing-Ke Shih 	case SER_EV_DO_RECOVERY:
432e3ec7017SPing-Ke Shih 		ser_state_goto(ser, SER_DO_HCI_ST);
433e3ec7017SPing-Ke Shih 		break;
434e3ec7017SPing-Ke Shih 
435e3ec7017SPing-Ke Shih 	case SER_EV_M3_TIMEOUT:
436e3ec7017SPing-Ke Shih 		ser_state_goto(ser, SER_L2_RESET_ST);
437e3ec7017SPing-Ke Shih 		break;
438e3ec7017SPing-Ke Shih 
439e3ec7017SPing-Ke Shih 	case SER_EV_STATE_OUT:
440e3ec7017SPing-Ke Shih 		ser_del_alarm(ser);
441e3ec7017SPing-Ke Shih 		hal_enable_dma(ser);
442e3ec7017SPing-Ke Shih 		drv_resume_rx(ser);
443e3ec7017SPing-Ke Shih 		drv_resume_tx(ser);
444e3ec7017SPing-Ke Shih 		break;
445e3ec7017SPing-Ke Shih 
446e3ec7017SPing-Ke Shih 	default:
447e3ec7017SPing-Ke Shih 		break;
448e3ec7017SPing-Ke Shih 	}
449e3ec7017SPing-Ke Shih }
450e3ec7017SPing-Ke Shih 
451e3ec7017SPing-Ke Shih static void ser_do_hci_st_hdl(struct rtw89_ser *ser, u8 evt)
452e3ec7017SPing-Ke Shih {
453e3ec7017SPing-Ke Shih 	switch (evt) {
454e3ec7017SPing-Ke Shih 	case SER_EV_STATE_IN:
455e3ec7017SPing-Ke Shih 		/* wait m5 */
456e3ec7017SPing-Ke Shih 		hal_send_m4_event(ser);
457e3ec7017SPing-Ke Shih 
458e3ec7017SPing-Ke Shih 		/* prevent FW response timeout */
459e3ec7017SPing-Ke Shih 		ser_set_alarm(ser, 1000, SER_EV_FW_M5_TIMEOUT);
460e3ec7017SPing-Ke Shih 		break;
461e3ec7017SPing-Ke Shih 
462e3ec7017SPing-Ke Shih 	case SER_EV_FW_M5_TIMEOUT:
463e3ec7017SPing-Ke Shih 		ser_state_goto(ser, SER_L2_RESET_ST);
464e3ec7017SPing-Ke Shih 		break;
465e3ec7017SPing-Ke Shih 
466e3ec7017SPing-Ke Shih 	case SER_EV_MAC_RESET_DONE:
467e3ec7017SPing-Ke Shih 		ser_state_goto(ser, SER_IDLE_ST);
468e3ec7017SPing-Ke Shih 		break;
469e3ec7017SPing-Ke Shih 
470e3ec7017SPing-Ke Shih 	case SER_EV_STATE_OUT:
471e3ec7017SPing-Ke Shih 		ser_del_alarm(ser);
472e3ec7017SPing-Ke Shih 		break;
473e3ec7017SPing-Ke Shih 
474e3ec7017SPing-Ke Shih 	default:
475e3ec7017SPing-Ke Shih 		break;
476e3ec7017SPing-Ke Shih 	}
477e3ec7017SPing-Ke Shih }
478e3ec7017SPing-Ke Shih 
4799f8004bfSZong-Zhe Yang static void ser_mac_mem_dump(struct rtw89_dev *rtwdev, u8 *buf,
4809f8004bfSZong-Zhe Yang 			     u8 sel, u32 start_addr, u32 len)
4819f8004bfSZong-Zhe Yang {
4829f8004bfSZong-Zhe Yang 	u32 *ptr = (u32 *)buf;
4839f8004bfSZong-Zhe Yang 	u32 base_addr, start_page, residue;
4849f8004bfSZong-Zhe Yang 	u32 cnt = 0;
4859f8004bfSZong-Zhe Yang 	u32 i;
4869f8004bfSZong-Zhe Yang 
4879f8004bfSZong-Zhe Yang 	start_page = start_addr / MAC_MEM_DUMP_PAGE_SIZE;
4889f8004bfSZong-Zhe Yang 	residue = start_addr % MAC_MEM_DUMP_PAGE_SIZE;
4899f8004bfSZong-Zhe Yang 	base_addr = rtw89_mac_mem_base_addrs[sel];
4909f8004bfSZong-Zhe Yang 	base_addr += start_page * MAC_MEM_DUMP_PAGE_SIZE;
4919f8004bfSZong-Zhe Yang 
4929f8004bfSZong-Zhe Yang 	while (cnt < len) {
4939f8004bfSZong-Zhe Yang 		rtw89_write32(rtwdev, R_AX_FILTER_MODEL_ADDR, base_addr);
4949f8004bfSZong-Zhe Yang 
4959f8004bfSZong-Zhe Yang 		for (i = R_AX_INDIR_ACCESS_ENTRY + residue;
4969f8004bfSZong-Zhe Yang 		     i < R_AX_INDIR_ACCESS_ENTRY + MAC_MEM_DUMP_PAGE_SIZE;
4979f8004bfSZong-Zhe Yang 		     i += 4, ptr++) {
4989f8004bfSZong-Zhe Yang 			*ptr = rtw89_read32(rtwdev, i);
4999f8004bfSZong-Zhe Yang 			cnt += 4;
5009f8004bfSZong-Zhe Yang 			if (cnt >= len)
5019f8004bfSZong-Zhe Yang 				break;
5029f8004bfSZong-Zhe Yang 		}
5039f8004bfSZong-Zhe Yang 
5049f8004bfSZong-Zhe Yang 		residue = 0;
5059f8004bfSZong-Zhe Yang 		base_addr += MAC_MEM_DUMP_PAGE_SIZE;
5069f8004bfSZong-Zhe Yang 	}
5079f8004bfSZong-Zhe Yang }
5089f8004bfSZong-Zhe Yang 
5099f8004bfSZong-Zhe Yang static void rtw89_ser_fw_rsvd_ple_dump(struct rtw89_dev *rtwdev, u8 *buf)
5109f8004bfSZong-Zhe Yang {
5119f8004bfSZong-Zhe Yang 	u32 start_addr = rtwdev->chip->rsvd_ple_ofst;
5129f8004bfSZong-Zhe Yang 
5139f8004bfSZong-Zhe Yang 	rtw89_debug(rtwdev, RTW89_DBG_SER,
5149f8004bfSZong-Zhe Yang 		    "dump mem for fw rsvd payload engine (start addr: 0x%x)\n",
5159f8004bfSZong-Zhe Yang 		    start_addr);
5169f8004bfSZong-Zhe Yang 	ser_mac_mem_dump(rtwdev, buf, RTW89_MAC_MEM_SHARED_BUF, start_addr,
5179f8004bfSZong-Zhe Yang 			 RTW89_FW_RSVD_PLE_SIZE);
5189f8004bfSZong-Zhe Yang }
5199f8004bfSZong-Zhe Yang 
520f5e24684SZong-Zhe Yang struct __fw_backtrace_entry {
521f5e24684SZong-Zhe Yang 	u32 wcpu_addr;
522f5e24684SZong-Zhe Yang 	u32 size;
523f5e24684SZong-Zhe Yang 	u32 key;
524f5e24684SZong-Zhe Yang } __packed;
525f5e24684SZong-Zhe Yang 
526f5e24684SZong-Zhe Yang struct __fw_backtrace_info {
527f5e24684SZong-Zhe Yang 	u32 ra;
528f5e24684SZong-Zhe Yang 	u32 sp;
529f5e24684SZong-Zhe Yang } __packed;
530f5e24684SZong-Zhe Yang 
531f5e24684SZong-Zhe Yang static_assert(RTW89_FW_BACKTRACE_INFO_SIZE ==
532f5e24684SZong-Zhe Yang 	      sizeof(struct __fw_backtrace_info));
533f5e24684SZong-Zhe Yang 
534f5e24684SZong-Zhe Yang static int rtw89_ser_fw_backtrace_dump(struct rtw89_dev *rtwdev, u8 *buf,
535f5e24684SZong-Zhe Yang 				       const struct __fw_backtrace_entry *ent)
536f5e24684SZong-Zhe Yang {
537f5e24684SZong-Zhe Yang 	struct __fw_backtrace_info *ptr = (struct __fw_backtrace_info *)buf;
538f5e24684SZong-Zhe Yang 	u32 fwbt_addr = ent->wcpu_addr - RTW89_WCPU_BASE_ADDR;
539f5e24684SZong-Zhe Yang 	u32 fwbt_size = ent->size;
540f5e24684SZong-Zhe Yang 	u32 fwbt_key = ent->key;
541f5e24684SZong-Zhe Yang 	u32 i;
542f5e24684SZong-Zhe Yang 
543f5e24684SZong-Zhe Yang 	if (fwbt_addr == 0) {
544f5e24684SZong-Zhe Yang 		rtw89_warn(rtwdev, "FW backtrace invalid address: 0x%x\n",
545f5e24684SZong-Zhe Yang 			   fwbt_addr);
546f5e24684SZong-Zhe Yang 		return -EINVAL;
547f5e24684SZong-Zhe Yang 	}
548f5e24684SZong-Zhe Yang 
549f5e24684SZong-Zhe Yang 	if (fwbt_key != RTW89_FW_BACKTRACE_KEY) {
550f5e24684SZong-Zhe Yang 		rtw89_warn(rtwdev, "FW backtrace invalid key: 0x%x\n",
551f5e24684SZong-Zhe Yang 			   fwbt_key);
552f5e24684SZong-Zhe Yang 		return -EINVAL;
553f5e24684SZong-Zhe Yang 	}
554f5e24684SZong-Zhe Yang 
555f5e24684SZong-Zhe Yang 	if (fwbt_size == 0 || !RTW89_VALID_FW_BACKTRACE_SIZE(fwbt_size) ||
556f5e24684SZong-Zhe Yang 	    fwbt_size > RTW89_FW_BACKTRACE_MAX_SIZE) {
557f5e24684SZong-Zhe Yang 		rtw89_warn(rtwdev, "FW backtrace invalid size: 0x%x\n",
558f5e24684SZong-Zhe Yang 			   fwbt_size);
559f5e24684SZong-Zhe Yang 		return -EINVAL;
560f5e24684SZong-Zhe Yang 	}
561f5e24684SZong-Zhe Yang 
562f5e24684SZong-Zhe Yang 	rtw89_debug(rtwdev, RTW89_DBG_SER, "dump fw backtrace start\n");
563f5e24684SZong-Zhe Yang 	rtw89_write32(rtwdev, R_AX_FILTER_MODEL_ADDR, fwbt_addr);
564f5e24684SZong-Zhe Yang 
565f5e24684SZong-Zhe Yang 	for (i = R_AX_INDIR_ACCESS_ENTRY;
566f5e24684SZong-Zhe Yang 	     i < R_AX_INDIR_ACCESS_ENTRY + fwbt_size;
567f5e24684SZong-Zhe Yang 	     i += RTW89_FW_BACKTRACE_INFO_SIZE, ptr++) {
568f5e24684SZong-Zhe Yang 		*ptr = (struct __fw_backtrace_info){
569f5e24684SZong-Zhe Yang 			.ra = rtw89_read32(rtwdev, i),
570f5e24684SZong-Zhe Yang 			.sp = rtw89_read32(rtwdev, i + 4),
571f5e24684SZong-Zhe Yang 		};
572f5e24684SZong-Zhe Yang 		rtw89_debug(rtwdev, RTW89_DBG_SER,
573f5e24684SZong-Zhe Yang 			    "next sp: 0x%x, next ra: 0x%x\n",
574f5e24684SZong-Zhe Yang 			    ptr->sp, ptr->ra);
575f5e24684SZong-Zhe Yang 	}
576f5e24684SZong-Zhe Yang 
577f5e24684SZong-Zhe Yang 	rtw89_debug(rtwdev, RTW89_DBG_SER, "dump fw backtrace end\n");
578f5e24684SZong-Zhe Yang 	return 0;
579f5e24684SZong-Zhe Yang }
580f5e24684SZong-Zhe Yang 
5819f8004bfSZong-Zhe Yang static void ser_l2_reset_st_pre_hdl(struct rtw89_ser *ser)
5829f8004bfSZong-Zhe Yang {
5839f8004bfSZong-Zhe Yang 	struct rtw89_dev *rtwdev = container_of(ser, struct rtw89_dev, ser);
5849f8004bfSZong-Zhe Yang 	struct rtw89_ser_cd_buffer *buf;
585f5e24684SZong-Zhe Yang 	struct __fw_backtrace_entry fwbt_ent;
586f5e24684SZong-Zhe Yang 	int ret = 0;
5879f8004bfSZong-Zhe Yang 
5889f8004bfSZong-Zhe Yang 	buf = rtw89_ser_cd_prep(rtwdev);
589f5e24684SZong-Zhe Yang 	if (!buf) {
590f5e24684SZong-Zhe Yang 		ret = -ENOMEM;
5919f8004bfSZong-Zhe Yang 		goto bottom;
592f5e24684SZong-Zhe Yang 	}
5939f8004bfSZong-Zhe Yang 
5949f8004bfSZong-Zhe Yang 	rtw89_ser_fw_rsvd_ple_dump(rtwdev, buf->fwple.data);
595f5e24684SZong-Zhe Yang 
596f5e24684SZong-Zhe Yang 	fwbt_ent = *(struct __fw_backtrace_entry *)buf->fwple.data;
597f5e24684SZong-Zhe Yang 	ret = rtw89_ser_fw_backtrace_dump(rtwdev, buf->fwbt.data, &fwbt_ent);
598f5e24684SZong-Zhe Yang 	if (ret)
599f5e24684SZong-Zhe Yang 		goto bottom;
600f5e24684SZong-Zhe Yang 
6019f8004bfSZong-Zhe Yang 	rtw89_ser_cd_send(rtwdev, buf);
6029f8004bfSZong-Zhe Yang 
6039f8004bfSZong-Zhe Yang bottom:
604f5e24684SZong-Zhe Yang 	rtw89_ser_cd_free(rtwdev, buf, !!ret);
605f5e24684SZong-Zhe Yang 
6069f8004bfSZong-Zhe Yang 	ser_reset_mac_binding(rtwdev);
6079f8004bfSZong-Zhe Yang 	rtw89_core_stop(rtwdev);
608a88b6cc4SZong-Zhe Yang 	rtw89_entity_init(rtwdev);
6099f8004bfSZong-Zhe Yang 	INIT_LIST_HEAD(&rtwdev->rtwvifs_list);
6109f8004bfSZong-Zhe Yang }
6119f8004bfSZong-Zhe Yang 
612e3ec7017SPing-Ke Shih static void ser_l2_reset_st_hdl(struct rtw89_ser *ser, u8 evt)
613e3ec7017SPing-Ke Shih {
614e3ec7017SPing-Ke Shih 	struct rtw89_dev *rtwdev = container_of(ser, struct rtw89_dev, ser);
615e3ec7017SPing-Ke Shih 
616e3ec7017SPing-Ke Shih 	switch (evt) {
617e3ec7017SPing-Ke Shih 	case SER_EV_STATE_IN:
618e3ec7017SPing-Ke Shih 		mutex_lock(&rtwdev->mutex);
6199f8004bfSZong-Zhe Yang 		ser_l2_reset_st_pre_hdl(ser);
620e3ec7017SPing-Ke Shih 		mutex_unlock(&rtwdev->mutex);
621e3ec7017SPing-Ke Shih 
622e3ec7017SPing-Ke Shih 		ieee80211_restart_hw(rtwdev->hw);
623e3ec7017SPing-Ke Shih 		ser_set_alarm(ser, SER_RECFG_TIMEOUT, SER_EV_L2_RECFG_TIMEOUT);
624e3ec7017SPing-Ke Shih 		break;
625e3ec7017SPing-Ke Shih 
626e3ec7017SPing-Ke Shih 	case SER_EV_L2_RECFG_TIMEOUT:
627e3ec7017SPing-Ke Shih 		rtw89_info(rtwdev, "Err: ser L2 re-config timeout\n");
628e3ec7017SPing-Ke Shih 		fallthrough;
629e3ec7017SPing-Ke Shih 	case SER_EV_L2_RECFG_DONE:
630e3ec7017SPing-Ke Shih 		ser_state_goto(ser, SER_IDLE_ST);
631edb89629SZong-Zhe Yang 		clear_bit(RTW89_FLAG_RESTART_TRIGGER, rtwdev->flags);
632e3ec7017SPing-Ke Shih 		break;
633e3ec7017SPing-Ke Shih 
634e3ec7017SPing-Ke Shih 	case SER_EV_STATE_OUT:
635e3ec7017SPing-Ke Shih 		ser_del_alarm(ser);
636e3ec7017SPing-Ke Shih 		break;
637e3ec7017SPing-Ke Shih 
638e3ec7017SPing-Ke Shih 	default:
639e3ec7017SPing-Ke Shih 		break;
640e3ec7017SPing-Ke Shih 	}
641e3ec7017SPing-Ke Shih }
642e3ec7017SPing-Ke Shih 
643af5175acSJoe Perches static const struct event_ent ser_ev_tbl[] = {
644e3ec7017SPing-Ke Shih 	{SER_EV_NONE, "SER_EV_NONE"},
645e3ec7017SPing-Ke Shih 	{SER_EV_STATE_IN, "SER_EV_STATE_IN"},
646e3ec7017SPing-Ke Shih 	{SER_EV_STATE_OUT, "SER_EV_STATE_OUT"},
647e3ec7017SPing-Ke Shih 	{SER_EV_L1_RESET, "SER_EV_L1_RESET"},
648e3ec7017SPing-Ke Shih 	{SER_EV_DO_RECOVERY, "SER_EV_DO_RECOVERY m3"},
649e3ec7017SPing-Ke Shih 	{SER_EV_MAC_RESET_DONE, "SER_EV_MAC_RESET_DONE m5"},
650e3ec7017SPing-Ke Shih 	{SER_EV_L2_RESET, "SER_EV_L2_RESET"},
651e3ec7017SPing-Ke Shih 	{SER_EV_L2_RECFG_DONE, "SER_EV_L2_RECFG_DONE"},
652e3ec7017SPing-Ke Shih 	{SER_EV_L2_RECFG_TIMEOUT, "SER_EV_L2_RECFG_TIMEOUT"},
653e3ec7017SPing-Ke Shih 	{SER_EV_M3_TIMEOUT, "SER_EV_M3_TIMEOUT"},
654e3ec7017SPing-Ke Shih 	{SER_EV_FW_M5_TIMEOUT, "SER_EV_FW_M5_TIMEOUT"},
655e3ec7017SPing-Ke Shih 	{SER_EV_L0_RESET, "SER_EV_L0_RESET"},
656e3ec7017SPing-Ke Shih 	{SER_EV_MAXX, "SER_EV_MAX"}
657e3ec7017SPing-Ke Shih };
658e3ec7017SPing-Ke Shih 
659af5175acSJoe Perches static const struct state_ent ser_st_tbl[] = {
660e3ec7017SPing-Ke Shih 	{SER_IDLE_ST, "SER_IDLE_ST", ser_idle_st_hdl},
661e3ec7017SPing-Ke Shih 	{SER_RESET_TRX_ST, "SER_RESET_TRX_ST", ser_reset_trx_st_hdl},
662e3ec7017SPing-Ke Shih 	{SER_DO_HCI_ST, "SER_DO_HCI_ST", ser_do_hci_st_hdl},
663e3ec7017SPing-Ke Shih 	{SER_L2_RESET_ST, "SER_L2_RESET_ST", ser_l2_reset_st_hdl}
664e3ec7017SPing-Ke Shih };
665e3ec7017SPing-Ke Shih 
666e3ec7017SPing-Ke Shih int rtw89_ser_init(struct rtw89_dev *rtwdev)
667e3ec7017SPing-Ke Shih {
668e3ec7017SPing-Ke Shih 	struct rtw89_ser *ser = &rtwdev->ser;
669e3ec7017SPing-Ke Shih 
670e3ec7017SPing-Ke Shih 	memset(ser, 0, sizeof(*ser));
671e3ec7017SPing-Ke Shih 	INIT_LIST_HEAD(&ser->msg_q);
672e3ec7017SPing-Ke Shih 	ser->state = SER_IDLE_ST;
673e3ec7017SPing-Ke Shih 	ser->st_tbl = ser_st_tbl;
674e3ec7017SPing-Ke Shih 	ser->ev_tbl = ser_ev_tbl;
675e3ec7017SPing-Ke Shih 
676e3ec7017SPing-Ke Shih 	bitmap_zero(ser->flags, RTW89_NUM_OF_SER_FLAGS);
677e3ec7017SPing-Ke Shih 	spin_lock_init(&ser->msg_q_lock);
678e3ec7017SPing-Ke Shih 	INIT_WORK(&ser->ser_hdl_work, rtw89_ser_hdl_work);
679e3ec7017SPing-Ke Shih 	INIT_DELAYED_WORK(&ser->ser_alarm_work, rtw89_ser_alarm_work);
680e3ec7017SPing-Ke Shih 	return 0;
681e3ec7017SPing-Ke Shih }
682e3ec7017SPing-Ke Shih 
683e3ec7017SPing-Ke Shih int rtw89_ser_deinit(struct rtw89_dev *rtwdev)
684e3ec7017SPing-Ke Shih {
685e3ec7017SPing-Ke Shih 	struct rtw89_ser *ser = (struct rtw89_ser *)&rtwdev->ser;
686e3ec7017SPing-Ke Shih 
687e3ec7017SPing-Ke Shih 	set_bit(RTW89_SER_DRV_STOP_RUN, ser->flags);
688e3ec7017SPing-Ke Shih 	cancel_delayed_work_sync(&ser->ser_alarm_work);
689e3ec7017SPing-Ke Shih 	cancel_work_sync(&ser->ser_hdl_work);
690e3ec7017SPing-Ke Shih 	clear_bit(RTW89_SER_DRV_STOP_RUN, ser->flags);
691e3ec7017SPing-Ke Shih 	return 0;
692e3ec7017SPing-Ke Shih }
693e3ec7017SPing-Ke Shih 
694e3ec7017SPing-Ke Shih void rtw89_ser_recfg_done(struct rtw89_dev *rtwdev)
695e3ec7017SPing-Ke Shih {
696e3ec7017SPing-Ke Shih 	ser_send_msg(&rtwdev->ser, SER_EV_L2_RECFG_DONE);
697e3ec7017SPing-Ke Shih }
698e3ec7017SPing-Ke Shih 
699e3ec7017SPing-Ke Shih int rtw89_ser_notify(struct rtw89_dev *rtwdev, u32 err)
700e3ec7017SPing-Ke Shih {
701e3ec7017SPing-Ke Shih 	u8 event = SER_EV_NONE;
702e3ec7017SPing-Ke Shih 
703198b6cf7SZong-Zhe Yang 	rtw89_info(rtwdev, "SER catches error: 0x%x\n", err);
704e3ec7017SPing-Ke Shih 
705e3ec7017SPing-Ke Shih 	switch (err) {
706e3ec7017SPing-Ke Shih 	case MAC_AX_ERR_L1_ERR_DMAC:
707e3ec7017SPing-Ke Shih 	case MAC_AX_ERR_L0_PROMOTE_TO_L1:
708e3ec7017SPing-Ke Shih 		event = SER_EV_L1_RESET; /* M1 */
709e3ec7017SPing-Ke Shih 		break;
710e3ec7017SPing-Ke Shih 	case MAC_AX_ERR_L1_RESET_DISABLE_DMAC_DONE:
711e3ec7017SPing-Ke Shih 		event = SER_EV_DO_RECOVERY; /* M3 */
712e3ec7017SPing-Ke Shih 		break;
713e3ec7017SPing-Ke Shih 	case MAC_AX_ERR_L1_RESET_RECOVERY_DONE:
714e3ec7017SPing-Ke Shih 		event = SER_EV_MAC_RESET_DONE; /* M5 */
715e3ec7017SPing-Ke Shih 		break;
716e3ec7017SPing-Ke Shih 	case MAC_AX_ERR_L0_ERR_CMAC0:
717e3ec7017SPing-Ke Shih 	case MAC_AX_ERR_L0_ERR_CMAC1:
718e3ec7017SPing-Ke Shih 	case MAC_AX_ERR_L0_RESET_DONE:
719e3ec7017SPing-Ke Shih 		event = SER_EV_L0_RESET;
720e3ec7017SPing-Ke Shih 		break;
721e3ec7017SPing-Ke Shih 	default:
722e3ec7017SPing-Ke Shih 		if (err == MAC_AX_ERR_L1_PROMOTE_TO_L2 ||
723e3ec7017SPing-Ke Shih 		    (err >= MAC_AX_ERR_L2_ERR_AH_DMA &&
724e3ec7017SPing-Ke Shih 		     err <= MAC_AX_GET_ERR_MAX))
725e3ec7017SPing-Ke Shih 			event = SER_EV_L2_RESET;
726e3ec7017SPing-Ke Shih 		break;
727e3ec7017SPing-Ke Shih 	}
728e3ec7017SPing-Ke Shih 
729198b6cf7SZong-Zhe Yang 	if (event == SER_EV_NONE) {
730198b6cf7SZong-Zhe Yang 		rtw89_warn(rtwdev, "SER cannot recognize error: 0x%x\n", err);
731e3ec7017SPing-Ke Shih 		return -EINVAL;
732198b6cf7SZong-Zhe Yang 	}
733e3ec7017SPing-Ke Shih 
734e3ec7017SPing-Ke Shih 	ser_send_msg(&rtwdev->ser, event);
735e3ec7017SPing-Ke Shih 	return 0;
736e3ec7017SPing-Ke Shih }
737e3ec7017SPing-Ke Shih EXPORT_SYMBOL(rtw89_ser_notify);
738