162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
262306a36Sopenharmony_ci/* Copyright(c) 2019-2020  Realtek Corporation
362306a36Sopenharmony_ci */
462306a36Sopenharmony_ci
562306a36Sopenharmony_ci#include <linux/devcoredump.h>
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include "cam.h"
862306a36Sopenharmony_ci#include "chan.h"
962306a36Sopenharmony_ci#include "debug.h"
1062306a36Sopenharmony_ci#include "fw.h"
1162306a36Sopenharmony_ci#include "mac.h"
1262306a36Sopenharmony_ci#include "ps.h"
1362306a36Sopenharmony_ci#include "reg.h"
1462306a36Sopenharmony_ci#include "ser.h"
1562306a36Sopenharmony_ci#include "util.h"
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#define SER_RECFG_TIMEOUT 1000
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_cienum ser_evt {
2062306a36Sopenharmony_ci	SER_EV_NONE,
2162306a36Sopenharmony_ci	SER_EV_STATE_IN,
2262306a36Sopenharmony_ci	SER_EV_STATE_OUT,
2362306a36Sopenharmony_ci	SER_EV_L1_RESET_PREPARE, /* pre-M0 */
2462306a36Sopenharmony_ci	SER_EV_L1_RESET, /* M1 */
2562306a36Sopenharmony_ci	SER_EV_DO_RECOVERY, /* M3 */
2662306a36Sopenharmony_ci	SER_EV_MAC_RESET_DONE, /* M5 */
2762306a36Sopenharmony_ci	SER_EV_L2_RESET,
2862306a36Sopenharmony_ci	SER_EV_L2_RECFG_DONE,
2962306a36Sopenharmony_ci	SER_EV_L2_RECFG_TIMEOUT,
3062306a36Sopenharmony_ci	SER_EV_M1_TIMEOUT,
3162306a36Sopenharmony_ci	SER_EV_M3_TIMEOUT,
3262306a36Sopenharmony_ci	SER_EV_FW_M5_TIMEOUT,
3362306a36Sopenharmony_ci	SER_EV_L0_RESET,
3462306a36Sopenharmony_ci	SER_EV_MAXX
3562306a36Sopenharmony_ci};
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_cienum ser_state {
3862306a36Sopenharmony_ci	SER_IDLE_ST,
3962306a36Sopenharmony_ci	SER_L1_RESET_PRE_ST,
4062306a36Sopenharmony_ci	SER_RESET_TRX_ST,
4162306a36Sopenharmony_ci	SER_DO_HCI_ST,
4262306a36Sopenharmony_ci	SER_L2_RESET_ST,
4362306a36Sopenharmony_ci	SER_ST_MAX_ST
4462306a36Sopenharmony_ci};
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_cistruct ser_msg {
4762306a36Sopenharmony_ci	struct list_head list;
4862306a36Sopenharmony_ci	u8 event;
4962306a36Sopenharmony_ci};
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_cistruct state_ent {
5262306a36Sopenharmony_ci	u8 state;
5362306a36Sopenharmony_ci	char *name;
5462306a36Sopenharmony_ci	void (*st_func)(struct rtw89_ser *ser, u8 event);
5562306a36Sopenharmony_ci};
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_cistruct event_ent {
5862306a36Sopenharmony_ci	u8 event;
5962306a36Sopenharmony_ci	char *name;
6062306a36Sopenharmony_ci};
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_cistatic char *ser_ev_name(struct rtw89_ser *ser, u8 event)
6362306a36Sopenharmony_ci{
6462306a36Sopenharmony_ci	if (event < SER_EV_MAXX)
6562306a36Sopenharmony_ci		return ser->ev_tbl[event].name;
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	return "err_ev_name";
6862306a36Sopenharmony_ci}
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_cistatic char *ser_st_name(struct rtw89_ser *ser)
7162306a36Sopenharmony_ci{
7262306a36Sopenharmony_ci	if (ser->state < SER_ST_MAX_ST)
7362306a36Sopenharmony_ci		return ser->st_tbl[ser->state].name;
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	return "err_st_name";
7662306a36Sopenharmony_ci}
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci#define RTW89_DEF_SER_CD_TYPE(_name, _type, _size) \
7962306a36Sopenharmony_cistruct ser_cd_ ## _name { \
8062306a36Sopenharmony_ci	u32 type; \
8162306a36Sopenharmony_ci	u32 type_size; \
8262306a36Sopenharmony_ci	u64 padding; \
8362306a36Sopenharmony_ci	u8 data[_size]; \
8462306a36Sopenharmony_ci} __packed; \
8562306a36Sopenharmony_cistatic void ser_cd_ ## _name ## _init(struct ser_cd_ ## _name *p) \
8662306a36Sopenharmony_ci{ \
8762306a36Sopenharmony_ci	p->type = _type; \
8862306a36Sopenharmony_ci	p->type_size = sizeof(p->data); \
8962306a36Sopenharmony_ci	p->padding = 0x0123456789abcdef; \
9062306a36Sopenharmony_ci}
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_cienum rtw89_ser_cd_type {
9362306a36Sopenharmony_ci	RTW89_SER_CD_FW_RSVD_PLE	= 0,
9462306a36Sopenharmony_ci	RTW89_SER_CD_FW_BACKTRACE	= 1,
9562306a36Sopenharmony_ci};
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ciRTW89_DEF_SER_CD_TYPE(fw_rsvd_ple,
9862306a36Sopenharmony_ci		      RTW89_SER_CD_FW_RSVD_PLE,
9962306a36Sopenharmony_ci		      RTW89_FW_RSVD_PLE_SIZE);
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ciRTW89_DEF_SER_CD_TYPE(fw_backtrace,
10262306a36Sopenharmony_ci		      RTW89_SER_CD_FW_BACKTRACE,
10362306a36Sopenharmony_ci		      RTW89_FW_BACKTRACE_MAX_SIZE);
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_cistruct rtw89_ser_cd_buffer {
10662306a36Sopenharmony_ci	struct ser_cd_fw_rsvd_ple fwple;
10762306a36Sopenharmony_ci	struct ser_cd_fw_backtrace fwbt;
10862306a36Sopenharmony_ci} __packed;
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_cistatic struct rtw89_ser_cd_buffer *rtw89_ser_cd_prep(struct rtw89_dev *rtwdev)
11162306a36Sopenharmony_ci{
11262306a36Sopenharmony_ci	struct rtw89_ser_cd_buffer *buf;
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	buf = vzalloc(sizeof(*buf));
11562306a36Sopenharmony_ci	if (!buf)
11662306a36Sopenharmony_ci		return NULL;
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	ser_cd_fw_rsvd_ple_init(&buf->fwple);
11962306a36Sopenharmony_ci	ser_cd_fw_backtrace_init(&buf->fwbt);
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	return buf;
12262306a36Sopenharmony_ci}
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_cistatic void rtw89_ser_cd_send(struct rtw89_dev *rtwdev,
12562306a36Sopenharmony_ci			      struct rtw89_ser_cd_buffer *buf)
12662306a36Sopenharmony_ci{
12762306a36Sopenharmony_ci	rtw89_debug(rtwdev, RTW89_DBG_SER, "SER sends core dump\n");
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	/* After calling dev_coredump, buf's lifetime is supposed to be
13062306a36Sopenharmony_ci	 * handled by the device coredump framework. Note that a new dump
13162306a36Sopenharmony_ci	 * will be discarded if a previous one hasn't been released by
13262306a36Sopenharmony_ci	 * framework yet.
13362306a36Sopenharmony_ci	 */
13462306a36Sopenharmony_ci	dev_coredumpv(rtwdev->dev, buf, sizeof(*buf), GFP_KERNEL);
13562306a36Sopenharmony_ci}
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_cistatic void rtw89_ser_cd_free(struct rtw89_dev *rtwdev,
13862306a36Sopenharmony_ci			      struct rtw89_ser_cd_buffer *buf, bool free_self)
13962306a36Sopenharmony_ci{
14062306a36Sopenharmony_ci	if (!free_self)
14162306a36Sopenharmony_ci		return;
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	rtw89_debug(rtwdev, RTW89_DBG_SER, "SER frees core dump by self\n");
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	/* When some problems happen during filling data of core dump,
14662306a36Sopenharmony_ci	 * we won't send it to device coredump framework. Instead, we
14762306a36Sopenharmony_ci	 * free buf by ourselves.
14862306a36Sopenharmony_ci	 */
14962306a36Sopenharmony_ci	vfree(buf);
15062306a36Sopenharmony_ci}
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_cistatic void ser_state_run(struct rtw89_ser *ser, u8 evt)
15362306a36Sopenharmony_ci{
15462306a36Sopenharmony_ci	struct rtw89_dev *rtwdev = container_of(ser, struct rtw89_dev, ser);
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	rtw89_debug(rtwdev, RTW89_DBG_SER, "ser: %s receive %s\n",
15762306a36Sopenharmony_ci		    ser_st_name(ser), ser_ev_name(ser, evt));
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	mutex_lock(&rtwdev->mutex);
16062306a36Sopenharmony_ci	rtw89_leave_lps(rtwdev);
16162306a36Sopenharmony_ci	mutex_unlock(&rtwdev->mutex);
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	ser->st_tbl[ser->state].st_func(ser, evt);
16462306a36Sopenharmony_ci}
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_cistatic void ser_state_goto(struct rtw89_ser *ser, u8 new_state)
16762306a36Sopenharmony_ci{
16862306a36Sopenharmony_ci	struct rtw89_dev *rtwdev = container_of(ser, struct rtw89_dev, ser);
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	if (ser->state == new_state || new_state >= SER_ST_MAX_ST)
17162306a36Sopenharmony_ci		return;
17262306a36Sopenharmony_ci	ser_state_run(ser, SER_EV_STATE_OUT);
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	rtw89_debug(rtwdev, RTW89_DBG_SER, "ser: %s goto -> %s\n",
17562306a36Sopenharmony_ci		    ser_st_name(ser), ser->st_tbl[new_state].name);
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	ser->state = new_state;
17862306a36Sopenharmony_ci	ser_state_run(ser, SER_EV_STATE_IN);
17962306a36Sopenharmony_ci}
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_cistatic struct ser_msg *__rtw89_ser_dequeue_msg(struct rtw89_ser *ser)
18262306a36Sopenharmony_ci{
18362306a36Sopenharmony_ci	struct ser_msg *msg;
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	spin_lock_irq(&ser->msg_q_lock);
18662306a36Sopenharmony_ci	msg = list_first_entry_or_null(&ser->msg_q, struct ser_msg, list);
18762306a36Sopenharmony_ci	if (msg)
18862306a36Sopenharmony_ci		list_del(&msg->list);
18962306a36Sopenharmony_ci	spin_unlock_irq(&ser->msg_q_lock);
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	return msg;
19262306a36Sopenharmony_ci}
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_cistatic void rtw89_ser_hdl_work(struct work_struct *work)
19562306a36Sopenharmony_ci{
19662306a36Sopenharmony_ci	struct ser_msg *msg;
19762306a36Sopenharmony_ci	struct rtw89_ser *ser = container_of(work, struct rtw89_ser,
19862306a36Sopenharmony_ci					     ser_hdl_work);
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci	while ((msg = __rtw89_ser_dequeue_msg(ser))) {
20162306a36Sopenharmony_ci		ser_state_run(ser, msg->event);
20262306a36Sopenharmony_ci		kfree(msg);
20362306a36Sopenharmony_ci	}
20462306a36Sopenharmony_ci}
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_cistatic int ser_send_msg(struct rtw89_ser *ser, u8 event)
20762306a36Sopenharmony_ci{
20862306a36Sopenharmony_ci	struct rtw89_dev *rtwdev = container_of(ser, struct rtw89_dev, ser);
20962306a36Sopenharmony_ci	struct ser_msg *msg = NULL;
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	if (test_bit(RTW89_SER_DRV_STOP_RUN, ser->flags))
21262306a36Sopenharmony_ci		return -EIO;
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	msg = kmalloc(sizeof(*msg), GFP_ATOMIC);
21562306a36Sopenharmony_ci	if (!msg)
21662306a36Sopenharmony_ci		return -ENOMEM;
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci	msg->event = event;
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	spin_lock_irq(&ser->msg_q_lock);
22162306a36Sopenharmony_ci	list_add(&msg->list, &ser->msg_q);
22262306a36Sopenharmony_ci	spin_unlock_irq(&ser->msg_q_lock);
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	ieee80211_queue_work(rtwdev->hw, &ser->ser_hdl_work);
22562306a36Sopenharmony_ci	return 0;
22662306a36Sopenharmony_ci}
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_cistatic void rtw89_ser_alarm_work(struct work_struct *work)
22962306a36Sopenharmony_ci{
23062306a36Sopenharmony_ci	struct rtw89_ser *ser = container_of(work, struct rtw89_ser,
23162306a36Sopenharmony_ci					     ser_alarm_work.work);
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	ser_send_msg(ser, ser->alarm_event);
23462306a36Sopenharmony_ci	ser->alarm_event = SER_EV_NONE;
23562306a36Sopenharmony_ci}
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_cistatic void ser_set_alarm(struct rtw89_ser *ser, u32 ms, u8 event)
23862306a36Sopenharmony_ci{
23962306a36Sopenharmony_ci	struct rtw89_dev *rtwdev = container_of(ser, struct rtw89_dev, ser);
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	if (test_bit(RTW89_SER_DRV_STOP_RUN, ser->flags))
24262306a36Sopenharmony_ci		return;
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	ser->alarm_event = event;
24562306a36Sopenharmony_ci	ieee80211_queue_delayed_work(rtwdev->hw, &ser->ser_alarm_work,
24662306a36Sopenharmony_ci				     msecs_to_jiffies(ms));
24762306a36Sopenharmony_ci}
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_cistatic void ser_del_alarm(struct rtw89_ser *ser)
25062306a36Sopenharmony_ci{
25162306a36Sopenharmony_ci	cancel_delayed_work(&ser->ser_alarm_work);
25262306a36Sopenharmony_ci	ser->alarm_event = SER_EV_NONE;
25362306a36Sopenharmony_ci}
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci/* driver function */
25662306a36Sopenharmony_cistatic void drv_stop_tx(struct rtw89_ser *ser)
25762306a36Sopenharmony_ci{
25862306a36Sopenharmony_ci	struct rtw89_dev *rtwdev = container_of(ser, struct rtw89_dev, ser);
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci	ieee80211_stop_queues(rtwdev->hw);
26162306a36Sopenharmony_ci	set_bit(RTW89_SER_DRV_STOP_TX, ser->flags);
26262306a36Sopenharmony_ci}
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_cistatic void drv_stop_rx(struct rtw89_ser *ser)
26562306a36Sopenharmony_ci{
26662306a36Sopenharmony_ci	struct rtw89_dev *rtwdev = container_of(ser, struct rtw89_dev, ser);
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	clear_bit(RTW89_FLAG_RUNNING, rtwdev->flags);
26962306a36Sopenharmony_ci	set_bit(RTW89_SER_DRV_STOP_RX, ser->flags);
27062306a36Sopenharmony_ci}
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_cistatic void drv_trx_reset(struct rtw89_ser *ser)
27362306a36Sopenharmony_ci{
27462306a36Sopenharmony_ci	struct rtw89_dev *rtwdev = container_of(ser, struct rtw89_dev, ser);
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	rtw89_hci_reset(rtwdev);
27762306a36Sopenharmony_ci}
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_cistatic void drv_resume_tx(struct rtw89_ser *ser)
28062306a36Sopenharmony_ci{
28162306a36Sopenharmony_ci	struct rtw89_dev *rtwdev = container_of(ser, struct rtw89_dev, ser);
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci	if (!test_bit(RTW89_SER_DRV_STOP_TX, ser->flags))
28462306a36Sopenharmony_ci		return;
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	ieee80211_wake_queues(rtwdev->hw);
28762306a36Sopenharmony_ci	clear_bit(RTW89_SER_DRV_STOP_TX, ser->flags);
28862306a36Sopenharmony_ci}
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_cistatic void drv_resume_rx(struct rtw89_ser *ser)
29162306a36Sopenharmony_ci{
29262306a36Sopenharmony_ci	struct rtw89_dev *rtwdev = container_of(ser, struct rtw89_dev, ser);
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	if (!test_bit(RTW89_SER_DRV_STOP_RX, ser->flags))
29562306a36Sopenharmony_ci		return;
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci	set_bit(RTW89_FLAG_RUNNING, rtwdev->flags);
29862306a36Sopenharmony_ci	clear_bit(RTW89_SER_DRV_STOP_RX, ser->flags);
29962306a36Sopenharmony_ci}
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_cistatic void ser_reset_vif(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif)
30262306a36Sopenharmony_ci{
30362306a36Sopenharmony_ci	rtw89_core_release_bit_map(rtwdev->hw_port, rtwvif->port);
30462306a36Sopenharmony_ci	rtwvif->net_type = RTW89_NET_TYPE_NO_LINK;
30562306a36Sopenharmony_ci	rtwvif->trigger = false;
30662306a36Sopenharmony_ci	rtwvif->tdls_peer = 0;
30762306a36Sopenharmony_ci}
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_cistatic void ser_sta_deinit_cam_iter(void *data, struct ieee80211_sta *sta)
31062306a36Sopenharmony_ci{
31162306a36Sopenharmony_ci	struct rtw89_vif *rtwvif = (struct rtw89_vif *)data;
31262306a36Sopenharmony_ci	struct rtw89_dev *rtwdev = rtwvif->rtwdev;
31362306a36Sopenharmony_ci	struct rtw89_sta *rtwsta = (struct rtw89_sta *)sta->drv_priv;
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	if (rtwvif->net_type == RTW89_NET_TYPE_AP_MODE || sta->tdls)
31662306a36Sopenharmony_ci		rtw89_cam_deinit_addr_cam(rtwdev, &rtwsta->addr_cam);
31762306a36Sopenharmony_ci	if (sta->tdls)
31862306a36Sopenharmony_ci		rtw89_cam_deinit_bssid_cam(rtwdev, &rtwsta->bssid_cam);
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	INIT_LIST_HEAD(&rtwsta->ba_cam_list);
32162306a36Sopenharmony_ci}
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_cistatic void ser_deinit_cam(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif)
32462306a36Sopenharmony_ci{
32562306a36Sopenharmony_ci	ieee80211_iterate_stations_atomic(rtwdev->hw,
32662306a36Sopenharmony_ci					  ser_sta_deinit_cam_iter,
32762306a36Sopenharmony_ci					  rtwvif);
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	rtw89_cam_deinit(rtwdev, rtwvif);
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci	bitmap_zero(rtwdev->cam_info.ba_cam_map, RTW89_MAX_BA_CAM_NUM);
33262306a36Sopenharmony_ci}
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_cistatic void ser_reset_mac_binding(struct rtw89_dev *rtwdev)
33562306a36Sopenharmony_ci{
33662306a36Sopenharmony_ci	struct rtw89_vif *rtwvif;
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	rtw89_cam_reset_keys(rtwdev);
33962306a36Sopenharmony_ci	rtw89_for_each_rtwvif(rtwdev, rtwvif)
34062306a36Sopenharmony_ci		ser_deinit_cam(rtwdev, rtwvif);
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	rtw89_core_release_all_bits_map(rtwdev->mac_id_map, RTW89_MAX_MAC_ID_NUM);
34362306a36Sopenharmony_ci	rtw89_for_each_rtwvif(rtwdev, rtwvif)
34462306a36Sopenharmony_ci		ser_reset_vif(rtwdev, rtwvif);
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci	rtwdev->total_sta_assoc = 0;
34762306a36Sopenharmony_ci}
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci/* hal function */
35062306a36Sopenharmony_cistatic int hal_enable_dma(struct rtw89_ser *ser)
35162306a36Sopenharmony_ci{
35262306a36Sopenharmony_ci	struct rtw89_dev *rtwdev = container_of(ser, struct rtw89_dev, ser);
35362306a36Sopenharmony_ci	int ret;
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci	if (!test_bit(RTW89_SER_HAL_STOP_DMA, ser->flags))
35662306a36Sopenharmony_ci		return 0;
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci	if (!rtwdev->hci.ops->mac_lv1_rcvy)
35962306a36Sopenharmony_ci		return -EIO;
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci	ret = rtwdev->hci.ops->mac_lv1_rcvy(rtwdev, RTW89_LV1_RCVY_STEP_2);
36262306a36Sopenharmony_ci	if (!ret)
36362306a36Sopenharmony_ci		clear_bit(RTW89_SER_HAL_STOP_DMA, ser->flags);
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci	return ret;
36662306a36Sopenharmony_ci}
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_cistatic int hal_stop_dma(struct rtw89_ser *ser)
36962306a36Sopenharmony_ci{
37062306a36Sopenharmony_ci	struct rtw89_dev *rtwdev = container_of(ser, struct rtw89_dev, ser);
37162306a36Sopenharmony_ci	int ret;
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci	if (!rtwdev->hci.ops->mac_lv1_rcvy)
37462306a36Sopenharmony_ci		return -EIO;
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	ret = rtwdev->hci.ops->mac_lv1_rcvy(rtwdev, RTW89_LV1_RCVY_STEP_1);
37762306a36Sopenharmony_ci	if (!ret)
37862306a36Sopenharmony_ci		set_bit(RTW89_SER_HAL_STOP_DMA, ser->flags);
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	return ret;
38162306a36Sopenharmony_ci}
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_cistatic void hal_send_post_m0_event(struct rtw89_ser *ser)
38462306a36Sopenharmony_ci{
38562306a36Sopenharmony_ci	struct rtw89_dev *rtwdev = container_of(ser, struct rtw89_dev, ser);
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci	rtw89_mac_set_err_status(rtwdev, MAC_AX_ERR_L1_RESET_START_DMAC);
38862306a36Sopenharmony_ci}
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_cistatic void hal_send_m2_event(struct rtw89_ser *ser)
39162306a36Sopenharmony_ci{
39262306a36Sopenharmony_ci	struct rtw89_dev *rtwdev = container_of(ser, struct rtw89_dev, ser);
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci	rtw89_mac_set_err_status(rtwdev, MAC_AX_ERR_L1_DISABLE_EN);
39562306a36Sopenharmony_ci}
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_cistatic void hal_send_m4_event(struct rtw89_ser *ser)
39862306a36Sopenharmony_ci{
39962306a36Sopenharmony_ci	struct rtw89_dev *rtwdev = container_of(ser, struct rtw89_dev, ser);
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci	rtw89_mac_set_err_status(rtwdev, MAC_AX_ERR_L1_RCVY_EN);
40262306a36Sopenharmony_ci}
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci/* state handler */
40562306a36Sopenharmony_cistatic void ser_idle_st_hdl(struct rtw89_ser *ser, u8 evt)
40662306a36Sopenharmony_ci{
40762306a36Sopenharmony_ci	struct rtw89_dev *rtwdev = container_of(ser, struct rtw89_dev, ser);
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	switch (evt) {
41062306a36Sopenharmony_ci	case SER_EV_STATE_IN:
41162306a36Sopenharmony_ci		rtw89_hci_recovery_complete(rtwdev);
41262306a36Sopenharmony_ci		clear_bit(RTW89_FLAG_SER_HANDLING, rtwdev->flags);
41362306a36Sopenharmony_ci		clear_bit(RTW89_FLAG_CRASH_SIMULATING, rtwdev->flags);
41462306a36Sopenharmony_ci		break;
41562306a36Sopenharmony_ci	case SER_EV_L1_RESET_PREPARE:
41662306a36Sopenharmony_ci		ser_state_goto(ser, SER_L1_RESET_PRE_ST);
41762306a36Sopenharmony_ci		break;
41862306a36Sopenharmony_ci	case SER_EV_L1_RESET:
41962306a36Sopenharmony_ci		ser_state_goto(ser, SER_RESET_TRX_ST);
42062306a36Sopenharmony_ci		break;
42162306a36Sopenharmony_ci	case SER_EV_L2_RESET:
42262306a36Sopenharmony_ci		ser_state_goto(ser, SER_L2_RESET_ST);
42362306a36Sopenharmony_ci		break;
42462306a36Sopenharmony_ci	case SER_EV_STATE_OUT:
42562306a36Sopenharmony_ci		set_bit(RTW89_FLAG_SER_HANDLING, rtwdev->flags);
42662306a36Sopenharmony_ci		rtw89_hci_recovery_start(rtwdev);
42762306a36Sopenharmony_ci		break;
42862306a36Sopenharmony_ci	default:
42962306a36Sopenharmony_ci		break;
43062306a36Sopenharmony_ci	}
43162306a36Sopenharmony_ci}
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_cistatic void ser_l1_reset_pre_st_hdl(struct rtw89_ser *ser, u8 evt)
43462306a36Sopenharmony_ci{
43562306a36Sopenharmony_ci	switch (evt) {
43662306a36Sopenharmony_ci	case SER_EV_STATE_IN:
43762306a36Sopenharmony_ci		ser->prehandle_l1 = true;
43862306a36Sopenharmony_ci		hal_send_post_m0_event(ser);
43962306a36Sopenharmony_ci		ser_set_alarm(ser, 1000, SER_EV_M1_TIMEOUT);
44062306a36Sopenharmony_ci		break;
44162306a36Sopenharmony_ci	case SER_EV_L1_RESET:
44262306a36Sopenharmony_ci		ser_state_goto(ser, SER_RESET_TRX_ST);
44362306a36Sopenharmony_ci		break;
44462306a36Sopenharmony_ci	case SER_EV_M1_TIMEOUT:
44562306a36Sopenharmony_ci		ser_state_goto(ser, SER_L2_RESET_ST);
44662306a36Sopenharmony_ci		break;
44762306a36Sopenharmony_ci	case SER_EV_STATE_OUT:
44862306a36Sopenharmony_ci		ser_del_alarm(ser);
44962306a36Sopenharmony_ci		break;
45062306a36Sopenharmony_ci	default:
45162306a36Sopenharmony_ci		break;
45262306a36Sopenharmony_ci	}
45362306a36Sopenharmony_ci}
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_cistatic void ser_reset_trx_st_hdl(struct rtw89_ser *ser, u8 evt)
45662306a36Sopenharmony_ci{
45762306a36Sopenharmony_ci	struct rtw89_dev *rtwdev = container_of(ser, struct rtw89_dev, ser);
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci	switch (evt) {
46062306a36Sopenharmony_ci	case SER_EV_STATE_IN:
46162306a36Sopenharmony_ci		cancel_delayed_work_sync(&rtwdev->track_work);
46262306a36Sopenharmony_ci		drv_stop_tx(ser);
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci		if (hal_stop_dma(ser)) {
46562306a36Sopenharmony_ci			ser_state_goto(ser, SER_L2_RESET_ST);
46662306a36Sopenharmony_ci			break;
46762306a36Sopenharmony_ci		}
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci		drv_stop_rx(ser);
47062306a36Sopenharmony_ci		drv_trx_reset(ser);
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci		/* wait m3 */
47362306a36Sopenharmony_ci		hal_send_m2_event(ser);
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci		/* set alarm to prevent FW response timeout */
47662306a36Sopenharmony_ci		ser_set_alarm(ser, 1000, SER_EV_M3_TIMEOUT);
47762306a36Sopenharmony_ci		break;
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci	case SER_EV_DO_RECOVERY:
48062306a36Sopenharmony_ci		ser_state_goto(ser, SER_DO_HCI_ST);
48162306a36Sopenharmony_ci		break;
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ci	case SER_EV_M3_TIMEOUT:
48462306a36Sopenharmony_ci		ser_state_goto(ser, SER_L2_RESET_ST);
48562306a36Sopenharmony_ci		break;
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci	case SER_EV_STATE_OUT:
48862306a36Sopenharmony_ci		ser_del_alarm(ser);
48962306a36Sopenharmony_ci		hal_enable_dma(ser);
49062306a36Sopenharmony_ci		drv_resume_rx(ser);
49162306a36Sopenharmony_ci		drv_resume_tx(ser);
49262306a36Sopenharmony_ci		ieee80211_queue_delayed_work(rtwdev->hw, &rtwdev->track_work,
49362306a36Sopenharmony_ci					     RTW89_TRACK_WORK_PERIOD);
49462306a36Sopenharmony_ci		break;
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	default:
49762306a36Sopenharmony_ci		break;
49862306a36Sopenharmony_ci	}
49962306a36Sopenharmony_ci}
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_cistatic void ser_do_hci_st_hdl(struct rtw89_ser *ser, u8 evt)
50262306a36Sopenharmony_ci{
50362306a36Sopenharmony_ci	switch (evt) {
50462306a36Sopenharmony_ci	case SER_EV_STATE_IN:
50562306a36Sopenharmony_ci		/* wait m5 */
50662306a36Sopenharmony_ci		hal_send_m4_event(ser);
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_ci		/* prevent FW response timeout */
50962306a36Sopenharmony_ci		ser_set_alarm(ser, 1000, SER_EV_FW_M5_TIMEOUT);
51062306a36Sopenharmony_ci		break;
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci	case SER_EV_FW_M5_TIMEOUT:
51362306a36Sopenharmony_ci		ser_state_goto(ser, SER_L2_RESET_ST);
51462306a36Sopenharmony_ci		break;
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_ci	case SER_EV_MAC_RESET_DONE:
51762306a36Sopenharmony_ci		ser_state_goto(ser, SER_IDLE_ST);
51862306a36Sopenharmony_ci		break;
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_ci	case SER_EV_STATE_OUT:
52162306a36Sopenharmony_ci		ser_del_alarm(ser);
52262306a36Sopenharmony_ci		break;
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci	default:
52562306a36Sopenharmony_ci		break;
52662306a36Sopenharmony_ci	}
52762306a36Sopenharmony_ci}
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_cistatic void ser_mac_mem_dump(struct rtw89_dev *rtwdev, u8 *buf,
53062306a36Sopenharmony_ci			     u8 sel, u32 start_addr, u32 len)
53162306a36Sopenharmony_ci{
53262306a36Sopenharmony_ci	const struct rtw89_mac_gen_def *mac = rtwdev->chip->mac_def;
53362306a36Sopenharmony_ci	u32 filter_model_addr = mac->filter_model_addr;
53462306a36Sopenharmony_ci	u32 indir_access_addr = mac->indir_access_addr;
53562306a36Sopenharmony_ci	u32 *ptr = (u32 *)buf;
53662306a36Sopenharmony_ci	u32 base_addr, start_page, residue;
53762306a36Sopenharmony_ci	u32 cnt = 0;
53862306a36Sopenharmony_ci	u32 i;
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci	start_page = start_addr / MAC_MEM_DUMP_PAGE_SIZE;
54162306a36Sopenharmony_ci	residue = start_addr % MAC_MEM_DUMP_PAGE_SIZE;
54262306a36Sopenharmony_ci	base_addr = mac->mem_base_addrs[sel];
54362306a36Sopenharmony_ci	base_addr += start_page * MAC_MEM_DUMP_PAGE_SIZE;
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci	while (cnt < len) {
54662306a36Sopenharmony_ci		rtw89_write32(rtwdev, filter_model_addr, base_addr);
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci		for (i = indir_access_addr + residue;
54962306a36Sopenharmony_ci		     i < indir_access_addr + MAC_MEM_DUMP_PAGE_SIZE;
55062306a36Sopenharmony_ci		     i += 4, ptr++) {
55162306a36Sopenharmony_ci			*ptr = rtw89_read32(rtwdev, i);
55262306a36Sopenharmony_ci			cnt += 4;
55362306a36Sopenharmony_ci			if (cnt >= len)
55462306a36Sopenharmony_ci				break;
55562306a36Sopenharmony_ci		}
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ci		residue = 0;
55862306a36Sopenharmony_ci		base_addr += MAC_MEM_DUMP_PAGE_SIZE;
55962306a36Sopenharmony_ci	}
56062306a36Sopenharmony_ci}
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_cistatic void rtw89_ser_fw_rsvd_ple_dump(struct rtw89_dev *rtwdev, u8 *buf)
56362306a36Sopenharmony_ci{
56462306a36Sopenharmony_ci	u32 start_addr = rtwdev->chip->rsvd_ple_ofst;
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_ci	rtw89_debug(rtwdev, RTW89_DBG_SER,
56762306a36Sopenharmony_ci		    "dump mem for fw rsvd payload engine (start addr: 0x%x)\n",
56862306a36Sopenharmony_ci		    start_addr);
56962306a36Sopenharmony_ci	ser_mac_mem_dump(rtwdev, buf, RTW89_MAC_MEM_SHARED_BUF, start_addr,
57062306a36Sopenharmony_ci			 RTW89_FW_RSVD_PLE_SIZE);
57162306a36Sopenharmony_ci}
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_cistruct __fw_backtrace_entry {
57462306a36Sopenharmony_ci	u32 wcpu_addr;
57562306a36Sopenharmony_ci	u32 size;
57662306a36Sopenharmony_ci	u32 key;
57762306a36Sopenharmony_ci} __packed;
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_cistruct __fw_backtrace_info {
58062306a36Sopenharmony_ci	u32 ra;
58162306a36Sopenharmony_ci	u32 sp;
58262306a36Sopenharmony_ci} __packed;
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_cistatic_assert(RTW89_FW_BACKTRACE_INFO_SIZE ==
58562306a36Sopenharmony_ci	      sizeof(struct __fw_backtrace_info));
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_cistatic int rtw89_ser_fw_backtrace_dump(struct rtw89_dev *rtwdev, u8 *buf,
58862306a36Sopenharmony_ci				       const struct __fw_backtrace_entry *ent)
58962306a36Sopenharmony_ci{
59062306a36Sopenharmony_ci	struct __fw_backtrace_info *ptr = (struct __fw_backtrace_info *)buf;
59162306a36Sopenharmony_ci	const struct rtw89_mac_gen_def *mac = rtwdev->chip->mac_def;
59262306a36Sopenharmony_ci	u32 filter_model_addr = mac->filter_model_addr;
59362306a36Sopenharmony_ci	u32 indir_access_addr = mac->indir_access_addr;
59462306a36Sopenharmony_ci	u32 fwbt_addr = ent->wcpu_addr & RTW89_WCPU_BASE_MASK;
59562306a36Sopenharmony_ci	u32 fwbt_size = ent->size;
59662306a36Sopenharmony_ci	u32 fwbt_key = ent->key;
59762306a36Sopenharmony_ci	u32 i;
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_ci	if (fwbt_addr == 0) {
60062306a36Sopenharmony_ci		rtw89_warn(rtwdev, "FW backtrace invalid address: 0x%x\n",
60162306a36Sopenharmony_ci			   fwbt_addr);
60262306a36Sopenharmony_ci		return -EINVAL;
60362306a36Sopenharmony_ci	}
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_ci	if (fwbt_key != RTW89_FW_BACKTRACE_KEY) {
60662306a36Sopenharmony_ci		rtw89_warn(rtwdev, "FW backtrace invalid key: 0x%x\n",
60762306a36Sopenharmony_ci			   fwbt_key);
60862306a36Sopenharmony_ci		return -EINVAL;
60962306a36Sopenharmony_ci	}
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_ci	if (fwbt_size == 0 || !RTW89_VALID_FW_BACKTRACE_SIZE(fwbt_size) ||
61262306a36Sopenharmony_ci	    fwbt_size > RTW89_FW_BACKTRACE_MAX_SIZE) {
61362306a36Sopenharmony_ci		rtw89_warn(rtwdev, "FW backtrace invalid size: 0x%x\n",
61462306a36Sopenharmony_ci			   fwbt_size);
61562306a36Sopenharmony_ci		return -EINVAL;
61662306a36Sopenharmony_ci	}
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_ci	rtw89_debug(rtwdev, RTW89_DBG_SER, "dump fw backtrace start\n");
61962306a36Sopenharmony_ci	rtw89_write32(rtwdev, filter_model_addr, fwbt_addr);
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_ci	for (i = indir_access_addr;
62262306a36Sopenharmony_ci	     i < indir_access_addr + fwbt_size;
62362306a36Sopenharmony_ci	     i += RTW89_FW_BACKTRACE_INFO_SIZE, ptr++) {
62462306a36Sopenharmony_ci		*ptr = (struct __fw_backtrace_info){
62562306a36Sopenharmony_ci			.ra = rtw89_read32(rtwdev, i),
62662306a36Sopenharmony_ci			.sp = rtw89_read32(rtwdev, i + 4),
62762306a36Sopenharmony_ci		};
62862306a36Sopenharmony_ci		rtw89_debug(rtwdev, RTW89_DBG_SER,
62962306a36Sopenharmony_ci			    "next sp: 0x%x, next ra: 0x%x\n",
63062306a36Sopenharmony_ci			    ptr->sp, ptr->ra);
63162306a36Sopenharmony_ci	}
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_ci	rtw89_debug(rtwdev, RTW89_DBG_SER, "dump fw backtrace end\n");
63462306a36Sopenharmony_ci	return 0;
63562306a36Sopenharmony_ci}
63662306a36Sopenharmony_ci
63762306a36Sopenharmony_cistatic void ser_l2_reset_st_pre_hdl(struct rtw89_ser *ser)
63862306a36Sopenharmony_ci{
63962306a36Sopenharmony_ci	struct rtw89_dev *rtwdev = container_of(ser, struct rtw89_dev, ser);
64062306a36Sopenharmony_ci	struct rtw89_ser_cd_buffer *buf;
64162306a36Sopenharmony_ci	struct __fw_backtrace_entry fwbt_ent;
64262306a36Sopenharmony_ci	int ret = 0;
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_ci	buf = rtw89_ser_cd_prep(rtwdev);
64562306a36Sopenharmony_ci	if (!buf) {
64662306a36Sopenharmony_ci		ret = -ENOMEM;
64762306a36Sopenharmony_ci		goto bottom;
64862306a36Sopenharmony_ci	}
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_ci	rtw89_ser_fw_rsvd_ple_dump(rtwdev, buf->fwple.data);
65162306a36Sopenharmony_ci
65262306a36Sopenharmony_ci	fwbt_ent = *(struct __fw_backtrace_entry *)buf->fwple.data;
65362306a36Sopenharmony_ci	ret = rtw89_ser_fw_backtrace_dump(rtwdev, buf->fwbt.data, &fwbt_ent);
65462306a36Sopenharmony_ci	if (ret)
65562306a36Sopenharmony_ci		goto bottom;
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_ci	rtw89_ser_cd_send(rtwdev, buf);
65862306a36Sopenharmony_ci
65962306a36Sopenharmony_cibottom:
66062306a36Sopenharmony_ci	rtw89_ser_cd_free(rtwdev, buf, !!ret);
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci	ser_reset_mac_binding(rtwdev);
66362306a36Sopenharmony_ci	rtw89_core_stop(rtwdev);
66462306a36Sopenharmony_ci	rtw89_entity_init(rtwdev);
66562306a36Sopenharmony_ci	rtw89_fw_release_general_pkt_list(rtwdev, false);
66662306a36Sopenharmony_ci	INIT_LIST_HEAD(&rtwdev->rtwvifs_list);
66762306a36Sopenharmony_ci}
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_cistatic void ser_l2_reset_st_hdl(struct rtw89_ser *ser, u8 evt)
67062306a36Sopenharmony_ci{
67162306a36Sopenharmony_ci	struct rtw89_dev *rtwdev = container_of(ser, struct rtw89_dev, ser);
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_ci	switch (evt) {
67462306a36Sopenharmony_ci	case SER_EV_STATE_IN:
67562306a36Sopenharmony_ci		mutex_lock(&rtwdev->mutex);
67662306a36Sopenharmony_ci		ser_l2_reset_st_pre_hdl(ser);
67762306a36Sopenharmony_ci		mutex_unlock(&rtwdev->mutex);
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ci		ieee80211_restart_hw(rtwdev->hw);
68062306a36Sopenharmony_ci		ser_set_alarm(ser, SER_RECFG_TIMEOUT, SER_EV_L2_RECFG_TIMEOUT);
68162306a36Sopenharmony_ci		break;
68262306a36Sopenharmony_ci
68362306a36Sopenharmony_ci	case SER_EV_L2_RECFG_TIMEOUT:
68462306a36Sopenharmony_ci		rtw89_info(rtwdev, "Err: ser L2 re-config timeout\n");
68562306a36Sopenharmony_ci		fallthrough;
68662306a36Sopenharmony_ci	case SER_EV_L2_RECFG_DONE:
68762306a36Sopenharmony_ci		ser_state_goto(ser, SER_IDLE_ST);
68862306a36Sopenharmony_ci		break;
68962306a36Sopenharmony_ci
69062306a36Sopenharmony_ci	case SER_EV_STATE_OUT:
69162306a36Sopenharmony_ci		ser_del_alarm(ser);
69262306a36Sopenharmony_ci		break;
69362306a36Sopenharmony_ci
69462306a36Sopenharmony_ci	default:
69562306a36Sopenharmony_ci		break;
69662306a36Sopenharmony_ci	}
69762306a36Sopenharmony_ci}
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_cistatic const struct event_ent ser_ev_tbl[] = {
70062306a36Sopenharmony_ci	{SER_EV_NONE, "SER_EV_NONE"},
70162306a36Sopenharmony_ci	{SER_EV_STATE_IN, "SER_EV_STATE_IN"},
70262306a36Sopenharmony_ci	{SER_EV_STATE_OUT, "SER_EV_STATE_OUT"},
70362306a36Sopenharmony_ci	{SER_EV_L1_RESET_PREPARE, "SER_EV_L1_RESET_PREPARE pre-m0"},
70462306a36Sopenharmony_ci	{SER_EV_L1_RESET, "SER_EV_L1_RESET m1"},
70562306a36Sopenharmony_ci	{SER_EV_DO_RECOVERY, "SER_EV_DO_RECOVERY m3"},
70662306a36Sopenharmony_ci	{SER_EV_MAC_RESET_DONE, "SER_EV_MAC_RESET_DONE m5"},
70762306a36Sopenharmony_ci	{SER_EV_L2_RESET, "SER_EV_L2_RESET"},
70862306a36Sopenharmony_ci	{SER_EV_L2_RECFG_DONE, "SER_EV_L2_RECFG_DONE"},
70962306a36Sopenharmony_ci	{SER_EV_L2_RECFG_TIMEOUT, "SER_EV_L2_RECFG_TIMEOUT"},
71062306a36Sopenharmony_ci	{SER_EV_M1_TIMEOUT, "SER_EV_M1_TIMEOUT"},
71162306a36Sopenharmony_ci	{SER_EV_M3_TIMEOUT, "SER_EV_M3_TIMEOUT"},
71262306a36Sopenharmony_ci	{SER_EV_FW_M5_TIMEOUT, "SER_EV_FW_M5_TIMEOUT"},
71362306a36Sopenharmony_ci	{SER_EV_L0_RESET, "SER_EV_L0_RESET"},
71462306a36Sopenharmony_ci	{SER_EV_MAXX, "SER_EV_MAX"}
71562306a36Sopenharmony_ci};
71662306a36Sopenharmony_ci
71762306a36Sopenharmony_cistatic const struct state_ent ser_st_tbl[] = {
71862306a36Sopenharmony_ci	{SER_IDLE_ST, "SER_IDLE_ST", ser_idle_st_hdl},
71962306a36Sopenharmony_ci	{SER_L1_RESET_PRE_ST, "SER_L1_RESET_PRE_ST", ser_l1_reset_pre_st_hdl},
72062306a36Sopenharmony_ci	{SER_RESET_TRX_ST, "SER_RESET_TRX_ST", ser_reset_trx_st_hdl},
72162306a36Sopenharmony_ci	{SER_DO_HCI_ST, "SER_DO_HCI_ST", ser_do_hci_st_hdl},
72262306a36Sopenharmony_ci	{SER_L2_RESET_ST, "SER_L2_RESET_ST", ser_l2_reset_st_hdl}
72362306a36Sopenharmony_ci};
72462306a36Sopenharmony_ci
72562306a36Sopenharmony_ciint rtw89_ser_init(struct rtw89_dev *rtwdev)
72662306a36Sopenharmony_ci{
72762306a36Sopenharmony_ci	struct rtw89_ser *ser = &rtwdev->ser;
72862306a36Sopenharmony_ci
72962306a36Sopenharmony_ci	memset(ser, 0, sizeof(*ser));
73062306a36Sopenharmony_ci	INIT_LIST_HEAD(&ser->msg_q);
73162306a36Sopenharmony_ci	ser->state = SER_IDLE_ST;
73262306a36Sopenharmony_ci	ser->st_tbl = ser_st_tbl;
73362306a36Sopenharmony_ci	ser->ev_tbl = ser_ev_tbl;
73462306a36Sopenharmony_ci
73562306a36Sopenharmony_ci	bitmap_zero(ser->flags, RTW89_NUM_OF_SER_FLAGS);
73662306a36Sopenharmony_ci	spin_lock_init(&ser->msg_q_lock);
73762306a36Sopenharmony_ci	INIT_WORK(&ser->ser_hdl_work, rtw89_ser_hdl_work);
73862306a36Sopenharmony_ci	INIT_DELAYED_WORK(&ser->ser_alarm_work, rtw89_ser_alarm_work);
73962306a36Sopenharmony_ci	return 0;
74062306a36Sopenharmony_ci}
74162306a36Sopenharmony_ci
74262306a36Sopenharmony_ciint rtw89_ser_deinit(struct rtw89_dev *rtwdev)
74362306a36Sopenharmony_ci{
74462306a36Sopenharmony_ci	struct rtw89_ser *ser = (struct rtw89_ser *)&rtwdev->ser;
74562306a36Sopenharmony_ci
74662306a36Sopenharmony_ci	set_bit(RTW89_SER_DRV_STOP_RUN, ser->flags);
74762306a36Sopenharmony_ci	cancel_delayed_work_sync(&ser->ser_alarm_work);
74862306a36Sopenharmony_ci	cancel_work_sync(&ser->ser_hdl_work);
74962306a36Sopenharmony_ci	clear_bit(RTW89_SER_DRV_STOP_RUN, ser->flags);
75062306a36Sopenharmony_ci	return 0;
75162306a36Sopenharmony_ci}
75262306a36Sopenharmony_ci
75362306a36Sopenharmony_civoid rtw89_ser_recfg_done(struct rtw89_dev *rtwdev)
75462306a36Sopenharmony_ci{
75562306a36Sopenharmony_ci	ser_send_msg(&rtwdev->ser, SER_EV_L2_RECFG_DONE);
75662306a36Sopenharmony_ci}
75762306a36Sopenharmony_ci
75862306a36Sopenharmony_ciint rtw89_ser_notify(struct rtw89_dev *rtwdev, u32 err)
75962306a36Sopenharmony_ci{
76062306a36Sopenharmony_ci	u8 event = SER_EV_NONE;
76162306a36Sopenharmony_ci
76262306a36Sopenharmony_ci	rtw89_info(rtwdev, "SER catches error: 0x%x\n", err);
76362306a36Sopenharmony_ci
76462306a36Sopenharmony_ci	switch (err) {
76562306a36Sopenharmony_ci	case MAC_AX_ERR_L1_PREERR_DMAC: /* pre-M0 */
76662306a36Sopenharmony_ci		event = SER_EV_L1_RESET_PREPARE;
76762306a36Sopenharmony_ci		break;
76862306a36Sopenharmony_ci	case MAC_AX_ERR_L1_ERR_DMAC:
76962306a36Sopenharmony_ci	case MAC_AX_ERR_L0_PROMOTE_TO_L1:
77062306a36Sopenharmony_ci		event = SER_EV_L1_RESET; /* M1 */
77162306a36Sopenharmony_ci		break;
77262306a36Sopenharmony_ci	case MAC_AX_ERR_L1_RESET_DISABLE_DMAC_DONE:
77362306a36Sopenharmony_ci		event = SER_EV_DO_RECOVERY; /* M3 */
77462306a36Sopenharmony_ci		break;
77562306a36Sopenharmony_ci	case MAC_AX_ERR_L1_RESET_RECOVERY_DONE:
77662306a36Sopenharmony_ci		event = SER_EV_MAC_RESET_DONE; /* M5 */
77762306a36Sopenharmony_ci		break;
77862306a36Sopenharmony_ci	case MAC_AX_ERR_L0_ERR_CMAC0:
77962306a36Sopenharmony_ci	case MAC_AX_ERR_L0_ERR_CMAC1:
78062306a36Sopenharmony_ci	case MAC_AX_ERR_L0_RESET_DONE:
78162306a36Sopenharmony_ci		event = SER_EV_L0_RESET;
78262306a36Sopenharmony_ci		break;
78362306a36Sopenharmony_ci	default:
78462306a36Sopenharmony_ci		if (err == MAC_AX_ERR_L1_PROMOTE_TO_L2 ||
78562306a36Sopenharmony_ci		    (err >= MAC_AX_ERR_L2_ERR_AH_DMA &&
78662306a36Sopenharmony_ci		     err <= MAC_AX_GET_ERR_MAX))
78762306a36Sopenharmony_ci			event = SER_EV_L2_RESET;
78862306a36Sopenharmony_ci		break;
78962306a36Sopenharmony_ci	}
79062306a36Sopenharmony_ci
79162306a36Sopenharmony_ci	if (event == SER_EV_NONE) {
79262306a36Sopenharmony_ci		rtw89_warn(rtwdev, "SER cannot recognize error: 0x%x\n", err);
79362306a36Sopenharmony_ci		return -EINVAL;
79462306a36Sopenharmony_ci	}
79562306a36Sopenharmony_ci
79662306a36Sopenharmony_ci	ser_send_msg(&rtwdev->ser, event);
79762306a36Sopenharmony_ci	return 0;
79862306a36Sopenharmony_ci}
79962306a36Sopenharmony_ciEXPORT_SYMBOL(rtw89_ser_notify);
800