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