1388 lines
35 KiB
C
1388 lines
35 KiB
C
/******************************************************************************
|
|
*
|
|
* Copyright(c) 2019 - 2020 Realtek Corporation.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms of version 2 of the GNU General Public License as
|
|
* published by the Free Software Foundation.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
* more details.
|
|
*
|
|
*****************************************************************************/
|
|
#include "phl_headers.h"
|
|
|
|
#ifdef CONFIG_FSM
|
|
/*
|
|
* SER stands for System Error Recovery
|
|
*/
|
|
|
|
#define SER_FW_TIMEOUT 1000 /* ms */
|
|
#define SER_POLLING_INTERVAL 10 /* ms */
|
|
#define SER_USB_POLLING_INTERVAL_IDL 1000 /* ms */
|
|
#define SER_USB_POLLING_INTERVAL_ACT 10 /* ms */
|
|
|
|
#define SER_POLL_IO_TIMES 200
|
|
#define SER_USB_POLL_IO_TIMES 300
|
|
#define SER_POLL_BULK_TIMES 100
|
|
|
|
#define SER_L0 0x00000001
|
|
#define SER_L1 0x00000002
|
|
#define SER_L2 0x00000004
|
|
|
|
struct ser_obj {
|
|
struct fsm_main *fsm;
|
|
struct phl_info_t *phl_info;
|
|
struct fsm_obj *fsm_obj;
|
|
|
|
int poll_io_times;
|
|
int poll_bulk_times;
|
|
bool trigger_l2_reset;
|
|
bool trigger_l1_reset;
|
|
bool dynamicThredRunning;
|
|
_os_lock state_lock;
|
|
};
|
|
|
|
enum SER_STATE_ST {
|
|
SER_ST_IDLE,
|
|
SER_ST_L1_PAUSE_TRX,
|
|
SER_ST_L1_DO_HCI,
|
|
SER_ST_L1_RESUME_TRX,
|
|
SER_ST_L2
|
|
};
|
|
|
|
enum SER_EV_ID {
|
|
SER_EV_L1_START,
|
|
//SER_EV_L2_START,
|
|
//SER_EV_M1_PAUSE_TRX,
|
|
SER_EV_M3_DO_RECOVERY,
|
|
SER_EV_M5_READY,
|
|
SER_EV_M9_L2_RESET,
|
|
SER_EV_FW_TIMER_EXPIRE,
|
|
SER_EV_POLL_IO_EXPIRE,
|
|
SER_EV_POLL_BULK_EXPIRE,
|
|
SER_EV_POLL_USB_INT_EXPIRE,
|
|
SER_EV_CHK_SER_EVENT,
|
|
SER_EV_MAX
|
|
};
|
|
|
|
static int ser_idle_st_hdl(void *obj, u16 event, void *param);
|
|
static int ser_usb_idle_st_hdl(void *obj, u16 event, void *param);
|
|
static int ser_pci_l1_pause_trx_st_hdl(void *obj, u16 event, void *param);
|
|
static int ser_usb_l1_pause_trx_st_hdl(void *obj, u16 event, void *param);
|
|
static int ser_sdio_l1_pause_trx_st_hdl(void *obj, u16 event, void *param);
|
|
static int ser_pci_l1_do_hci_st_hdl(void *obj, u16 event, void *param);
|
|
static int ser_usb_l1_do_hci_st_hdl(void *obj, u16 event, void *param);
|
|
static int ser_sdio_l1_do_hci_st_hdl(void *obj, u16 event, void *param);
|
|
static int ser_l1_resume_trx_st_hdl(void *obj, u16 event, void *param);
|
|
static int ser_l2_st_hdl(void *obj, u16 event, void *param);
|
|
|
|
/* PCIE STATE table */
|
|
static struct fsm_state_ent ser_pci_state_tbl[] = {
|
|
ST_ENT(SER_ST_IDLE, ser_idle_st_hdl),
|
|
ST_ENT(SER_ST_L1_PAUSE_TRX, ser_pci_l1_pause_trx_st_hdl),
|
|
ST_ENT(SER_ST_L1_DO_HCI, ser_pci_l1_do_hci_st_hdl),
|
|
ST_ENT(SER_ST_L1_RESUME_TRX, ser_l1_resume_trx_st_hdl),
|
|
ST_ENT(SER_ST_L2, ser_l2_st_hdl)
|
|
};
|
|
|
|
/* USB STATE table */
|
|
static struct fsm_state_ent ser_usb_state_tbl[] = {
|
|
ST_ENT(SER_ST_IDLE, ser_usb_idle_st_hdl),
|
|
ST_ENT(SER_ST_L1_PAUSE_TRX, ser_usb_l1_pause_trx_st_hdl),
|
|
ST_ENT(SER_ST_L1_DO_HCI, ser_usb_l1_do_hci_st_hdl),
|
|
ST_ENT(SER_ST_L1_RESUME_TRX, ser_l1_resume_trx_st_hdl),
|
|
ST_ENT(SER_ST_L2, ser_l2_st_hdl)
|
|
};
|
|
|
|
/* SDIO STATE table */
|
|
static struct fsm_state_ent ser_sdio_state_tbl[] = {
|
|
ST_ENT(SER_ST_IDLE, ser_idle_st_hdl),
|
|
ST_ENT(SER_ST_L1_PAUSE_TRX, ser_sdio_l1_pause_trx_st_hdl),
|
|
ST_ENT(SER_ST_L1_DO_HCI, ser_sdio_l1_do_hci_st_hdl),
|
|
ST_ENT(SER_ST_L1_RESUME_TRX, ser_l1_resume_trx_st_hdl),
|
|
ST_ENT(SER_ST_L2, ser_l2_st_hdl)
|
|
};
|
|
|
|
|
|
/* EVENT table */
|
|
static struct fsm_event_ent ser_event_tbl[] = {
|
|
EV_ENT(SER_EV_L1_START),
|
|
//EV_ENT(SER_EV_L2_START),
|
|
//EV_ENT(SER_EV_M1_PAUSE_TRX),
|
|
EV_ENT(SER_EV_M3_DO_RECOVERY),
|
|
EV_ENT(SER_EV_M5_READY),
|
|
EV_ENT(SER_EV_M9_L2_RESET),
|
|
EV_ENT(SER_EV_FW_TIMER_EXPIRE),
|
|
EV_ENT(SER_EV_POLL_IO_EXPIRE),
|
|
EV_ENT(SER_EV_POLL_BULK_EXPIRE),
|
|
EV_DBG(SER_EV_POLL_USB_INT_EXPIRE),
|
|
EV_ENT(SER_EV_CHK_SER_EVENT),
|
|
EV_ENT(SER_EV_MAX) /* EV_MAX for fsm safety checking */
|
|
};
|
|
|
|
static enum rtw_phl_status _ser_event_notify(void *phl, u8* p_ntfy)
|
|
{
|
|
struct phl_info_t *phl_info = (struct phl_info_t *)phl;
|
|
struct ser_obj *pser = phl_info->ser_obj;
|
|
struct phl_msg msg = {0};
|
|
enum RTW_PHL_SER_NOTIFY_EVENT notify = RTW_PHL_SER_L2_RESET;
|
|
u32 err = 0;
|
|
|
|
notify = rtw_hal_ser_get_error_status(pser->phl_info->hal, &err);
|
|
|
|
if (p_ntfy != NULL)
|
|
*p_ntfy = notify;
|
|
|
|
phl_info->phl_com->phl_stats.ser_event[notify]++;
|
|
|
|
PHL_TRACE(COMP_PHL_DBG, _PHL_INFO_, "_ser_event_notify, error 0x%x, notify 0x%x\n", err, notify);
|
|
|
|
if (notify == RTW_PHL_SER_L0_RESET) {
|
|
PHL_TRACE(COMP_PHL_DBG, _PHL_WARNING_, "_ser_event_notify, hit L0 Reset\n");
|
|
return RTW_PHL_STATUS_SUCCESS;
|
|
}
|
|
|
|
if (notify == RTW_PHL_SER_LOG_ONLY) {
|
|
PHL_TRACE(COMP_PHL_DBG, _PHL_WARNING_, "_ser_event_notify, RTW_PHL_SER_LOG_ONLY\n");
|
|
return RTW_PHL_STATUS_SUCCESS;
|
|
}
|
|
|
|
if (notify == RTW_PHL_SER_DUMP_FW_LOG) {
|
|
PHL_TRACE(COMP_PHL_DBG, _PHL_WARNING_, "_ser_event_notify, RTW_PHL_SER_DUMP_FW_LOG\n");
|
|
|
|
SET_MSG_MDL_ID_FIELD(msg.msg_id, PHL_MDL_PHY_MGNT);
|
|
SET_MSG_EVT_ID_FIELD(msg.msg_id, MSG_EVT_DUMP_PLE_BUFFER);
|
|
phl_msg_hub_send(phl_info, NULL, &msg);
|
|
|
|
return RTW_PHL_STATUS_SUCCESS;
|
|
}
|
|
|
|
return phl_ser_send_msg(phl, notify);
|
|
}
|
|
|
|
|
|
/*
|
|
* SER sub function
|
|
*/
|
|
void ser_pcie_pause_dma_io(struct ser_obj *pser)
|
|
{
|
|
FSM_DBG(pser->fsm, "%s: %s\n",
|
|
phl_fsm_obj_name(pser->fsm_obj), __func__);
|
|
}
|
|
|
|
int ser_polling_io_state(struct ser_obj *pser)
|
|
{
|
|
FSM_DBG(pser->fsm, "%s: %s\n",
|
|
phl_fsm_obj_name(pser->fsm_obj), __func__);
|
|
return 0; /* success */
|
|
}
|
|
|
|
void ser_return_all_tcb_and_mem(struct ser_obj *pser)
|
|
{
|
|
FSM_DBG(pser->fsm, "%s: %s\n",
|
|
phl_fsm_obj_name(pser->fsm_obj), __func__);
|
|
}
|
|
|
|
void ser_send_l0_handle_method_event(struct ser_obj *pser, enum RTW_PHL_SER_RCVY_STEP event)
|
|
{
|
|
FSM_DBG(pser->fsm, "%s: %s with event = 0x%04X\n",
|
|
phl_fsm_obj_name(pser->fsm_obj), __func__, event);
|
|
|
|
/*
|
|
* Default use notify only RTW_PHL_SER_L0_CFG_NOTIFY
|
|
* if need do l0 debug, set RTW_PHL_SER_L0_CFG_HANDSHAKE when init.
|
|
* 1. after receive L0 notify within h2c, dump some Crs for debug.
|
|
* 2. set RTW_PHL_SER_L0_RCVY_EN
|
|
*/
|
|
phl_ser_event_to_fw(pser->phl_info, event);
|
|
}
|
|
|
|
void ser_send_l0_do_rcvy_event(struct ser_obj *pser)
|
|
{
|
|
FSM_DBG(pser->fsm, "%s: %s\n",
|
|
phl_fsm_obj_name(pser->fsm_obj), __func__);
|
|
|
|
/*Default use notify only RTW_PHL_SER_L0_CFG_NOTIFY*/
|
|
phl_ser_event_to_fw(pser->phl_info, RTW_PHL_SER_L0_RCVY_EN);
|
|
}
|
|
|
|
|
|
void ser_send_m2_event(struct ser_obj *pser)
|
|
{
|
|
FSM_DBG(pser->fsm, "%s: %s\n",
|
|
phl_fsm_obj_name(pser->fsm_obj), __func__);
|
|
|
|
phl_ser_event_to_fw(pser->phl_info, RTW_PHL_SER_L1_DISABLE_EN);
|
|
}
|
|
|
|
void ser_send_m4_event(struct ser_obj *pser)
|
|
{
|
|
FSM_DBG(pser->fsm, "%s: %s\n",
|
|
phl_fsm_obj_name(pser->fsm_obj), __func__);
|
|
phl_ser_event_to_fw(pser->phl_info, RTW_PHL_SER_L1_RCVY_EN);
|
|
}
|
|
|
|
void ser_usb_trx_disable_cr(struct ser_obj *pser)
|
|
{
|
|
FSM_DBG(pser->fsm, "%s: %s\n",
|
|
phl_fsm_obj_name(pser->fsm_obj), __func__);
|
|
}
|
|
|
|
int ser_usb_wait_all_bulk_complete(struct ser_obj *pser)
|
|
{
|
|
FSM_DBG(pser->fsm, "%s: %s\n",
|
|
phl_fsm_obj_name(pser->fsm_obj), __func__);
|
|
return 0; /* success */
|
|
}
|
|
|
|
void ser_pci_clear_rw_pointer(struct ser_obj *pser)
|
|
{
|
|
FSM_DBG(pser->fsm, "%s: %s\n",
|
|
phl_fsm_obj_name(pser->fsm_obj), __func__);
|
|
}
|
|
|
|
void ser_pci_assert_dessert_dma_reset(struct ser_obj *pser)
|
|
{
|
|
FSM_DBG(pser->fsm, "%s: %s\n",
|
|
phl_fsm_obj_name(pser->fsm_obj), __func__);
|
|
}
|
|
|
|
void ser_pci_reset_bdram(struct ser_obj *pser)
|
|
{
|
|
FSM_DBG(pser->fsm, "%s: %s\n",
|
|
phl_fsm_obj_name(pser->fsm_obj), __func__);
|
|
}
|
|
|
|
void ser_pci_enable_dma_io(struct ser_obj *pser)
|
|
{
|
|
FSM_DBG(pser->fsm, "%s: %s\n",
|
|
phl_fsm_obj_name(pser->fsm_obj), __func__);
|
|
}
|
|
|
|
void ser_usb_enable_bulk_IO(struct ser_obj *pser)
|
|
{
|
|
FSM_DBG(pser->fsm, "%s: %s\n",
|
|
phl_fsm_obj_name(pser->fsm_obj), __func__);
|
|
}
|
|
void ser_resume_trx_process(struct ser_obj *pser, u8 type)
|
|
{
|
|
struct phl_hci_trx_ops *ops = pser->phl_info->hci_trx_ops;
|
|
|
|
FSM_DBG(pser->fsm, "%s: %s\n",
|
|
phl_fsm_obj_name(pser->fsm_obj), __func__);
|
|
|
|
ops->trx_resume(pser->phl_info, type);
|
|
}
|
|
|
|
#ifdef RTW_WKARD_SER_USB_POLLING_EVENT
|
|
extern bool rtw_phl_recognize_interrupt(void *phl);
|
|
extern enum rtw_phl_status rtw_phl_interrupt_handler(void *phl);
|
|
static void ser_usb_chk_int_event(struct ser_obj *pser)
|
|
{
|
|
if (true == rtw_phl_recognize_interrupt(pser->phl_info))
|
|
rtw_phl_interrupt_handler(pser->phl_info);
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* SER state handler
|
|
*/
|
|
u8 rtw_phl_ser_inprogress(void *phl)
|
|
{
|
|
return phl_ser_inprogress(phl);
|
|
}
|
|
|
|
void rtw_phl_ser_clear_status(void *phl, u32 serstatus)
|
|
{
|
|
phl_ser_clear_status(phl, serstatus);
|
|
}
|
|
|
|
/*
|
|
* ser idle handler
|
|
*/
|
|
static int ser_idle_st_hdl(void *obj, u16 event, void *param)
|
|
{
|
|
struct ser_obj *pser = (struct ser_obj *)obj;
|
|
u8 notify;
|
|
|
|
switch (event) {
|
|
case FSM_EV_STATE_IN:
|
|
break;
|
|
|
|
case SER_EV_CHK_SER_EVENT:
|
|
if (true == rtw_hal_recognize_halt_c2h_interrupt(pser->phl_info->hal)) {
|
|
_ser_event_notify(pser->phl_info, ¬ify);
|
|
if ((notify == RTW_PHL_SER_L0_RESET) || (notify == RTW_PHL_SER_L2_RESET))
|
|
rtw_hal_config_interrupt(pser->phl_info->hal, RTW_PHL_EN_HCI_INT);
|
|
}
|
|
else {
|
|
rtw_hal_config_interrupt(pser->phl_info->hal, RTW_PHL_EN_HCI_INT);
|
|
}
|
|
|
|
break;
|
|
|
|
case SER_EV_L1_START:
|
|
phl_fsm_state_goto(pser->fsm_obj, SER_ST_L1_PAUSE_TRX);
|
|
break;
|
|
#if 0
|
|
case SER_EV_L2_START:
|
|
phl_fsm_state_goto(pser->fsm_obj, SER_ST_L2);
|
|
break;
|
|
#endif
|
|
|
|
case SER_EV_M9_L2_RESET:
|
|
phl_fsm_state_goto(pser->fsm_obj, SER_ST_L2);
|
|
break;
|
|
|
|
case FSM_EV_STATE_OUT:
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int ser_usb_idle_st_hdl(void *obj, u16 event, void *param)
|
|
{
|
|
struct ser_obj *pser = (struct ser_obj *)obj;
|
|
|
|
switch (event) {
|
|
case FSM_EV_SWITCH_IN:
|
|
#if 1
|
|
/* Disable L0 Reset Notify from FW to driver */
|
|
ser_send_l0_handle_method_event(pser, RTW_PHL_SER_L0_CFG_DIS_NOTIFY);
|
|
#endif
|
|
/* fallthrough */
|
|
case FSM_EV_STATE_IN:
|
|
|
|
#if defined(RTW_WKARD_SER_USB_POLLING_EVENT) && !defined(CONFIG_PHL_CMD_SER)
|
|
phl_fsm_set_alarm_ext(pser->fsm_obj,
|
|
SER_USB_POLLING_INTERVAL_IDL, SER_EV_POLL_USB_INT_EXPIRE, 1, NULL);
|
|
#endif
|
|
|
|
break;
|
|
|
|
#ifdef RTW_WKARD_SER_USB_POLLING_EVENT
|
|
case SER_EV_POLL_USB_INT_EXPIRE:
|
|
ser_usb_chk_int_event(pser);
|
|
|
|
phl_fsm_set_alarm_ext(pser->fsm_obj,
|
|
SER_USB_POLLING_INTERVAL_IDL, SER_EV_POLL_USB_INT_EXPIRE, 1, NULL);
|
|
break;
|
|
#endif
|
|
|
|
case SER_EV_L1_START:
|
|
#ifndef RTW_WKARD_SER_USB_DISABLE_L1_RCVY_FLOW
|
|
phl_fsm_state_goto(pser->fsm_obj, SER_ST_L1_PAUSE_TRX);
|
|
#endif
|
|
break;
|
|
|
|
#if 0
|
|
case SER_EV_L2_START:
|
|
phl_fsm_state_goto(pser->fsm_obj, SER_ST_L2);
|
|
break;
|
|
#endif
|
|
|
|
case SER_EV_M9_L2_RESET:
|
|
phl_fsm_state_goto(pser->fsm_obj, SER_ST_L2);
|
|
break;
|
|
|
|
case FSM_EV_STATE_OUT:
|
|
phl_fsm_cancel_alarm_ext(pser->fsm_obj, 1);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int ser_pci_l1_pause_trx_st_hdl(void *obj, u16 event, void *param)
|
|
{
|
|
struct ser_obj *pser = (struct ser_obj *)obj;
|
|
struct phl_hci_trx_ops *ops = pser->phl_info->hci_trx_ops;
|
|
struct phl_msg msg = {0};
|
|
enum rtw_hal_status status = RTW_HAL_STATUS_FAILURE;
|
|
|
|
SET_MSG_MDL_ID_FIELD(msg.msg_id, PHL_MDL_SER);
|
|
SET_MSG_EVT_ID_FIELD(msg.msg_id, MSG_EVT_SER_L1);
|
|
|
|
switch (event) {
|
|
case FSM_EV_STATE_IN:
|
|
|
|
phl_ser_set_status(pser, SER_L1);
|
|
|
|
//send msg to core
|
|
phl_msg_hub_send(pser->phl_info, NULL, &msg);
|
|
|
|
ops->req_tx_stop(pser->phl_info);
|
|
rtw_phl_tx_req_notify(pser->phl_info);
|
|
|
|
if(rtw_hal_lv1_rcvy(pser->phl_info->hal, RTW_PHL_SER_LV1_RCVY_STEP_1)
|
|
!= RTW_HAL_STATUS_SUCCESS){
|
|
ser_resume_trx_process(pser, PHL_CTRL_TX);
|
|
rtw_phl_tx_req_notify(pser->phl_info);
|
|
phl_fsm_state_goto(pser->fsm_obj, SER_ST_L2);
|
|
break;
|
|
}
|
|
|
|
ops->req_rx_stop(pser->phl_info);
|
|
rtw_phl_start_rx_process(pser->phl_info);
|
|
|
|
if (false == ops->is_tx_pause(pser->phl_info) ||
|
|
false == ops->is_rx_pause(pser->phl_info)) {
|
|
/* pci: polling fail; wait for a while */
|
|
phl_fsm_set_alarm(pser->fsm_obj,
|
|
SER_POLLING_INTERVAL, SER_EV_POLL_IO_EXPIRE);
|
|
|
|
/* prevent infinite polling */
|
|
pser->poll_io_times = SER_POLL_IO_TIMES;
|
|
break;
|
|
}
|
|
|
|
ops->trx_reset(pser->phl_info, PHL_CTRL_TX|PHL_CTRL_RX);
|
|
|
|
/* pci: send M2 event */
|
|
ser_send_m2_event(pser);
|
|
|
|
/* pci: wait M3 */
|
|
phl_fsm_set_alarm(pser->fsm_obj, SER_FW_TIMEOUT,
|
|
SER_EV_FW_TIMER_EXPIRE);
|
|
break;
|
|
|
|
case SER_EV_M3_DO_RECOVERY:
|
|
phl_fsm_state_goto(pser->fsm_obj, SER_ST_L1_DO_HCI);
|
|
break;
|
|
|
|
case SER_EV_FW_TIMER_EXPIRE:
|
|
/*phl_fsm_state_goto(pser->fsm_obj, SER_ST_L2);*/
|
|
status = rtw_hal_lv1_rcvy(pser->phl_info->hal, RTW_PHL_SER_LV1_SER_RCVY_STEP_2);
|
|
|
|
ser_resume_trx_process(pser, PHL_CTRL_RX);
|
|
rtw_phl_start_rx_process(pser->phl_info);
|
|
ser_resume_trx_process(pser, PHL_CTRL_TX);
|
|
rtw_phl_tx_req_notify(pser->phl_info);
|
|
|
|
if (status != RTW_HAL_STATUS_SUCCESS) {
|
|
phl_fsm_state_goto(pser->fsm_obj, SER_ST_L2);
|
|
} else {
|
|
phl_ser_clear_status(pser, SER_L1);
|
|
phl_fsm_state_goto(pser->fsm_obj, SER_ST_IDLE);
|
|
}
|
|
|
|
break;
|
|
|
|
case SER_EV_M9_L2_RESET:
|
|
|
|
ser_resume_trx_process(pser, PHL_CTRL_RX);
|
|
rtw_phl_start_rx_process(pser->phl_info);
|
|
ser_resume_trx_process(pser, PHL_CTRL_TX);
|
|
rtw_phl_tx_req_notify(pser->phl_info);
|
|
|
|
phl_fsm_state_goto(pser->fsm_obj, SER_ST_L2);
|
|
|
|
break;
|
|
|
|
case SER_EV_POLL_IO_EXPIRE:
|
|
|
|
if (pser->poll_io_times-- <= 0) {
|
|
#ifdef RTW_WKARD_SER_L1_EXPIRE
|
|
|
|
ops->trx_reset(pser->phl_info, PHL_CTRL_TX|PHL_CTRL_RX);
|
|
|
|
/* pci: send M2 event */
|
|
ser_send_m2_event(pser);
|
|
|
|
/* pci: wait M3 */
|
|
phl_fsm_set_alarm(pser->fsm_obj, SER_FW_TIMEOUT,
|
|
SER_EV_FW_TIMER_EXPIRE);
|
|
|
|
#else
|
|
ser_resume_trx_process(pser, PHL_CTRL_RX);
|
|
rtw_phl_start_rx_process(pser->phl_info);
|
|
ser_resume_trx_process(pser, PHL_CTRL_TX);
|
|
rtw_phl_tx_req_notify(pser->phl_info);
|
|
phl_fsm_state_goto(pser->fsm_obj, SER_ST_L2);
|
|
#endif
|
|
break;
|
|
}
|
|
|
|
/*send msg to core*/
|
|
/*phl_msg_hub_send(pser->phl_info, &msg);*/
|
|
|
|
if (false == ops->is_tx_pause(pser->phl_info) ||
|
|
false == ops->is_rx_pause(pser->phl_info) ) {
|
|
/* pci: polling fail; wait for a while */
|
|
phl_fsm_set_alarm(pser->fsm_obj,
|
|
SER_POLLING_INTERVAL, SER_EV_POLL_IO_EXPIRE);
|
|
break;
|
|
}
|
|
/*
|
|
if(rtw_hal_lv1_rcvy(hal_info, RTW_PHL_SER_LV1_RCVY_STEP_1) != RTW_HAL_STATUS_SUCCESS){
|
|
phl_fsm_state_goto(pser->fsm_obj, SER_ST_L2);
|
|
break;
|
|
}
|
|
*/
|
|
ops->trx_reset(pser->phl_info, PHL_CTRL_TX|PHL_CTRL_RX);
|
|
|
|
/* pci: send M2 event */
|
|
ser_send_m2_event(pser);
|
|
|
|
/* pci: wait M3 */
|
|
phl_fsm_set_alarm(pser->fsm_obj, SER_FW_TIMEOUT,
|
|
SER_EV_FW_TIMER_EXPIRE);
|
|
break;
|
|
|
|
case FSM_EV_CANCEL:
|
|
phl_fsm_state_goto(pser->fsm_obj, SER_ST_IDLE);
|
|
break;
|
|
|
|
case FSM_EV_STATE_OUT:
|
|
phl_fsm_cancel_alarm(pser->fsm_obj);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void ser_usb_l1_pause_trx_done(struct ser_obj *pser)
|
|
{
|
|
rtw_hal_lv1_rcvy(pser->phl_info->hal, RTW_PHL_SER_LV1_RCVY_STEP_1);
|
|
|
|
ser_send_m2_event(pser);
|
|
}
|
|
|
|
static int ser_usb_l1_pause_trx_st_hdl(void *obj, u16 event, void *param)
|
|
{
|
|
struct ser_obj *pser = (struct ser_obj *)obj;
|
|
struct phl_hci_trx_ops *ops = pser->phl_info->hci_trx_ops;
|
|
struct phl_msg msg = {0};
|
|
enum rtw_hal_status status = RTW_HAL_STATUS_FAILURE;
|
|
|
|
switch (event) {
|
|
case FSM_EV_STATE_IN:
|
|
phl_ser_set_status(pser, SER_L1);
|
|
|
|
SET_MSG_MDL_ID_FIELD(msg.msg_id, PHL_MDL_SER);
|
|
SET_MSG_EVT_ID_FIELD(msg.msg_id, MSG_EVT_SER_L1);
|
|
phl_msg_hub_send(pser->phl_info, NULL, &msg);
|
|
|
|
ops->req_tx_stop(pser->phl_info);
|
|
rtw_phl_tx_req_notify(pser->phl_info);
|
|
|
|
#if 0
|
|
/* not request pause PHL Rx in USB data path */
|
|
ops->req_rx_stop(pser->phl_info);
|
|
rtw_phl_start_rx_process(pser->phl_info);
|
|
#endif
|
|
|
|
if (false == ops->is_tx_pause(pser->phl_info)
|
|
#if 0
|
|
|| false == ops->is_rx_pause(pser->phl_info)
|
|
#endif
|
|
) {
|
|
phl_fsm_set_alarm(pser->fsm_obj,
|
|
SER_USB_POLLING_INTERVAL_ACT, SER_EV_POLL_IO_EXPIRE);
|
|
|
|
pser->poll_io_times = SER_USB_POLL_IO_TIMES;
|
|
break;
|
|
}
|
|
|
|
ser_usb_l1_pause_trx_done(pser);
|
|
|
|
#ifdef RTW_WKARD_SER_USB_POLLING_EVENT
|
|
phl_fsm_set_alarm_ext(pser->fsm_obj,
|
|
SER_USB_POLLING_INTERVAL_ACT, SER_EV_POLL_USB_INT_EXPIRE, 1, NULL);
|
|
#endif
|
|
|
|
phl_fsm_set_alarm(pser->fsm_obj, SER_FW_TIMEOUT,
|
|
SER_EV_FW_TIMER_EXPIRE);
|
|
|
|
break;
|
|
|
|
#ifdef RTW_WKARD_SER_USB_POLLING_EVENT
|
|
case SER_EV_POLL_USB_INT_EXPIRE:
|
|
ser_usb_chk_int_event(pser);
|
|
|
|
phl_fsm_set_alarm_ext(pser->fsm_obj,
|
|
SER_USB_POLLING_INTERVAL_ACT, SER_EV_POLL_USB_INT_EXPIRE, 1, NULL);
|
|
break;
|
|
#endif
|
|
|
|
case SER_EV_M3_DO_RECOVERY:
|
|
phl_fsm_state_goto(pser->fsm_obj, SER_ST_L1_DO_HCI);
|
|
break;
|
|
|
|
case SER_EV_FW_TIMER_EXPIRE:
|
|
status = rtw_hal_lv1_rcvy(pser->phl_info->hal, RTW_PHL_SER_LV1_SER_RCVY_STEP_2);
|
|
ser_resume_trx_process(pser, PHL_CTRL_TX);
|
|
rtw_phl_tx_req_notify(pser->phl_info);
|
|
|
|
if (status != RTW_HAL_STATUS_SUCCESS)
|
|
phl_fsm_state_goto(pser->fsm_obj, SER_ST_L2);
|
|
else
|
|
phl_fsm_state_goto(pser->fsm_obj, SER_ST_IDLE);
|
|
|
|
break;
|
|
|
|
case SER_EV_M9_L2_RESET:
|
|
ser_resume_trx_process(pser, PHL_CTRL_TX);
|
|
rtw_phl_tx_req_notify(pser->phl_info);
|
|
phl_fsm_state_goto(pser->fsm_obj, SER_ST_L2);
|
|
break;
|
|
|
|
case SER_EV_POLL_IO_EXPIRE:
|
|
|
|
if (pser->poll_io_times-- <= 0) {
|
|
#ifdef RTW_WKARD_SER_L1_EXPIRE
|
|
ser_usb_l1_pause_trx_done(pser);
|
|
|
|
phl_fsm_set_alarm_ext(pser->fsm_obj,
|
|
SER_USB_POLLING_INTERVAL_ACT, SER_EV_POLL_USB_INT_EXPIRE, 1, NULL);
|
|
|
|
phl_fsm_set_alarm(pser->fsm_obj, SER_FW_TIMEOUT,
|
|
SER_EV_FW_TIMER_EXPIRE);
|
|
#else
|
|
phl_fsm_state_goto(pser->fsm_obj, SER_ST_L2);
|
|
#endif
|
|
break;
|
|
}
|
|
|
|
if (false == ops->is_tx_pause(pser->phl_info)
|
|
#if 0
|
|
|| false == ops->is_rx_pause(pser->phl_info)
|
|
#endif
|
|
) {
|
|
phl_fsm_set_alarm(pser->fsm_obj,
|
|
SER_USB_POLLING_INTERVAL_ACT, SER_EV_POLL_IO_EXPIRE);
|
|
break;
|
|
}
|
|
|
|
ser_usb_l1_pause_trx_done(pser);
|
|
|
|
phl_fsm_set_alarm_ext(pser->fsm_obj,
|
|
SER_USB_POLLING_INTERVAL_ACT, SER_EV_POLL_USB_INT_EXPIRE, 1, NULL);
|
|
|
|
phl_fsm_set_alarm(pser->fsm_obj, SER_FW_TIMEOUT,
|
|
SER_EV_FW_TIMER_EXPIRE);
|
|
|
|
break;
|
|
|
|
case FSM_EV_CANCEL:
|
|
phl_fsm_state_goto(pser->fsm_obj, SER_ST_IDLE);
|
|
break;
|
|
|
|
case FSM_EV_STATE_OUT:
|
|
phl_fsm_cancel_alarm(pser->fsm_obj);
|
|
phl_fsm_cancel_alarm_ext(pser->fsm_obj, 1);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int ser_sdio_l1_pause_trx_st_hdl(void *obj, u16 event, void *param)
|
|
{
|
|
struct ser_obj *pser = (struct ser_obj *)obj;
|
|
struct phl_hci_trx_ops *ops = pser->phl_info->hci_trx_ops;
|
|
struct phl_msg msg = {0};
|
|
enum rtw_hal_status status = RTW_HAL_STATUS_FAILURE;
|
|
|
|
switch (event) {
|
|
case FSM_EV_STATE_IN:
|
|
phl_ser_set_status(pser, BIT1);
|
|
|
|
SET_MSG_MDL_ID_FIELD(msg.msg_id, PHL_MDL_SER);
|
|
SET_MSG_EVT_ID_FIELD(msg.msg_id, MSG_EVT_SER_L1);
|
|
phl_msg_hub_send(pser->phl_info, NULL, &msg);
|
|
|
|
ops->req_tx_stop(pser->phl_info);
|
|
rtw_phl_tx_req_notify(pser->phl_info);
|
|
|
|
ops->req_rx_stop(pser->phl_info);
|
|
rtw_phl_start_rx_process(pser->phl_info);
|
|
|
|
if (false == ops->is_tx_pause(pser->phl_info)
|
|
|| false == ops->is_rx_pause(pser->phl_info)) {
|
|
phl_fsm_set_alarm(pser->fsm_obj,
|
|
SER_POLLING_INTERVAL, SER_EV_POLL_IO_EXPIRE);
|
|
|
|
pser->poll_io_times = SER_POLL_IO_TIMES;
|
|
break;
|
|
}
|
|
|
|
rtw_hal_config_interrupt(pser->phl_info->hal, RTW_PHL_DIS_HCI_INT);
|
|
|
|
if (rtw_hal_lv1_rcvy(pser->phl_info->hal, RTW_PHL_SER_LV1_RCVY_STEP_1)
|
|
!= RTW_HAL_STATUS_SUCCESS) {
|
|
rtw_hal_config_interrupt(pser->phl_info->hal, RTW_PHL_EN_HCI_INT);
|
|
phl_fsm_state_goto(pser->fsm_obj, SER_ST_L2);
|
|
break;
|
|
}
|
|
|
|
rtw_hal_config_interrupt(pser->phl_info->hal, RTW_PHL_SER_HANDSHAKE_MODE);
|
|
|
|
ser_send_m2_event(pser);
|
|
|
|
phl_fsm_set_alarm(pser->fsm_obj, SER_FW_TIMEOUT,
|
|
SER_EV_FW_TIMER_EXPIRE);
|
|
|
|
break;
|
|
|
|
case SER_EV_CHK_SER_EVENT:
|
|
if (true == rtw_hal_recognize_halt_c2h_interrupt(pser->phl_info->hal))
|
|
_ser_event_notify(pser->phl_info, NULL);
|
|
|
|
break;
|
|
|
|
case SER_EV_M3_DO_RECOVERY:
|
|
rtw_hal_clear_interrupt(pser->phl_info->hal);
|
|
phl_fsm_state_goto(pser->fsm_obj, SER_ST_L1_DO_HCI);
|
|
break;
|
|
|
|
case SER_EV_FW_TIMER_EXPIRE:
|
|
status = rtw_hal_lv1_rcvy(pser->phl_info->hal, RTW_PHL_SER_LV1_SER_RCVY_STEP_2);
|
|
|
|
ser_resume_trx_process(pser, PHL_CTRL_RX);
|
|
rtw_phl_start_rx_process(pser->phl_info);
|
|
ser_resume_trx_process(pser, PHL_CTRL_TX);
|
|
rtw_phl_tx_req_notify(pser->phl_info);
|
|
|
|
rtw_hal_config_interrupt(pser->phl_info->hal, RTW_PHL_EN_HCI_INT);
|
|
|
|
if (status != RTW_HAL_STATUS_SUCCESS) {
|
|
phl_fsm_state_goto(pser->fsm_obj, SER_ST_L2);
|
|
} else {
|
|
phl_ser_clear_status(pser, BIT1);
|
|
phl_fsm_state_goto(pser->fsm_obj, SER_ST_IDLE);
|
|
}
|
|
|
|
break;
|
|
|
|
case SER_EV_M9_L2_RESET:
|
|
ser_resume_trx_process(pser, PHL_CTRL_RX);
|
|
rtw_phl_start_rx_process(pser->phl_info);
|
|
ser_resume_trx_process(pser, PHL_CTRL_TX);
|
|
rtw_phl_tx_req_notify(pser->phl_info);
|
|
|
|
rtw_hal_config_interrupt(pser->phl_info->hal, RTW_PHL_EN_HCI_INT);
|
|
|
|
phl_fsm_state_goto(pser->fsm_obj, SER_ST_L2);
|
|
break;
|
|
|
|
case SER_EV_POLL_IO_EXPIRE:
|
|
|
|
if (pser->poll_io_times-- <= 0) {
|
|
#ifdef RTW_WKARD_SER_L1_EXPIRE
|
|
|
|
if (rtw_hal_lv1_rcvy(pser->phl_info->hal, RTW_PHL_SER_LV1_RCVY_STEP_1)
|
|
!= RTW_HAL_STATUS_SUCCESS) {
|
|
rtw_hal_config_interrupt(pser->phl_info->hal, RTW_PHL_EN_HCI_INT);
|
|
phl_fsm_state_goto(pser->fsm_obj, SER_ST_L2);
|
|
break;
|
|
}
|
|
|
|
rtw_hal_config_interrupt(pser->phl_info->hal, RTW_PHL_SER_HANDSHAKE_MODE);
|
|
|
|
ser_send_m2_event(pser);
|
|
|
|
phl_fsm_set_alarm(pser->fsm_obj, SER_FW_TIMEOUT,
|
|
SER_EV_FW_TIMER_EXPIRE);
|
|
|
|
#else
|
|
ser_resume_trx_process(pser, PHL_CTRL_RX);
|
|
rtw_phl_start_rx_process(pser->phl_info);
|
|
ser_resume_trx_process(pser, PHL_CTRL_TX);
|
|
rtw_phl_tx_req_notify(pser->phl_info);
|
|
|
|
rtw_hal_config_interrupt(pser->phl_info->hal, RTW_PHL_EN_HCI_INT);
|
|
|
|
phl_fsm_state_goto(pser->fsm_obj, SER_ST_L2);
|
|
#endif
|
|
break;
|
|
}
|
|
|
|
if (false == ops->is_tx_pause(pser->phl_info) ||
|
|
false == ops->is_rx_pause(pser->phl_info)) {
|
|
phl_fsm_set_alarm(pser->fsm_obj,
|
|
SER_POLLING_INTERVAL, SER_EV_POLL_IO_EXPIRE);
|
|
break;
|
|
}
|
|
|
|
rtw_hal_config_interrupt(pser->phl_info->hal, RTW_PHL_DIS_HCI_INT);
|
|
|
|
if (rtw_hal_lv1_rcvy(pser->phl_info->hal, RTW_PHL_SER_LV1_RCVY_STEP_1)
|
|
!= RTW_HAL_STATUS_SUCCESS) {
|
|
rtw_hal_config_interrupt(pser->phl_info->hal, RTW_PHL_EN_HCI_INT);
|
|
phl_fsm_state_goto(pser->fsm_obj, SER_ST_L2);
|
|
break;
|
|
}
|
|
|
|
rtw_hal_config_interrupt(pser->phl_info->hal, RTW_PHL_SER_HANDSHAKE_MODE);
|
|
|
|
ser_send_m2_event(pser);
|
|
|
|
phl_fsm_set_alarm(pser->fsm_obj, SER_FW_TIMEOUT,
|
|
SER_EV_FW_TIMER_EXPIRE);
|
|
break;
|
|
|
|
case FSM_EV_CANCEL:
|
|
rtw_hal_config_interrupt(pser->phl_info->hal, RTW_PHL_EN_HCI_INT);
|
|
phl_fsm_state_goto(pser->fsm_obj, SER_ST_IDLE);
|
|
break;
|
|
|
|
case FSM_EV_STATE_OUT:
|
|
phl_fsm_cancel_alarm(pser->fsm_obj);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int ser_pci_l1_do_hci_st_hdl(void *obj, u16 event, void *param)
|
|
{
|
|
struct ser_obj *pser = (struct ser_obj *)obj;
|
|
|
|
switch (event) {
|
|
case FSM_EV_STATE_IN:
|
|
|
|
if(rtw_hal_lv1_rcvy(pser->phl_info->hal, RTW_PHL_SER_LV1_SER_RCVY_STEP_2)
|
|
!= RTW_HAL_STATUS_SUCCESS){
|
|
ser_resume_trx_process(pser, PHL_CTRL_RX);
|
|
rtw_phl_start_rx_process(pser->phl_info);
|
|
ser_resume_trx_process(pser, PHL_CTRL_TX);
|
|
rtw_phl_tx_req_notify(pser->phl_info);
|
|
phl_fsm_state_goto(pser->fsm_obj, SER_ST_L2);
|
|
break;
|
|
}
|
|
ser_resume_trx_process(pser, PHL_CTRL_RX);
|
|
rtw_phl_start_rx_process(pser->phl_info);
|
|
|
|
/* pci: send M4 event */
|
|
ser_send_m4_event(pser);
|
|
|
|
/* pci: set timeout to wait M5 */
|
|
phl_fsm_set_alarm(pser->fsm_obj, SER_FW_TIMEOUT,
|
|
SER_EV_FW_TIMER_EXPIRE);
|
|
break;
|
|
|
|
case SER_EV_M5_READY:
|
|
phl_fsm_state_goto(pser->fsm_obj, SER_ST_L1_RESUME_TRX);
|
|
break;
|
|
|
|
|
|
case SER_EV_FW_TIMER_EXPIRE:
|
|
/* PHL_INFO("ser_pci_l1_do_hci_st_hdl(): SER_EV_FW_TIMER_EXPIRE \n"); */
|
|
/* _ser_event_notify(pser->phl_info, NULL); */
|
|
/* phl_fsm_state_goto(pser->fsm_obj, SER_ST_L2); */
|
|
ser_resume_trx_process(pser, PHL_CTRL_TX);
|
|
rtw_phl_tx_req_notify(pser->phl_info);
|
|
phl_ser_clear_status(pser, SER_L1);
|
|
phl_fsm_state_goto(pser->fsm_obj, SER_ST_IDLE);
|
|
break;
|
|
|
|
case SER_EV_M9_L2_RESET:
|
|
|
|
ser_resume_trx_process(pser, PHL_CTRL_TX);
|
|
rtw_phl_tx_req_notify(pser->phl_info);
|
|
phl_fsm_state_goto(pser->fsm_obj, SER_ST_L2);
|
|
|
|
break;
|
|
|
|
case FSM_EV_CANCEL:
|
|
phl_fsm_state_goto(pser->fsm_obj, SER_ST_IDLE);
|
|
break;
|
|
|
|
case FSM_EV_STATE_OUT:
|
|
phl_fsm_cancel_alarm(pser->fsm_obj);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int ser_usb_l1_do_hci_st_hdl(void *obj, u16 event, void *param)
|
|
{
|
|
struct ser_obj *pser = (struct ser_obj *)obj;
|
|
|
|
switch (event) {
|
|
case FSM_EV_STATE_IN:
|
|
if (rtw_hal_lv1_rcvy(pser->phl_info->hal, RTW_PHL_SER_LV1_SER_RCVY_STEP_2)
|
|
!= RTW_HAL_STATUS_SUCCESS) {
|
|
phl_fsm_state_goto(pser->fsm_obj, SER_ST_L2);
|
|
break;
|
|
}
|
|
ser_resume_trx_process(pser, PHL_CTRL_RX);
|
|
rtw_phl_start_rx_process(pser->phl_info);
|
|
|
|
ser_send_m4_event(pser);
|
|
|
|
#ifdef RTW_WKARD_SER_USB_POLLING_EVENT
|
|
phl_fsm_set_alarm_ext(pser->fsm_obj,
|
|
SER_USB_POLLING_INTERVAL_ACT, SER_EV_POLL_USB_INT_EXPIRE, 1, NULL);
|
|
#endif
|
|
|
|
phl_fsm_set_alarm(pser->fsm_obj, SER_FW_TIMEOUT,
|
|
SER_EV_FW_TIMER_EXPIRE);
|
|
break;
|
|
|
|
#ifdef RTW_WKARD_SER_USB_POLLING_EVENT
|
|
case SER_EV_POLL_USB_INT_EXPIRE:
|
|
ser_usb_chk_int_event(pser);
|
|
|
|
phl_fsm_set_alarm_ext(pser->fsm_obj,
|
|
SER_USB_POLLING_INTERVAL_ACT, SER_EV_POLL_USB_INT_EXPIRE, 1, NULL);
|
|
break;
|
|
#endif
|
|
|
|
case SER_EV_M5_READY:
|
|
case SER_EV_FW_TIMER_EXPIRE:
|
|
phl_fsm_state_goto(pser->fsm_obj, SER_ST_L1_RESUME_TRX);
|
|
break;
|
|
|
|
case SER_EV_M9_L2_RESET:
|
|
phl_fsm_state_goto(pser->fsm_obj, SER_ST_L2);
|
|
break;
|
|
|
|
case FSM_EV_CANCEL:
|
|
phl_fsm_state_goto(pser->fsm_obj, SER_ST_IDLE);
|
|
break;
|
|
|
|
case FSM_EV_STATE_OUT:
|
|
phl_fsm_cancel_alarm(pser->fsm_obj);
|
|
phl_fsm_cancel_alarm_ext(pser->fsm_obj, 1);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int ser_sdio_l1_do_hci_st_hdl(void *obj, u16 event, void *param)
|
|
{
|
|
struct ser_obj *pser = (struct ser_obj *)obj;
|
|
|
|
switch (event) {
|
|
case FSM_EV_STATE_IN:
|
|
|
|
if (rtw_hal_lv1_rcvy(pser->phl_info->hal, RTW_PHL_SER_LV1_SER_RCVY_STEP_2)
|
|
!= RTW_HAL_STATUS_SUCCESS) {
|
|
rtw_hal_config_interrupt(pser->phl_info->hal, RTW_PHL_EN_HCI_INT);
|
|
phl_fsm_state_goto(pser->fsm_obj, SER_ST_L2);
|
|
break;
|
|
}
|
|
ser_resume_trx_process(pser, PHL_CTRL_RX);
|
|
rtw_phl_start_rx_process(pser->phl_info);
|
|
|
|
rtw_hal_config_interrupt(pser->phl_info->hal, RTW_PHL_SER_HANDSHAKE_MODE);
|
|
|
|
ser_send_m4_event(pser);
|
|
|
|
phl_fsm_set_alarm(pser->fsm_obj, SER_FW_TIMEOUT,
|
|
SER_EV_FW_TIMER_EXPIRE);
|
|
break;
|
|
|
|
case SER_EV_CHK_SER_EVENT:
|
|
if (true == rtw_hal_recognize_halt_c2h_interrupt(pser->phl_info->hal))
|
|
_ser_event_notify(pser->phl_info, NULL);
|
|
|
|
break;
|
|
|
|
case SER_EV_M5_READY:
|
|
rtw_hal_config_interrupt(pser->phl_info->hal, RTW_PHL_EN_HCI_INT);
|
|
phl_fsm_state_goto(pser->fsm_obj, SER_ST_L1_RESUME_TRX);
|
|
break;
|
|
|
|
case SER_EV_FW_TIMER_EXPIRE:
|
|
ser_resume_trx_process(pser, PHL_CTRL_TX);
|
|
rtw_phl_tx_req_notify(pser->phl_info);
|
|
rtw_hal_config_interrupt(pser->phl_info->hal, RTW_PHL_EN_HCI_INT);
|
|
phl_fsm_state_goto(pser->fsm_obj, SER_ST_IDLE);
|
|
break;
|
|
|
|
case SER_EV_M9_L2_RESET:
|
|
rtw_hal_config_interrupt(pser->phl_info->hal, RTW_PHL_EN_HCI_INT);
|
|
phl_fsm_state_goto(pser->fsm_obj, SER_ST_L2);
|
|
break;
|
|
|
|
case FSM_EV_CANCEL:
|
|
rtw_hal_config_interrupt(pser->phl_info->hal, RTW_PHL_EN_HCI_INT);
|
|
phl_fsm_state_goto(pser->fsm_obj, SER_ST_IDLE);
|
|
break;
|
|
|
|
case FSM_EV_STATE_OUT:
|
|
phl_fsm_cancel_alarm(pser->fsm_obj);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int ser_l1_resume_trx_st_hdl(void *obj, u16 event, void *param)
|
|
{
|
|
struct ser_obj *pser = (struct ser_obj *)obj;
|
|
|
|
|
|
switch (event) {
|
|
case FSM_EV_STATE_IN:
|
|
|
|
/* TODO resume TRX process */
|
|
ser_resume_trx_process(pser, PHL_CTRL_TX);
|
|
rtw_phl_tx_req_notify(pser->phl_info);
|
|
phl_fsm_state_goto(pser->fsm_obj, SER_ST_IDLE);
|
|
break;
|
|
|
|
case FSM_EV_CANCEL:
|
|
phl_fsm_state_goto(pser->fsm_obj, SER_ST_IDLE);
|
|
break;
|
|
|
|
case FSM_EV_STATE_OUT:
|
|
phl_fsm_cancel_alarm(pser->fsm_obj);
|
|
phl_ser_clear_status(pser, SER_L1);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int ser_l2_st_hdl(void *obj, u16 event, void *param)
|
|
{
|
|
struct ser_obj *pser = (struct ser_obj *)obj;
|
|
struct phl_msg msg = {0};
|
|
|
|
switch (event) {
|
|
case FSM_EV_STATE_IN:
|
|
|
|
/*To avoid fw watchdog intr trigger periodically*/
|
|
rtw_hal_ser_reset_wdt_intr(pser->phl_info->hal);
|
|
|
|
/* TODO do something */
|
|
phl_ser_clear_status(pser, SER_L1);
|
|
phl_ser_set_status(pser, SER_L2);
|
|
|
|
SET_MSG_MDL_ID_FIELD(msg.msg_id, PHL_MDL_SER);
|
|
SET_MSG_EVT_ID_FIELD(msg.msg_id, MSG_EVT_SER_L2);
|
|
phl_msg_hub_send(pser->phl_info, NULL, &msg);
|
|
|
|
phl_fsm_state_goto(pser->fsm_obj, SER_ST_IDLE);
|
|
break;
|
|
|
|
case FSM_EV_TIMER_EXPIRE:
|
|
break;
|
|
|
|
case FSM_EV_CANCEL:
|
|
phl_fsm_state_goto(pser->fsm_obj, SER_ST_IDLE);
|
|
break;
|
|
|
|
case FSM_EV_STATE_OUT:
|
|
phl_fsm_cancel_alarm(pser->fsm_obj);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void ser_dump_obj(void *obj, char *s, int *sz)
|
|
{
|
|
/* nothing to do for now */
|
|
}
|
|
|
|
static void ser_dump_fsm(void *fsm, char *s, int *sz)
|
|
{
|
|
/* nothing to do for now */
|
|
}
|
|
|
|
/* For EXTERNAL application to create a ser FSM */
|
|
/* @root: FSM root structure
|
|
* @phl_info: private data structure to invoke hal/phl function
|
|
*
|
|
* return
|
|
* fsm_main: FSM main structure (Do NOT expose)
|
|
*/
|
|
struct fsm_main *phl_ser_new_fsm(struct fsm_root *root,
|
|
struct phl_info_t *phl_info)
|
|
{
|
|
void *d = phl_to_drvpriv(phl_info);
|
|
struct fsm_main *fsm = NULL;
|
|
struct rtw_phl_fsm_tb tb;
|
|
|
|
_os_mem_set(d, &tb, 0, sizeof(tb));
|
|
|
|
if (phl_info->phl_com->hci_type == RTW_HCI_PCIE)
|
|
tb.state_tbl = ser_pci_state_tbl;
|
|
else if (phl_info->phl_com->hci_type == RTW_HCI_USB)
|
|
tb.state_tbl = ser_usb_state_tbl;
|
|
else if (phl_info->phl_com->hci_type == RTW_HCI_SDIO)
|
|
tb.state_tbl = ser_sdio_state_tbl;
|
|
else
|
|
return NULL;
|
|
|
|
tb.max_state = sizeof(ser_pci_state_tbl)/sizeof(ser_pci_state_tbl[0]);
|
|
tb.max_event = sizeof(ser_event_tbl)/sizeof(ser_event_tbl[0]);
|
|
tb.evt_tbl = ser_event_tbl;
|
|
tb.dump_obj = ser_dump_obj;
|
|
tb.dump_obj = ser_dump_fsm;
|
|
tb.dbg_level = FSM_DBG_INFO;
|
|
tb.evt_level = FSM_DBG_INFO;
|
|
|
|
fsm = phl_fsm_init_fsm(root, "ser", phl_info, &tb);
|
|
|
|
return fsm;
|
|
}
|
|
|
|
/* For EXTERNAL application to destory ser fsm */
|
|
/* @fsm: see fsm_main
|
|
*/
|
|
void phl_ser_destory_fsm(struct fsm_main *fsm)
|
|
{
|
|
if (fsm == NULL)
|
|
return;
|
|
|
|
/* deinit fsm local variable if has */
|
|
|
|
/* call FSM Framewro to deinit fsm */
|
|
phl_fsm_deinit_fsm(fsm);
|
|
}
|
|
|
|
/* For EXTERNAL application to create SER object */
|
|
/* @fsm: FSM main structure which created by phl_ser_new_fsm()
|
|
* @phl_info: private data structure to invoke hal/phl function
|
|
*
|
|
* return
|
|
* ser_obj: structure of SER object (Do NOT expose)
|
|
*/
|
|
struct ser_obj *phl_ser_new_obj(struct fsm_main *fsm,
|
|
struct phl_info_t *phl_info)
|
|
{
|
|
void *d = phl_to_drvpriv(phl_info);
|
|
struct fsm_obj *obj;
|
|
struct ser_obj *pser;
|
|
|
|
|
|
pser = phl_fsm_new_obj(fsm, (void **)&obj, sizeof(*pser));
|
|
|
|
if (pser == NULL) {
|
|
/* TODO free fsm; currently will be freed in deinit process */
|
|
FSM_ERR(fsm, "ser: malloc obj fail\n");
|
|
return NULL;
|
|
}
|
|
pser->fsm = fsm;
|
|
pser->fsm_obj = obj;
|
|
pser->phl_info = phl_info;
|
|
|
|
/* init local use variable */
|
|
_os_spinlock_init(d, &pser->state_lock);
|
|
|
|
return pser;
|
|
}
|
|
|
|
/* For EXTERNAL application to destory ser object */
|
|
/* @pser: local created command object
|
|
*
|
|
*/
|
|
void phl_ser_destory_obj(struct ser_obj *pser)
|
|
{
|
|
void *d;
|
|
|
|
if (pser == NULL)
|
|
return;
|
|
|
|
d = phl_to_drvpriv(pser->phl_info);
|
|
|
|
/* deinit obj local variable if has */
|
|
_os_spinlock_free(d, &pser->state_lock);
|
|
|
|
/* inform FSM framewory to recycle fsm_obj */
|
|
phl_fsm_destory_obj(pser->fsm_obj);
|
|
}
|
|
|
|
/* For EXTERNAL application to stop ser service (expose) */
|
|
/* @pser: ser job will be cancelled
|
|
*/
|
|
enum rtw_phl_status phl_ser_cancel(void *phl)
|
|
{
|
|
struct phl_info_t *phl_info = (struct phl_info_t *)phl;
|
|
struct ser_obj *pser = phl_info->ser_obj;
|
|
|
|
return phl_fsm_gen_msg(phl, pser->fsm_obj, NULL, 0, FSM_EV_CANCEL);
|
|
}
|
|
|
|
/* For EXTERNAL interrupt handler to send event into ser fsm (expose) */
|
|
enum rtw_phl_status phl_ser_event_to_fw(void *phl, u32 err)
|
|
{
|
|
struct phl_info_t *phl_info = (struct phl_info_t *)phl;
|
|
enum rtw_hal_status status;
|
|
|
|
status = rtw_hal_ser_set_error_status(phl_info->hal ,err);
|
|
PHL_TRACE(COMP_PHL_DBG, _PHL_ERR_, "phl_ser_event_to_fw err %d, status 0x%x\n", err, status);
|
|
return status;
|
|
}
|
|
|
|
/* For EXTERNAL interrupt handler and dump fw ple (expose) */
|
|
enum rtw_phl_status rtw_phl_ser_dump_ple_buffer(void *phl)
|
|
{
|
|
struct phl_info_t *phl_info = (struct phl_info_t *)phl;
|
|
|
|
PHL_TRACE(COMP_PHL_DBG, _PHL_WARNING_, "rtw_phl_ser_dump_ple_buffer\n");
|
|
|
|
rtw_hal_dump_fw_rsvd_ple(phl_info->hal);
|
|
|
|
return RTW_PHL_STATUS_SUCCESS;
|
|
}
|
|
|
|
/* For EXTERNAL interrupt handler and dump fw ple (expose) */
|
|
enum rtw_phl_status phl_fw_watchdog_timeout_notify(void *phl)
|
|
{
|
|
enum RTW_PHL_SER_NOTIFY_EVENT notify = RTW_PHL_SER_L2_RESET;
|
|
PHL_TRACE(COMP_PHL_DBG, _PHL_ERR_, "phl_fw_watchdog_timeout_notify triggle L2 Reset !!!\n");
|
|
|
|
return phl_ser_send_msg(phl, notify);
|
|
}
|
|
|
|
enum rtw_phl_status rtw_phl_ser_l2_notify(struct rtw_phl_com_t *phl_com)
|
|
{
|
|
enum RTW_PHL_SER_NOTIFY_EVENT notify = RTW_PHL_SER_L2_RESET;
|
|
PHL_TRACE(COMP_PHL_DBG, _PHL_ERR_, "rtw_phl_ser_l2_notify triggle L2 Reset !!!\n");
|
|
|
|
return phl_ser_send_msg(phl_com->phl_priv, notify);
|
|
}
|
|
|
|
/* @phl: phl_info_t
|
|
* @notify: event to ser fsm
|
|
*/
|
|
|
|
#ifndef CONFIG_PHL_CMD_SER
|
|
enum rtw_phl_status phl_ser_send_msg(void *phl,
|
|
enum RTW_PHL_SER_NOTIFY_EVENT notify)
|
|
{
|
|
struct phl_info_t *phl_info = (struct phl_info_t *)phl;
|
|
struct ser_obj *pser = phl_info->ser_obj;
|
|
u16 event;
|
|
|
|
switch (notify) {
|
|
case RTW_PHL_SER_PAUSE_TRX: /* M1 */
|
|
event = SER_EV_L1_START;
|
|
break;
|
|
case RTW_PHL_SER_DO_RECOVERY: /* M3 */
|
|
event = SER_EV_M3_DO_RECOVERY;
|
|
break;
|
|
case RTW_PHL_SER_READY: /* M5 */
|
|
event = SER_EV_M5_READY;
|
|
break;
|
|
case RTW_PHL_SER_L2_RESET: /* M9 */
|
|
event = SER_EV_M9_L2_RESET;
|
|
break;
|
|
case RTW_PHL_SER_EVENT_CHK:
|
|
event = SER_EV_CHK_SER_EVENT;
|
|
break;
|
|
case RTW_PHL_SER_L0_RESET:
|
|
PHL_TRACE(COMP_PHL_DBG, _PHL_DEBUG_, "phl_ser_send_msg(): Unsupported case:%d, please check it\n",
|
|
notify);
|
|
return RTW_PHL_STATUS_FAILURE;
|
|
default:
|
|
PHL_TRACE(COMP_PHL_DBG, _PHL_DEBUG_, "phl_ser_send_msg(): Unrecognize case:%d, please check it\n",
|
|
notify);
|
|
return RTW_PHL_STATUS_FAILURE;
|
|
}
|
|
PHL_TRACE(COMP_PHL_DBG, _PHL_INFO_, "phl_ser_send_msg event %d\n", event);
|
|
|
|
return phl_fsm_gen_msg(phl, pser->fsm_obj, NULL, 0, event);
|
|
}
|
|
|
|
|
|
/* For EXTERNAL application to do L2 reset (expose) */
|
|
/* @pser: ser job will be cancelled
|
|
*/
|
|
u8 phl_ser_inprogress(void *phl)
|
|
{
|
|
struct phl_info_t *phl_info = (struct phl_info_t *)phl;
|
|
struct ser_obj *pser = phl_info->ser_obj;
|
|
u8 reset_type = 0;
|
|
void *d = phl_to_drvpriv(phl_info);
|
|
|
|
_os_spinlock(d, &pser->state_lock, _bh, NULL);
|
|
|
|
if(pser->trigger_l1_reset == true) {
|
|
reset_type |= SER_L1;
|
|
//pser->trigger_l2_reset = false;
|
|
}
|
|
if(pser->trigger_l2_reset == true) {
|
|
reset_type |= SER_L2;
|
|
}
|
|
|
|
_os_spinunlock(d, &pser->state_lock, _bh, NULL);
|
|
return reset_type;
|
|
}
|
|
#endif
|
|
|
|
/* For EXTERNAL application to do L2 reset (expose) */
|
|
/* @pser: ser job will be cancelled
|
|
*/
|
|
void phl_ser_clear_status(struct ser_obj *pser, u32 serstatus)
|
|
{
|
|
void *d = phl_to_drvpriv(pser->phl_info);
|
|
|
|
_os_spinlock(d, &pser->state_lock, _bh, NULL);
|
|
|
|
if(serstatus & SER_L1) {
|
|
pser->trigger_l1_reset = false;
|
|
}
|
|
if(serstatus & SER_L2) {
|
|
pser->trigger_l2_reset = false;
|
|
}
|
|
|
|
_os_spinunlock(d, &pser->state_lock, _bh, NULL);
|
|
}
|
|
|
|
void phl_ser_set_status(struct ser_obj *pser, u32 serstatus)
|
|
{
|
|
void *d = phl_to_drvpriv(pser->phl_info);
|
|
|
|
_os_spinlock(d, &pser->state_lock, _bh, NULL);
|
|
|
|
if(serstatus & SER_L1) {
|
|
pser->trigger_l1_reset = true;
|
|
}
|
|
if(serstatus & SER_L2) {
|
|
pser->trigger_l2_reset = true;
|
|
}
|
|
|
|
_os_spinunlock(d, &pser->state_lock, _bh, NULL);
|
|
}
|
|
|
|
|
|
/* For EXTERNAL application notify from upper layer*/
|
|
void phl_ser_notify_from_upper_watchdog_status(void *phl, bool inprogress)
|
|
{
|
|
struct phl_info_t *phl_info = (struct phl_info_t *)phl;
|
|
struct ser_obj *pser = phl_info->ser_obj;
|
|
|
|
pser->dynamicThredRunning = inprogress;
|
|
}
|
|
|
|
void rtw_phl_notify_watchdog_status(void *phl, bool inprogress)
|
|
{
|
|
phl_ser_notify_from_upper_watchdog_status(phl, inprogress);
|
|
}
|
|
#endif /*CONFIG_FSM*/
|
|
|