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