/****************************************************************************** * * Copyright(c) 2019 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. * *****************************************************************************/ #define _PHL_WATCHDOG_C_ #include "phl_headers.h" #ifdef CONFIG_FSM static void _phl_datapath_watchdog(struct phl_info_t *phl_info) { phl_tx_watchdog(phl_info); phl_rx_watchdog(phl_info); phl_sta_trx_tfc_upd(phl_info); } void rtw_phl_watchdog_callback(void *phl) { struct phl_info_t *phl_info = (struct phl_info_t *)phl; do { _phl_datapath_watchdog(phl_info); #ifdef CONFIG_PCI_HCI #ifdef RTW_WKARD_DYNAMIC_LTR phl_ltr_ctrl_watchdog(phl_info); #endif #ifdef PCIE_TRX_MIT_EN phl_pcie_trx_mit_watchdog(phl_info); #endif #endif phl_mr_watchdog(phl_info); rtw_hal_watchdog(phl_info->hal); } while (false); } #endif static void _phl_watchdog_sw(struct phl_info_t *phl) { /* Only sw statistics or sw behavior or trigger FG cmd */ phl_tx_watchdog(phl); phl_rx_watchdog(phl); phl_sta_trx_tfc_upd(phl); } static void _phl_watchdog_hw(struct phl_info_t *phl) { #ifdef CONFIG_PHL_THERMAL_PROTECT phl_thermal_protect_watchdog(phl); #endif /* I/O, tx behavior, request power, ... */ #ifdef CONFIG_PCI_HCI #ifdef RTW_WKARD_DYNAMIC_LTR phl_ltr_ctrl_watchdog(phl); #endif #ifdef PCIE_TRX_MIT_EN phl_pcie_trx_mit_watchdog(phl); #endif #endif phl_mr_watchdog(phl); rtw_hal_watchdog(phl->hal); phl_bcn_watchdog(phl); } #ifdef CONFIG_CMD_DISP static void _phl_watchdog_post_action(struct phl_info_t *phl_info) { #ifdef CONFIG_POWER_SAVE rtw_hal_ps_chk_hw_rf_state(phl_info->phl_com, phl_info->hal); #endif /* CONFIG_POWER_SAVE */ } static void _phl_trigger_next_watchdog(struct phl_info_t *phl_info) { struct phl_watchdog *wdog = &(phl_info->wdog); if (wdog->state == WD_STATE_STARTED) _os_set_timer(phl_to_drvpriv(phl_info), &wdog->wdog_timer, wdog->period); } static void _phl_watchdog_hw_done(void *drv_priv, u8 *cmd, u32 cmd_len, enum rtw_phl_status status) { struct phl_info_t *phl_info = (struct phl_info_t *)cmd; _phl_watchdog_post_action(phl_info); _phl_trigger_next_watchdog(phl_info); } static enum rtw_phl_status _phl_watchdog_hw_cmd(struct phl_info_t *phl_info, enum phl_cmd_type cmd_type, u32 cmd_timeout) { enum rtw_phl_status phl_status = RTW_PHL_STATUS_FAILURE; if (cmd_type == PHL_CMD_DIRECTLY) { phl_status = phl_watchdog_hw_cmd_hdl(phl_info, RTW_PHL_STATUS_SUCCESS); goto _exit; } /* watchdog dont care hw_band */ phl_status = phl_cmd_enqueue(phl_info, HW_BAND_0, MSG_EVT_HW_WATCHDOG, (u8 *)phl_info, 0, _phl_watchdog_hw_done, cmd_type, cmd_timeout); if (is_cmd_failure(phl_status)) { /* Send cmd success, but wait cmd fail*/ } else if (phl_status != RTW_PHL_STATUS_SUCCESS) { /* Send cmd fail */ phl_status = RTW_PHL_STATUS_FAILURE; } _exit: return phl_status; } static void _phl_watchdog_sw_done(void *drv_priv, u8 *cmd, u32 cmd_len, enum rtw_phl_status status) { struct phl_info_t *phl_info = (struct phl_info_t *)cmd; enum rtw_phl_status psts = RTW_PHL_STATUS_FAILURE; bool set_timer = true; if (true == phl_disp_eng_is_fg_empty(phl_info, HW_BAND_MAX)) { /* send watchdog cmd to request privilege of I/O */ psts = _phl_watchdog_hw_cmd(phl_info, PHL_CMD_NO_WAIT, 0); if (psts != RTW_PHL_STATUS_FAILURE) set_timer = false; } else { PHL_TRACE(COMP_PHL_DBG, _PHL_DEBUG_, "%s: skip watchdog\n", __FUNCTION__); } if (set_timer) _phl_trigger_next_watchdog(phl_info); } static enum rtw_phl_status _phl_watchdog_sw_cmd(struct phl_info_t *phl_info, enum phl_cmd_type cmd_type, u32 cmd_timeout) { enum rtw_phl_status phl_status = RTW_PHL_STATUS_FAILURE; if (cmd_type == PHL_CMD_DIRECTLY) { phl_status = phl_watchdog_sw_cmd_hdl(phl_info, RTW_PHL_STATUS_SUCCESS); goto _exit; } /* watchdog dont care hw_band */ phl_status = phl_cmd_enqueue(phl_info, HW_BAND_0, MSG_EVT_SW_WATCHDOG, (u8 *)phl_info, 0, _phl_watchdog_sw_done, cmd_type, cmd_timeout); if (is_cmd_failure(phl_status)) { /* Send cmd success, but wait cmd fail*/ } else if (phl_status != RTW_PHL_STATUS_SUCCESS) { /* Send cmd fail */ phl_status = RTW_PHL_STATUS_FAILURE; } _exit: return phl_status; } #endif static void _phl_watchdog_timer_expired(void *context) { #ifdef CONFIG_CMD_DISP struct phl_info_t *phl_info = (struct phl_info_t *)context; enum rtw_phl_status psts = RTW_PHL_STATUS_FAILURE; bool set_timer = true; /* phl sw watchdog */ _phl_watchdog_sw(phl_info); psts = _phl_watchdog_sw_cmd(phl_info, PHL_CMD_NO_WAIT, 0); if (psts != RTW_PHL_STATUS_FAILURE) set_timer = false; if (set_timer) _phl_trigger_next_watchdog(phl_info); #else PHL_TRACE(COMP_PHL_DBG, _PHL_ERR_, "%s: Not support watchdog\n", __FUNCTION__); #endif } enum rtw_phl_status phl_watchdog_hw_cmd_hdl(struct phl_info_t *phl_info, enum rtw_phl_status psts) { struct phl_watchdog *wdog = &(phl_info->wdog); if (false == is_cmd_failure(psts)) { _phl_watchdog_hw(phl_info); if (NULL != wdog->core_hw_wdog) wdog->core_hw_wdog(phl_to_drvpriv(phl_info)); } return RTW_PHL_STATUS_SUCCESS; } enum rtw_phl_status phl_watchdog_sw_cmd_hdl(struct phl_info_t *phl_info, enum rtw_phl_status psts) { struct phl_watchdog *wdog = &(phl_info->wdog); if (false == is_cmd_failure(psts)) { if (NULL != wdog->core_sw_wdog) wdog->core_sw_wdog(phl_to_drvpriv(phl_info)); } return RTW_PHL_STATUS_SUCCESS; } void rtw_phl_watchdog_init(void *phl, u16 period, void (*core_sw_wdog)(void *drv_priv), void (*core_hw_wdog)(void *drv_priv)) { struct phl_info_t *phl_info = (struct phl_info_t *)phl; struct phl_watchdog *wdog = &(phl_info->wdog); wdog->state = WD_STATE_INIT; wdog->core_sw_wdog = core_sw_wdog; wdog->core_hw_wdog = core_hw_wdog; if (period > 0) wdog->period = period; else wdog->period = WDOG_PERIOD; _os_init_timer(phl_to_drvpriv(phl_info), &wdog->wdog_timer, _phl_watchdog_timer_expired, phl, "phl_watchdog_timer"); } void rtw_phl_watchdog_deinit(void *phl) { struct phl_info_t *phl_info = (struct phl_info_t *)phl; struct phl_watchdog *wdog = &(phl_info->wdog); _os_release_timer(phl_to_drvpriv(phl_info), &wdog->wdog_timer); } void rtw_phl_watchdog_start(void *phl) { struct phl_info_t *phl_info = (struct phl_info_t *)phl; struct phl_watchdog *wdog = &(phl_info->wdog); wdog->state = WD_STATE_STARTED; _phl_trigger_next_watchdog(phl_info); } void rtw_phl_watchdog_stop(void *phl) { struct phl_info_t *phl_info = (struct phl_info_t *)phl; struct phl_watchdog *wdog = &(phl_info->wdog); PHL_INFO("%s\n", __func__); wdog->state = WD_STATE_STOP; _os_cancel_timer(phl_to_drvpriv(phl_info), &wdog->wdog_timer); }