/* SPDX-License-Identifier: GPL-2.0 */ /****************************************************************************** * * Copyright(c) 2016 - 2017 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 _RTL8821CS_OPS_C_ #include /* PADAPTER, basic_types.h and etc. */ #include /* HAL_DATA_TYPE, GET_HAL_DATA() and etc. */ #include /* struct hal_ops */ #include "../rtl8821c.h" #include "rtl8821cs.h" /* rtl8821cs_hal_init() */ #include "rtl8821cs_xmit.h" #include "rtl8821cs_recv.h" #include "rtl8821cs_io.h" #include "rtl8821cs_led.h" #include "rtl8821cs_halmac.h" static void intf_chip_configure(PADAPTER adapter) { #if 0 u8 try_cnt = 0; /*adjust SDIO output driving -output delay time about 8ns (SDIO SPEC must last than 7.5ns),offset 0x74[18]*/ if (rtw_is_sdio30(adapter)) { rtw_write8(adapter, REG_SDIO_HSUS_CTRL, rtw_read8(adapter, REG_SDIO_HSUS_CTRL) & ~BIT(0)); do { rtw_mdelay_os(2); if ((rtw_read8(adapter, REG_SDIO_HSUS_CTRL) & BIT(1))) break; try_cnt++; } while (try_cnt <= 50); if (try_cnt == 10) RTW_ERR("%s: SDIO active state change failed!!\n", __func__); rtw_write32(adapter, REG_HCI_OPT_CTRL, rtw_read32(adapter, REG_HCI_OPT_CTRL) | BIT(18)); } #endif } u32 rtl8821cs_get_interrupt(PADAPTER adapter) { return rtw_read32(adapter, REG_SDIO_HISR_8821C); } void rtl8821cs_clear_interrupt(PADAPTER adapter, u32 hisr) { /* Perform write one clear operation */ if (hisr) rtw_write32(adapter, REG_SDIO_HISR_8821C, hisr); } u32 rtl8821cs_get_himr(PADAPTER adapter) { return rtw_read32(adapter, REG_SDIO_HIMR_8821C); } void rtl8821cs_update_himr(PADAPTER adapter, u32 himr) { rtw_write32(adapter, REG_SDIO_HIMR_8821C, himr); } void rtl8821cs_update_interrupt_mask(PADAPTER padapter,u32 AddMSR, u32 RemoveMSR) { HAL_DATA_TYPE *pHalData; pHalData = GET_HAL_DATA(padapter); if (AddMSR) pHalData->sdio_himr |= AddMSR; if (RemoveMSR) pHalData->sdio_himr &= (~RemoveMSR); rtl8821cs_update_himr(padapter,pHalData->sdio_himr); } /* * Description: * Initialize SDIO Host Interrupt Mask configuration variables for future use. * * Assumption: * Using SDIO Local register ONLY for configuration. */ void rtl8821cs_init_interrupt(PADAPTER adapter) { PHAL_DATA_TYPE hal; hal = GET_HAL_DATA(adapter); hal->sdio_himr = (u32)(\ BIT_RX_REQUEST_MSK_8821C | #ifdef CONFIG_SDIO_TX_ENABLE_AVAL_INT BIT_SDIO_AVAL_MSK_8821C | #endif /* CONFIG_SDIO_TX_ENABLE_AVAL_INT */ #ifdef CONFIG_ERROR_STATE_MONITOR BIT_SDIO_TXERR_MSK_8821C | BIT_SDIO_RXERR_MSK_8821C | #endif #ifdef CONFIG_MONITOR_OVERFLOW BIT_SDIO_TXFOVW_MSK_8821C | BIT_SDIO_RXFOVW_MSK_8821C | #endif #ifdef CONFIG_INTERRUPT_BASED_TXBCN #ifdef CONFIG_INTERRUPT_BASED_TXBCN_BCN_OK_ERR BIT_SDIO_TXBCNOK_MSK_8821C | BIT_SDIO_TXBCNERR_MSK_8821C | #endif #ifdef CONFIG_INTERRUPT_BASED_TXBCN_EARLY_INT BIT_SDIO_BCNERLY_INT_MSK_8821C | #endif #endif #if 0 BIT_SDIO_C2HCMD_INT_MSK_8821C | #endif #if defined(CONFIG_LPS_LCLK) && !defined(CONFIG_DETECT_CPWM_BY_POLLING) BIT_SDIO_CPWM1_MSK_8821C | #if 0 BIT_SDIO_CPWM2_MSK_8821C | #endif #endif /* CONFIG_LPS_LCLK && !CONFIG_DETECT_CPWM_BY_POLLING */ #if 0 BIT_SDIO_HSISR_IND_MSK_8821C | BIT_SDIO_GTINT3_MSK_8821C | BIT_SDIO_GTINT4_MSK_8821C | BIT_SDIO_PSTIMEOUT_MSK_8821C | BIT_SDIO_OCPINT_MSK_8821C | BIT_SDIIO_ATIMend_MSK_8821C | BIT_SDIO_ATIMend_E_MSK_8821C | BIT_SDIO_CTWend_MSK_8821C | BIT_SDIO_CRCERR_MSK_8821C | #endif 0); } /* * Description: * Clear corresponding SDIO Host ISR interrupt service. * * Assumption: * Using SDIO Local register ONLY for configuration. */ #if defined(CONFIG_WOWLAN) || defined(CONFIG_AP_WOWLAN) static void clear_interrupt_all(PADAPTER adapter) { PHAL_DATA_TYPE hal; if (rtw_is_surprise_removed(adapter)) return; hal = GET_HAL_DATA(adapter); rtl8821cs_clear_interrupt(adapter, 0xFFFFFFFF); } #endif /*#if defined(CONFIG_WOWLAN) || defined(CONFIG_AP_WOWLAN)*/ /* * Description: * Enalbe SDIO Host Interrupt Mask configuration on SDIO local domain. * * Assumption: * 1. Using SDIO Local register ONLY for configuration. * 2. PASSIVE LEVEL */ static void enable_interrupt(PADAPTER adapter) { PHAL_DATA_TYPE hal; hal = GET_HAL_DATA(adapter); rtl8821cs_update_himr(adapter, hal->sdio_himr); RTW_INFO(FUNC_ADPT_FMT ": update SDIO HIMR=0x%08X\n", FUNC_ADPT_ARG(adapter), hal->sdio_himr); } /* * Description: * Disable SDIO Host IMR configuration to mask unnecessary interrupt service. * * Assumption: * Using SDIO Local register ONLY for configuration. */ static void disable_interrupt(PADAPTER adapter) { PHAL_DATA_TYPE hal; hal = GET_HAL_DATA(adapter); rtl8821cs_update_himr(adapter, 0); RTW_INFO("%s: update SDIO HIMR=0\n", __FUNCTION__); } #ifdef CONFIG_WOWLAN void rtl8821cs_disable_interrupt_but_cpwm2(PADAPTER adapter) { u32 himr, tmp; tmp = rtw_read32(adapter, REG_SDIO_HIMR); RTW_INFO("%s: Read SDIO_REG_HIMR: 0x%08x\n", __FUNCTION__, tmp); himr = BIT_SDIO_CPWM2_MSK; rtl8821cs_update_himr(adapter, himr); tmp = rtw_read32(adapter, REG_SDIO_HIMR); RTW_INFO("%s: Read again SDIO_REG_HIMR: 0x%08x\n", __FUNCTION__, tmp); } #endif /* CONFIG_WOWLAN */ static void _run_thread(PADAPTER adapter) { #ifndef CONFIG_SDIO_TX_TASKLET struct xmit_priv *xmitpriv = &adapter->xmitpriv; if (xmitpriv->SdioXmitThread == NULL) { RTW_INFO(FUNC_ADPT_FMT " start RTWHALXT\n", FUNC_ADPT_ARG(adapter)); xmitpriv->SdioXmitThread = kthread_run(rtl8821cs_xmit_thread, adapter, "RTWHALXT"); if (IS_ERR(xmitpriv->SdioXmitThread)) { RTW_ERR("%s: start rtl8821cs_xmit_thread FAIL!!\n", __FUNCTION__); xmitpriv->SdioXmitThread = NULL; } } #endif /* !CONFIG_SDIO_TX_TASKLET */ } static void run_thread(PADAPTER adapter) { _run_thread(adapter); rtl8821c_run_thread(adapter); } static void _cancel_thread(PADAPTER adapter) { #ifndef CONFIG_SDIO_TX_TASKLET struct xmit_priv *xmitpriv = &adapter->xmitpriv; /* stop xmit_buf_thread */ if (xmitpriv->SdioXmitThread) { _rtw_up_sema(&xmitpriv->SdioXmitSema); rtw_thread_stop(xmitpriv->SdioXmitThread); xmitpriv->SdioXmitThread = NULL; } #endif /* !CONFIG_SDIO_TX_TASKLET */ } static void cancel_thread(PADAPTER adapter) { rtl8821c_cancel_thread(adapter); _cancel_thread(adapter); } #ifdef CONFIG_SDIO_OOB /* REG_SYS_SDIO_CTRL_8821C BIT_SDIO_INT - 1: Enabled (GPIO4:SDIO_INT); 0 : Disabled BIT_SDIO_INT_POLARITY - 0: Low Active 1: High Active */ void rtl8821cs_gpio4_sdio_int_enable(_adapter *adapter, u8 enable, u8 polarity_high) { u32 sdio_ctrl = 0; sdio_ctrl = rtw_read32(adapter, REG_SYS_SDIO_CTRL_8821C); if (enable) { sdio_ctrl |= BIT_SDIO_INT; if (polarity_high == _TRUE) sdio_ctrl |= BIT_SDIO_INT_POLARITY; else sdio_ctrl &= (~BIT_SDIO_INT_POLARITY); } else sdio_ctrl &= (~BIT_SDIO_INT); rtw_write32(adapter, REG_SYS_SDIO_CTRL_8821C, sdio_ctrl); } #endif /* * If variable not handled here, * some variables will be processed in rtl8821c_sethwreg() */ static u8 sethwreg(PADAPTER adapter, u8 variable, u8 *val) { u8 ret = _SUCCESS; switch (variable) { case HW_VAR_SET_RPWM: { /* * rpwm value only use BIT0(clock bit), and BIT7(Toggle bit) * BIT0 value - 1: 32k, 0:40MHz. * BIT4 value - 1: enable power gated, 0: disable power gated * BIT6 value - 1: report cpwm value after success set, 0:do not report. * BIT7 value - Toggling Bit. */ u8 val8 = 0; struct pwrctrl_priv *pwrpriv = adapter_to_pwrctl(adapter); val8 = (*val) & 0xC1; #ifdef CONFIG_LPS_PG if ((val8 & BIT(0)) && (LPS_PG == pwrpriv->lps_level)) val8 |= BIT(4); #endif rtw_write8(adapter, REG_SDIO_HRPWM1_8821C, val8); } break; #ifdef CONFIG_GPIO_WAKEUP case HW_SET_GPIO_WL_CTRL: { } break; #endif /* #ifdef CONFIG_SDIO_OOB case HW_SET_SDIO_OOB: { u8 sdio_ctrl = *val; u8 enable, is_high_active; enable = (sdio_ctrl & BIT(0)) ? _TRUE : _FALSE; is_high_active = (sdio_ctrl & BIT(4)) ? _TRUE : _FALSE; rtl8821cs_gpio4_sdio_int_enable(adapter, enable, is_high_active); } break; #endif */ default: ret = rtl8821c_sethwreg(adapter, variable, val); break; } return ret; } /* * If variable not handled here, * some variables will be processed in GetHwReg8723B() */ static void gethwreg(PADAPTER adapter, u8 variable, u8 *val) { PHAL_DATA_TYPE hal; hal = GET_HAL_DATA(adapter); switch (variable) { case HW_VAR_CPWM: *val = rtw_read8(adapter, REG_SDIO_HCPWM1_V2_8821C); break; case HW_VAR_RPWM_TOG: *val = rtw_read8(adapter, REG_SDIO_HRPWM1_8821C); *val &= BIT_TOGGLE_8821C; break; #ifdef CONFIG_GPIO_WAKEUP case HW_SET_GPIO_WL_CTRL: break; #endif default: rtl8821c_gethwreg(adapter, variable, val); break; } } /* * Description: * Query setting of specified variable. */ static u8 gethaldefvar(PADAPTER adapter, HAL_DEF_VARIABLE eVariable, void *pval) { u8 bResult = _SUCCESS; struct dvobj_priv *dvobj = adapter_to_dvobj(adapter); PSDIO_DATA psdio_data = &dvobj->intf_data; PHAL_DATA_TYPE hal = GET_HAL_DATA(adapter); switch (eVariable) { case HW_VAR_MAX_RX_AMPDU_FACTOR: { if (psdio_data->clock > RTW_SDIO_CLK_40M) { *(HT_CAP_AMPDU_FACTOR *)pval = MAX_AMPDU_FACTOR_32K; RTW_INFO("AMPDU FACTOR - 32K\n"); } else if (psdio_data->clock > RTW_SDIO_CLK_33M) { *(HT_CAP_AMPDU_FACTOR *)pval = MAX_AMPDU_FACTOR_16K; RTW_INFO("AMPDU FACTOR - 16K\n"); } else { *(HT_CAP_AMPDU_FACTOR *)pval = MAX_AMPDU_FACTOR_8K; RTW_INFO("AMPDU FACTOR - 8K\n"); } } break; default: bResult = rtl8821c_gethaldefvar(adapter, eVariable, pval); break; } return bResult; } /* * Description: * Change default setting of specified variable. */ static u8 sethaldefvar(PADAPTER adapter, HAL_DEF_VARIABLE eVariable, void *pval) { PHAL_DATA_TYPE hal = GET_HAL_DATA(adapter); u8 bResult = _SUCCESS; switch (eVariable) { default: bResult = rtl8821c_sethaldefvar(adapter, eVariable, pval); break; } return bResult; } u8 rtl8821cs_set_hal_ops(PADAPTER adapter) { struct hal_ops *ops_func = &adapter->hal_func; u8 err; err = rtl8821cs_halmac_init_adapter(adapter); if (err) { RTW_INFO("%s: [ERROR]HALMAC initialize FAIL!\n", __FUNCTION__); return _FAIL; } rtl8821c_set_hal_ops(adapter); rtl8821cs_init_interrupt(adapter); ops_func->init_default_value = rtl8821cs_init_default_value; ops_func->intf_chip_configure = intf_chip_configure; ops_func->hal_init = rtl8821cs_hal_init; ops_func->hal_deinit = rtl8821cs_hal_deinit; ops_func->init_xmit_priv = rtl8821cs_init_xmit_priv; ops_func->free_xmit_priv = rtl8821cs_free_xmit_priv; ops_func->hal_xmit = rtl8821cs_hal_xmit; ops_func->mgnt_xmit = rtl8821cs_mgnt_xmit; #ifdef CONFIG_RTW_MGMT_QUEUE ops_func->hal_mgmt_xmitframe_enqueue = rtl8821cs_hal_mgmt_xmit_enqueue; #endif ops_func->hal_xmitframe_enqueue = rtl8821cs_hal_xmit_enqueue; #ifdef CONFIG_XMIT_THREAD_MODE ops_func->xmit_thread_handler = rtl8821cs_xmit_buf_handler; #endif ops_func->run_thread = run_thread; ops_func->cancel_thread = cancel_thread; ops_func->init_recv_priv = rtl8821cs_init_recv_priv; ops_func->free_recv_priv = rtl8821cs_free_recv_priv; #ifdef CONFIG_RECV_THREAD_MODE ops_func->recv_hdl = rtl8821cs_recv_hdl; #endif ops_func->enable_interrupt = enable_interrupt; ops_func->disable_interrupt = disable_interrupt; #if defined(CONFIG_WOWLAN) || defined(CONFIG_AP_WOWLAN) ops_func->clear_interrupt = clear_interrupt_all; #endif #ifdef CONFIG_RTW_SW_LED ops_func->InitSwLeds = rtl8821cs_initswleds; ops_func->DeInitSwLeds = rtl8821cs_deinitswleds; #endif ops_func->set_hw_reg_handler = sethwreg; ops_func->GetHwRegHandler = gethwreg; ops_func->get_hal_def_var_handler = gethaldefvar; ops_func->SetHalDefVarHandler = sethaldefvar; return _TRUE; }